2
0
mirror of https://github.com/rehlds/revoice.git synced 2025-03-03 17:15:25 +03:00

Implement simple voice playback for codecs testing purposes

This commit is contained in:
s1lent 2017-12-06 19:53:55 +07:00 committed by s1lentq
parent 15780dfcaf
commit f0b5d960b3
20 changed files with 1321 additions and 216 deletions

BIN
revoice/dist/c2a2_ba_launch.wav vendored Normal file

Binary file not shown.

BIN
revoice/dist/cslig_nuages.wav vendored Normal file

Binary file not shown.

BIN
revoice/dist/dance.wav vendored Normal file

Binary file not shown.

BIN
revoice/dist/kolshik.wav vendored Normal file

Binary file not shown.

BIN
revoice/dist/pchela.wav vendored Normal file

Binary file not shown.

View File

@ -145,7 +145,7 @@
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)../src/;$(ProjectDir)../public/;$(ProjectDir)../version/;$(ProjectDir)../include/;$(SolutionDir)../dep/rehlsdk/common/;$(SolutionDir)../dep/rehlsdk/dlls/;$(SolutionDir)../dep/rehlsdk/engine/;$(SolutionDir)../dep/rehlsdk/public/;$(SolutionDir)../dep/silk/include/;$(SolutionDir)../dep/opus/include/;$(SolutionDir)../dep/speex/include/;$(SolutionDir)../dep/metamod/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)../src/;$(ProjectDir)../public/;$(ProjectDir)../version/;$(ProjectDir)../include/;$(SolutionDir)../dep/rehlsdk/common/;$(SolutionDir)../dep/rehlsdk/dlls/;$(SolutionDir)../dep/rehlsdk/engine/;$(SolutionDir)../dep/rehlsdk/public/;$(SolutionDir)../dep/silk/include/;$(SolutionDir)../dep/opus/include/;$(SolutionDir)../dep/speex/include/;$(SolutionDir)../dep/metamod/;$(SolutionDir)../dep/opus/silk/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
</ClCompile>
<Link>

View File

@ -12,6 +12,7 @@ public:
// Initialize the object. The uncompressed format is always 8-bit signed mono.
virtual bool Init(int quality) = 0;
virtual void Release() = 0;
virtual int GetSamplesPerFrame() { return 0; }
// Compress the voice data.
// pUncompressed - 16-bit signed mono voice data.

188
revoice/public/counter.h Normal file
View File

@ -0,0 +1,188 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#pragma once
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <io.h>
#include <direct.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef OSX
#include <limits.h>
#else
#include <linux/limits.h>
#endif
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
class CCounter
{
public:
CCounter();
bool Init();
double GetCurTime();
private:
int m_iLowShift;
double m_flPerfCounterFreq;
double m_flCurrentTime;
double m_flLastCurrentTime;
};
inline CCounter::CCounter() :
m_iLowShift(0),
m_flPerfCounterFreq(0),
m_flCurrentTime(0),
m_flLastCurrentTime(0)
{
Init();
}
inline bool CCounter::Init()
{
#ifdef _WIN32
LARGE_INTEGER performanceFreq;
if (!QueryPerformanceFrequency(&performanceFreq))
return false;
// get 32 out of the 64 time bits such that we have around
// 1 microsecond resolution
unsigned int lowpart, highpart;
lowpart = (unsigned int)performanceFreq.LowPart;
highpart = (unsigned int)performanceFreq.HighPart;
m_iLowShift = 0;
while (highpart || (lowpart > 2000000.0))
{
m_iLowShift++;
lowpart >>= 1;
lowpart |= (highpart & 1) << 31;
highpart >>= 1;
}
m_flPerfCounterFreq = 1.0 / (double)lowpart;
#endif // _WIN32
return true;
}
inline double CCounter::GetCurTime()
{
#ifdef _WIN32
static int sametimecount;
static unsigned int oldtime;
static int first = 1;
LARGE_INTEGER PerformanceCount;
unsigned int temp, t2;
double time;
QueryPerformanceCounter(&PerformanceCount);
if (m_iLowShift == 0)
{
temp = (unsigned int)PerformanceCount.LowPart;
}
else
{
temp = ((unsigned int)PerformanceCount.LowPart >> m_iLowShift) |
((unsigned int)PerformanceCount.HighPart << (32 - m_iLowShift));
}
if (first)
{
oldtime = temp;
first = 0;
}
else
{
// check for turnover or backward time
if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
{
// so we can't get stuck
oldtime = temp;
}
else
{
t2 = temp - oldtime;
time = (double)t2 * m_flPerfCounterFreq;
oldtime = temp;
m_flCurrentTime += time;
if (m_flCurrentTime == m_flLastCurrentTime)
{
if (++sametimecount > 100000)
{
m_flCurrentTime += 1.0;
sametimecount = 0;
}
}
else
{
sametimecount = 0;
}
m_flLastCurrentTime = m_flCurrentTime;
}
}
return m_flCurrentTime;
#else // _WIN32
struct timeval tp;
static int secbase = 0;
gettimeofday(&tp, NULL);
if (!secbase)
{
secbase = tp.tv_sec;
return (tp.tv_usec / 1000000.0);
}
return ((tp.tv_sec - secbase) + tp.tv_usec / 1000000.0);
#endif // _WIN32
}

View File

@ -90,10 +90,10 @@ int CSteamP2PCodec::StreamEncode(const char *pUncompressedBytes, int nSamples, c
}
*(writePos++) = PLT_SamplingRate; // Set sampling rate
*(uint16 *)writePos = 16000;
*(uint16 *)writePos = 8000;
writePos += 2;
*(writePos++) = PLT_Silk; // Voice payload
*(writePos++) = PLT_Silk;//PLT_Silk; // Voice payload
int compressRes = m_BackendCodec->Compress(pUncompressedBytes, nSamples, writePos + 2, maxCompressedBytes - (1 + 2 + 1 + 2), bFinal);
if (compressRes == 0) {
@ -123,18 +123,37 @@ int CSteamP2PCodec::Decompress(const char *pCompressed, int compressedBytes, cha
return StreamDecode(pCompressed + 8, compressedBytes - 12, pUncompressed, maxUncompressedBytes);
}
extern uint64_t g_ulSteamIdCurrent;
int CSteamP2PCodec::Compress(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal)
{
if (maxCompressedBytes < 12) { // no room
return 0;
}
/*union CSteamID
{
uint64 allbits64;
struct
{
uint32 lo;
uint32 hi;
};
};
CSteamID steamid;
steamid.allbits64 = 76561198051972183;
char *writePos = pCompressed;
*(uint32 *)writePos = 0x00000011; // steamid (low part)
*(uint32 *)writePos = steamid.lo; // steamid (low part)
writePos += 4;
*(uint32 *)writePos = 0x01100001; // steamid (high part)
writePos += 4;
*(uint32 *)writePos = steamid.hi; // steamid (high part)
writePos += 4;*/
char *writePos = pCompressed;
*(uint64 *)writePos = g_ulSteamIdCurrent;//76561198051972183;
writePos += 8;
int encodeRes = StreamEncode(pUncompressedBytes, nSamples, writePos, maxCompressedBytes - 12, bFinal);
if (encodeRes <= 0) {

View File

@ -59,82 +59,88 @@ bool VoiceEncoder_Silk::ResetState()
int VoiceEncoder_Silk::Compress(const char *pUncompressedIn, int nSamplesIn, char *pCompressed, int maxCompressedBytes, bool bFinal)
{
signed int nSamplesToUse; // edi@4
const __int16 *psRead; // ecx@4
int nSamples; // edi@5
int nSamplesToEncode; // esi@6
char *pWritePos; // ebp@6
int nSamplesPerFrame; // [sp+28h] [bp-44h]@5
const char *pWritePosMax; // [sp+2Ch] [bp-40h]@5
int nSamplesRemaining; // [sp+38h] [bp-34h]@5
const int inSampleRate = 8000;
const int nSampleDataMinMS = 100;
const int nSamplesMin = inSampleRate * nSampleDataMinMS / 1000;
const int nSampleDataMinMS = 20;
const int nSamplesMin = (nSampleDataMinMS * inSampleRate) / 1000;
/*
if ((nSamplesIn + m_bufOverflowBytes.TellPut() / 2) < nSamplesMin && !bFinal) {
m_bufOverflowBytes.Put(pUncompressedIn, 2 * nSamplesIn);
if (nSamplesIn + GetNumQueuedEncodingSamples() < nSamplesMin && !bFinal)
{
m_bufOverflowBytes.Put(pUncompressedIn, nSamplesIn * BYTES_PER_SAMPLE);
return 0;
}
*/
if (m_bufOverflowBytes.TellPut()) {
m_bufOverflowBytes.Put(pUncompressedIn, 2 * nSamplesIn);
const int16_t *psRead = (const int16_t *)pUncompressedIn;
psRead = (const __int16 *)m_bufOverflowBytes.Base();
nSamplesToUse = m_bufOverflowBytes.TellPut() / 2;
} else {
psRead = (const __int16 *)pUncompressedIn;
nSamplesToUse = nSamplesIn;
int nSamplesToUse = nSamplesIn;
int nSamplesPerFrame = nSamplesMin;
int nSamplesRemaining = nSamplesIn % nSamplesMin;
if (m_bufOverflowBytes.TellPut() || nSamplesRemaining && bFinal)
{
m_bufOverflowBytes.Put(pUncompressedIn, nSamplesIn * BYTES_PER_SAMPLE);
nSamplesToUse = GetNumQueuedEncodingSamples();
nSamplesRemaining = nSamplesToUse % nSamplesPerFrame;
if (bFinal && nSamplesRemaining)
{
// fill samples of silence at the remaining bytes
for (int i = nSamplesPerFrame - nSamplesRemaining; i > 0; i--)
{
m_bufOverflowBytes.PutShort(0);
}
nSamplesPerFrame = inSampleRate / 50;
nSamplesToUse = GetNumQueuedEncodingSamples();
nSamplesRemaining = nSamplesToUse % nSamplesPerFrame;
pWritePosMax = pCompressed + maxCompressedBytes;
nSamples = nSamplesToUse - nSamplesRemaining;
pWritePos = pCompressed;
}
psRead = (const int16_t *)m_bufOverflowBytes.Base();
Assert(!bFinal || nSamplesRemaining == 0);
}
char *pWritePos = pCompressed;
const char *pWritePosMax = &pCompressed[maxCompressedBytes];
int nSamples = nSamplesToUse - nSamplesRemaining;
while (nSamples > 0)
{
int16 *pWritePayloadSize = (int16 *)pWritePos;
pWritePos += sizeof(int16); //leave 2 bytes for the frame size (will be written after encoding)
int16_t *pWritePayloadSize = (int16_t *)pWritePos;
pWritePos += sizeof(int16_t);
int originalNBytes = (pWritePosMax - pWritePos > 0xFFFF) ? -1 : (pWritePosMax - pWritePos);
nSamplesToEncode = (nSamples < nSamplesPerFrame) ? nSamples : nSamplesPerFrame;
m_encControl.maxInternalSampleRate = 24000;
m_encControl.useInBandFEC = 0;
m_encControl.useDTX = 1;
m_encControl.complexity = 2;
m_encControl.API_sampleRate = inSampleRate;
m_encControl.packetSize = 20 * (inSampleRate / 1000);
m_encControl.packetLossPercentage = m_packetLoss_perc;
m_encControl.bitRate = 30000;//(m_targetRate_bps >= 0) ? m_targetRate_bps : 0;//clamp(voice_silk_bitrate.GetInt(), 10000, 40000);
this->m_encControl.useDTX = 0;
this->m_encControl.maxInternalSampleRate = 16000;
this->m_encControl.useInBandFEC = 0;
this->m_encControl.API_sampleRate = inSampleRate;
this->m_encControl.complexity = 2;
this->m_encControl.packetSize = 20 * (inSampleRate / 1000);
this->m_encControl.packetLossPercentage = this->m_packetLoss_perc;
this->m_encControl.bitRate = (m_targetRate_bps >= 0) ? m_targetRate_bps : 0;
int nSamplesToEncode = min(nSamples, nSamplesPerFrame);
int nBytes = ((pWritePosMax - pWritePos) < 0x7FFF) ? (pWritePosMax - pWritePos) : 0x7FFF;
int ret = SKP_Silk_SDK_Encode(m_pEncoder, &m_encControl, psRead, nSamplesToEncode, (unsigned char *)pWritePos, (__int16 *)&nBytes);
*pWritePayloadSize = nBytes; // write frame size
nSamples -= nSamplesToEncode;
int16 nBytes = originalNBytes;
int res = SKP_Silk_SDK_Encode(this->m_pEncoder, &this->m_encControl, psRead, nSamplesToEncode, (unsigned char *)pWritePos, &nBytes);
*pWritePayloadSize = nBytes; //write frame size
pWritePos += nBytes;
psRead += nSamplesToEncode;
pWritePos += nBytes;
}
m_bufOverflowBytes.Clear();
if (nSamplesRemaining <= nSamplesIn && nSamplesRemaining) {
m_bufOverflowBytes.Put(&pUncompressedIn[2 * (nSamplesIn - nSamplesRemaining)], 2 * nSamplesRemaining);
if (nSamplesRemaining && nSamplesRemaining <= nSamplesIn)
{
m_bufOverflowBytes.Put(pUncompressedIn + ((nSamplesIn - nSamplesRemaining) * sizeof(int16_t)), nSamplesRemaining * BYTES_PER_SAMPLE);
}
if (bFinal)
{
ResetState();
if (pWritePosMax > pWritePos + 2) {
uint16 *pWriteEndFlag = (uint16*)pWritePos;
pWritePos += sizeof(uint16);
if (pWritePosMax > pWritePos + 2)
{
uint16_t *pWriteEndFlag = (uint16_t *)pWritePos;
pWritePos += sizeof(uint16_t);
*pWriteEndFlag = 0xFFFF;
}
}

View File

@ -52,7 +52,7 @@ DLL_FUNCTIONS g_DLLFuncTable =
NULL, // pfnClientDisconnect
NULL, // pfnClientKill
NULL, // pfnClientPutInServer
NULL, // pfnClientCommand
&ClientCommand, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
NULL, // pfnServerActivate
NULL, // pfnServerDeactivate
@ -105,14 +105,14 @@ DLL_FUNCTIONS g_DLLFuncTable_Post =
NULL, // pfnClientConnect
NULL, // pfnClientDisconnect
NULL, // pfnClientKill
NULL, // pfnClientPutInServer
&ClientPutInServer_PostHook, // pfnClientPutInServer
NULL, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
&ServerActivate_PostHook, // pfnServerActivate
NULL, // pfnServerDeactivate
NULL, // pfnPlayerPreThink
NULL, // pfnPlayerPostThink
NULL, // pfnStartFrame
&StartFrame_Post, // pfnStartFrame
NULL, // pfnParmsNewLevel
NULL, // pfnParmsChangeLevel
NULL, // pfnGetGameDescription

View File

@ -18,6 +18,9 @@ bool Revoice_Init_Config()
const char *pszGameDir = GET_GAME_INFO(PLID, GINFO_GAMEDIR);
const char *pszPluginDir = GET_PLUGIN_PATH(PLID);
printf("> pszGameDir: (%s)\n", pszGameDir);
printf("> pszPluginDir: (%s)\n", pszPluginDir);
char szRelativePath[MAX_PATH];
strncpy(szRelativePath, &pszPluginDir[strlen(pszGameDir) + 1], sizeof(szRelativePath) - 1);
szRelativePath[sizeof(szRelativePath) - 1] = '\0';
@ -94,7 +97,7 @@ void Cmd_REV_Status()
for (int i = 0; i < g_RehldsSvs->GetMaxClients(); i++) {
auto plr = &g_Players[i];
if (plr->IsConnected()) {
printf("#%-4i %-32s %-6s %-4i %-2i %-3s", i + 1, UTIL_VarArgs("\"%s\"", plr->GetClient()->GetName()), plr->GetCodecTypeToString(), plr->GetVoiceRate(), plr->GetProtocol(), plr->IsHLTV() ? " (HLTV)" : "");
UTIL_ServerPrintf("#%-4i %-32s %-6s %-4i %-2i %-3s", i + 1, UTIL_VarArgs("\"%s\"", plr->GetClient()->GetName()), plr->GetCodecTypeToString(), plr->GetVoiceRate(), plr->GetProtocol(), plr->IsHLTV() ? " (HLTV)" : "");
nUsers++;
}
}

View File

@ -20,7 +20,7 @@ void CvarValue2_PreHook(const edict_t *pEnt, int requestID, const char *cvarName
if (lastSeparator)
{
int buildNumber = atoi(lastSeparator + 1);
if (buildNumber > 4554) {
if (buildNumber > 4554 || buildNumber == 2017) {
plr->SetCodecType(vct_opus);
}
}
@ -75,52 +75,34 @@ void SV_ParseVoiceData_emu(IGameClient *cl)
}
CRevoicePlayer *srcPlayer = GetPlayerByClientPtr(cl);
srcPlayer->SetLastVoiceTime(g_RehldsSv->GetTime());
//FILE *fp = fopen("recorder.snd", "ab");
//if (fp)
//{
// printf(" -> Write chunk: (%d)\n", nDataLength);
// fwrite(chReceived, 1, nDataLength, fp);
// fclose(fp);
//}
/*srcPlayer->SetLastVoiceTime(g_RehldsSv->GetTime());
srcPlayer->IncreaseVoiceRate(nDataLength);
char transcodedBuf[4096];
char compressedBuf[16384];
int compressedSize = TranscodeVoice(srcPlayer, chReceived, nDataLength, srcPlayer->GetOpusCodec(), srcPlayer->GetSilkCodec(), compressedBuf, sizeof(compressedBuf));
char *silkData = nullptr;
char *speexData = nullptr;
uint32_t computedChecksum = crc32(compressedBuf, compressedSize - 4);
uint32 wireChecksum = *(uint32 *)(compressedBuf + compressedSize - 4);
int silkDataLen = 0;
int speexDataLen = 0;
switch (srcPlayer->GetCodecType())
FILE *fp = fopen("recorder.snd", "ab");
if (fp)
{
case vct_silk:
{
if (nDataLength > MAX_SILK_DATA_LEN || srcPlayer->GetVoiceRate() > MAX_SILK_VOICE_RATE)
return;
printf(" -> Write chunk: (%d), computedChecksum: (%u), wireChecksum: (%u)\n", compressedSize, computedChecksum, wireChecksum);
printf(" -> Write chunk: (%d), computedChecksum: (%u), wireChecksum: (%u)\n", compressedSize, computedChecksum, wireChecksum);
fwrite(compressedBuf, 1, compressedSize, fp);
fclose(fp);
}*/
silkData = chReceived; silkDataLen = nDataLength;
speexData = transcodedBuf;
speexDataLen = TranscodeVoice(srcPlayer, silkData, silkDataLen, srcPlayer->GetSilkCodec(), srcPlayer->GetSpeexCodec(), transcodedBuf, sizeof(transcodedBuf));
break;
}
case vct_opus:
{
if (nDataLength > MAX_OPUS_DATA_LEN || srcPlayer->GetVoiceRate() > MAX_OPUS_VOICE_RATE)
return;
silkData = chReceived; silkDataLen = nDataLength;
speexData = transcodedBuf;
speexDataLen = TranscodeVoice(srcPlayer, silkData, silkDataLen, srcPlayer->GetOpusCodec(), srcPlayer->GetSpeexCodec(), transcodedBuf, sizeof(transcodedBuf));
break;
}
case vct_speex:
{
if (nDataLength > MAX_SPEEX_DATA_LEN || srcPlayer->GetVoiceRate() > MAX_SPEEX_VOICE_RATE)
return;
speexData = chReceived; speexDataLen = nDataLength;
silkData = transcodedBuf;
silkDataLen = TranscodeVoice(srcPlayer, speexData, speexDataLen, srcPlayer->GetSpeexCodec(), srcPlayer->GetSilkCodec(), transcodedBuf, sizeof(transcodedBuf));
break;
}
default:
return;
}
//return;
int maxclients = g_RehldsSvs->GetMaxClients();
for (int i = 0; i < maxclients; i++)
@ -128,43 +110,21 @@ void SV_ParseVoiceData_emu(IGameClient *cl)
CRevoicePlayer *dstPlayer = &g_Players[i];
IGameClient *dstClient = dstPlayer->GetClient();
if (!((1 << i) & cl->GetVoiceStream(0)) && dstPlayer != srcPlayer)
if (!((1 << i) & cl->GetVoiceStream(0)) && dstPlayer != srcPlayer) {
continue;
if (!dstClient->IsActive() && !dstClient->IsConnected() && dstPlayer != srcPlayer)
continue;
char *sendBuf;
int nSendLen;
switch (dstPlayer->GetCodecType())
{
case vct_silk:
case vct_opus:
sendBuf = silkData;
nSendLen = silkDataLen;
break;
case vct_speex:
sendBuf = speexData;
nSendLen = speexDataLen;
break;
default:
sendBuf = nullptr;
nSendLen = 0;
break;
}
if (sendBuf == nullptr || nSendLen == 0)
if (!dstClient->IsActive() && !dstClient->IsConnected() && dstPlayer != srcPlayer) {
continue;
if (dstPlayer == srcPlayer && !dstClient->GetLoopback())
nSendLen = 0;
}
sizebuf_t *dstDatagram = dstClient->GetDatagram();
if (dstDatagram->cursize + nSendLen + 6 < dstDatagram->maxsize) {
if (dstDatagram->cursize + nDataLength + 6 < dstDatagram->maxsize)
{
g_RehldsFuncs->MSG_WriteByte(dstDatagram, svc_voicedata);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, cl->GetId());
g_RehldsFuncs->MSG_WriteShort(dstDatagram, nSendLen);
g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nSendLen, sendBuf);
g_RehldsFuncs->MSG_WriteShort(dstDatagram, nDataLength);
g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nDataLength, chReceived);
}
}
}
@ -172,10 +132,10 @@ void SV_ParseVoiceData_emu(IGameClient *cl)
void Rehlds_HandleNetCommand(IRehldsHook_HandleNetCommand *chain, IGameClient *cl, int8 opcode)
{
const int clc_voicedata = 8;
if (opcode == clc_voicedata) {
SV_ParseVoiceData_emu(cl);
return;
}
//if (opcode == clc_voicedata) {
// SV_ParseVoiceData_emu(cl);
// return;
//}
chain->callNext(cl, opcode);
}
@ -188,6 +148,438 @@ qboolean ClientConnect_PreHook(edict_t *pEntity, const char *pszName, const char
RETURN_META_VALUE(MRES_IGNORED, TRUE);
}
enum VoiceCodec_e
{
VC_SPEEX = 0,
VC_SILK,
VC_MAX
};
struct SpeexContext_t
{
VoiceCodec_e type;
const char *filename;
char *data;
int nDataBytes;
int wBitsPerSample;
int frame;
bool play;
bool load;
bool failed;
bool send_order;
double time;
double nextsend;
float framerate;
int index;
int startindex;
uint64_t steamid;
IVoiceCodec *encoder;
};
int g_tracks_silk = 0;
int g_tracks_speex = 0;
int g_tracksNumbers[VC_MAX][16] = {0};
uint64_t g_ulSteamIdCurrent = 0ULL;
SpeexContext_t g_SoundLists[] =
{
{ VC_SPEEX, "kolshik.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 8000, 2, 0, 76561198051972183ULL, nullptr },
{ VC_SPEEX, "pchela.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 11025, 3, 0, 76561198051972185ULL, nullptr },
{ VC_SILK, "c2a2_ba_launch.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 8000, 7, 0, 76561198051972186ULL, nullptr },
{ VC_SILK, "cslig_nuages.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 8000, 7, 0, 76561198051972182ULL, nullptr },
{ VC_SILK, "kolshik.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 8000, 4, 0, 76561198051972187ULL, nullptr },
{ VC_SILK, "dance.wav", nullptr, 0, 0, 0, false, false, false, false, 0, 0, 8000, 5, 102832, 76561198051972188ULL, nullptr },
};
void ClientCommand(edict_t *pEdict)
{
const char *argv1 = CMD_ARGV(0);
if (_stricmp(argv1, "speexmulti") == 0 || _stricmp(argv1, "silkmulti") == 0)
{
VoiceCodec_e type = _stricmp(argv1, "speexmulti") == 0 ? VC_SPEEX : VC_SILK;
if (type == VC_SILK)
{
g_SoundLists[2].play ^= true;
g_SoundLists[3].play ^= true;
g_SoundLists[4].play ^= true;
g_SoundLists[5].play ^= true;
if (g_SoundLists[2].play)
g_SoundLists[2].time = gpGlobals->time;
if (g_SoundLists[3].play)
g_SoundLists[3].time = gpGlobals->time;
if (g_SoundLists[4].play)
g_SoundLists[4].time = gpGlobals->time;
if (g_SoundLists[5].play)
g_SoundLists[5].time = gpGlobals->time;
g_SoundLists[2].frame = g_SoundLists[2].startindex;
g_SoundLists[3].frame = g_SoundLists[3].startindex;
g_SoundLists[4].frame = g_SoundLists[4].startindex;
g_SoundLists[5].frame = g_SoundLists[5].startindex;
}
else
{
/*
g_SoundLists[0].play ^= true;
g_SoundLists[1].play ^= true;
if (g_SoundLists[0].play)
{
g_SoundLists[0].time = gpGlobals->time;
}
if (g_SoundLists[1].play)
{
g_SoundLists[1].time = gpGlobals->time;
}
g_SoundLists[0].frame = 0;
g_SoundLists[1].frame = 0;
*/
}
RETURN_META(MRES_SUPERCEDE);
}
else if (_stricmp(argv1, "speexplay") == 0 || _stricmp(argv1, "silkplay") == 0)
{
VoiceCodec_e type = _stricmp(argv1, "speexplay") == 0 ? VC_SPEEX : VC_SILK;
if (CMD_ARGC() < 2)
{
int nIndex = 0;
bool header_once = false;
for (auto &list : g_SoundLists)
{
if (list.type != type)
continue;
if (!header_once)
{
header_once = true;
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("\nusage: silkplay [ <tracknumber> ]\n\n"));
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("%4s %-32s %-10s %-12s\n", "#", "[name]", "[chunk]", "[framerate]"));
}
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("%4d. %-32s %-10d %-12.2f\n", nIndex++, list.filename, list.nDataBytes, list.framerate));
}
CLIENT_PRINTF(pEdict, print_console, "\n");
RETURN_META(MRES_SUPERCEDE);
}
int trackNumber = atoi(CMD_ARGV(1));
if (trackNumber < 0 || trackNumber >= (type == VC_SPEEX ? g_tracks_speex : g_tracks_silk))
{
trackNumber = 0;
}
trackNumber = g_tracksNumbers[type][trackNumber];
g_SoundLists[trackNumber].play ^= true;
if (g_SoundLists[trackNumber].play)
{
g_SoundLists[trackNumber].time = gpGlobals->time;
}
g_SoundLists[trackNumber].frame = g_SoundLists[trackNumber].startindex;
g_SoundLists[trackNumber].type = type;
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs(" -> send voice are %s, track: (%s)\n", g_SoundLists[trackNumber].play ? "RUNNING" : "STOPPED", g_SoundLists[trackNumber].filename));
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}
unsigned long ReadDWord(FILE *fp)
{
unsigned long ret;
fread(&ret, 4, 1, fp);
return ret;
}
unsigned short ReadWord(FILE *fp)
{
unsigned short ret;
fread(&ret, 2, 1, fp);
return ret;
}
bool ReadWaveFile(const char *pFilename, char *&pData, int &nDataBytes, int &wBitsPerSample, int &nChannels, int &nSamplesPerSec)
{
FILE *fp = fopen(pFilename, "rb");
if(!fp)
return false;
fseek(fp, 22, SEEK_SET);
nChannels = ReadWord(fp);
nSamplesPerSec = ReadDWord(fp);
fseek(fp, 34, SEEK_SET);
wBitsPerSample = ReadWord(fp);
fseek(fp, 40, SEEK_SET);
nDataBytes = ReadDWord(fp);
ReadDWord(fp);
pData = new char[nDataBytes];
if(!pData)
{
fclose(fp);
return false;
}
fread(pData, nDataBytes, 1, fp);
fclose(fp);
return true;
}
int Voice_GetSpeexCompressed(SpeexContext_t &snd, char *pchDest, int nCount, bool bFinal)
{
if (!snd.encoder)
{
snd.encoder = new VoiceCodec_Frame(new VoiceEncoder_Speex());
snd.encoder->Init(SPEEX_VOICE_QUALITY);
}
if (snd.encoder)
{
int gotten;
short tempData[8192];
// If they want to get the data from a file instead of the mic, use that.
if (snd.data)
{
double curtime = gpGlobals->time;
int nShouldGet = (curtime - snd.time) * 8000;
gotten = min((signed)sizeof(tempData) / 2, min(nShouldGet, (snd.nDataBytes - snd.frame) / 2));
memcpy(tempData, &snd.data[snd.frame], gotten * 2);
snd.frame += gotten * 2;
snd.time = curtime;
return snd.encoder->Compress((char *)tempData, gotten, pchDest, nCount, !!bFinal);
}
}
return 0;
}
char uncompressedBuf[12002];
int Voice_GetSilkCompressed(SpeexContext_t &snd, char *pchDest, int nCount, bool bFinal)
{
if (!snd.encoder)
{
snd.encoder = new CSteamP2PCodec(new VoiceEncoder_Silk());
snd.encoder->Init(SILK_VOICE_QUALITY);
}
if (snd.encoder)
{
int gotten;
short tempData[8192];
// If they want to get the data from a file instead of the mic, use that.
if (snd.data)
{
double curtime = gpGlobals->time;
int nShouldGet = (curtime - snd.time) * 8000;
gotten = min((signed)sizeof(tempData) / 2, min(nShouldGet, (snd.nDataBytes - snd.frame) / 2));
memcpy(tempData, &snd.data[snd.frame], gotten * 2);
snd.frame += gotten * 2;
snd.time = curtime;
g_ulSteamIdCurrent = snd.steamid;
int res = snd.encoder->Compress((char *)tempData, gotten, pchDest, nCount, !!bFinal);
g_ulSteamIdCurrent = 0ULL;
return res;
}
}
return 0;
}
int Voice_GetCompressedData(SpeexContext_t &snd, char *pchDest, int nCount, bool bFinal)
{
switch (snd.type)
{
case VC_SPEEX:
return Voice_GetSpeexCompressed(snd, pchDest, nCount, bFinal);
case VC_SILK:
return Voice_GetSilkCompressed(snd, pchDest, nCount, bFinal);
default:
break;
}
return 0;
}
void StartFrame_Post()
{
static bool all_sound_initializes = false;
if (!all_sound_initializes)
{
for (auto &list : g_SoundLists)
{
if (list.load || list.failed)
{
continue;
}
int nDataBytes, wBitsPerSample, nChannels, nSamplesPerSec;
list.load = ReadWaveFile(list.filename, list.data, nDataBytes, wBitsPerSample, nChannels, nSamplesPerSec);
list.frame = 0;
list.nDataBytes = nDataBytes;
list.wBitsPerSample = wBitsPerSample;
list.time = gpGlobals->time;
//if (list.type == VC_SILK)
//{
// if (wBitsPerSample != 16 || nChannels != 1 || nSamplesPerSec != 24000)
// {
// printf(" > %s Wave file mismatch was got %d bits, %d channels, "
// "%d sample rate was expecting %d bits, %d channels, %d sample rate\n",
// list.filename, wBitsPerSample, nChannels, nSamplesPerSec, 16, 1, 24000);
//
// delete [] list.data;
// list.load = false;
// list.failed = true;
// continue;
// }
//}
if (list.load)
{
g_tracksNumbers[list.type][list.type == VC_SPEEX ? g_tracks_speex++ : g_tracks_silk++] = &list - g_SoundLists;
printf(" '%s' trackid: (%d), Load: (%s), chunk: (%d), framerate: (%0.2f), wBitsPerSample: (%d), nDataBytes: (%d), nSamplesPerSec: (%d)\n",
list.type == VC_SPEEX ? "SPEEX" : "SILK", g_tracksNumbers[list.type][(list.type == VC_SPEEX ? g_tracks_speex++ : g_tracks_silk) - 1],
list.filename, list.nDataBytes, list.framerate, list.wBitsPerSample, nDataBytes, nSamplesPerSec);
}
}
}
all_sound_initializes = true;
for (auto &list : g_SoundLists)
{
if (!list.load)
{
all_sound_initializes = false;
break;
}
}
static float fltime = 0.0f;
if (fltime > gpGlobals->time)
{
RETURN_META(MRES_IGNORED);
}
fltime = gpGlobals->time + 0.02f;// - 0.000125;
for (int snd = 0; snd < ARRAYSIZE(g_SoundLists); snd++)
{
if (!g_SoundLists[snd].play)
continue;
char uchVoiceData[4096];
bool bFinal = false;
int nDataLength = Voice_GetCompressedData(g_SoundLists[snd], uchVoiceData, sizeof(uchVoiceData), bFinal);
if (nDataLength <= 0)
continue;
if (g_SoundLists[snd].frame >= g_SoundLists[snd].nDataBytes)// || nDataLength <= 0)
{
g_SoundLists[snd].play = false;
g_SoundLists[snd].frame = 0;
//printf("> HIT END\n");
continue;
}
int maxclients = g_RehldsSvs->GetMaxClients();
for (int i = 0; i < maxclients; i++)
{
CRevoicePlayer *dstPlayer = &g_Players[i];
IGameClient *dstClient = dstPlayer->GetClient();
if (!dstClient->IsActive() && !dstClient->IsConnected()) {
continue;
}
sizebuf_t *dstDatagram = dstClient->GetDatagram();
if (dstDatagram->cursize + nDataLength + 6 < dstDatagram->maxsize)
{
//CLIENT_PRINTF(dstPlayer->GetClient()->GetEdict(), print_console, UTIL_VarArgs(" -> chunk: %4d / %4d, chunksize: %4d, progress: %3.2f%%\n", g_SoundLists[snd].frame, g_SoundLists[snd].nDataBytes, nDataLength, ((double)g_SoundLists[snd].frame / (double)g_SoundLists[snd].nDataBytes) * 100.0));
printf(" -> chunk: %4d / %4d, chunksize: %4d, progress: %3.2f%%\n", g_SoundLists[snd].frame, g_SoundLists[snd].nDataBytes, nDataLength, ((double)g_SoundLists[snd].frame / (double)g_SoundLists[snd].nDataBytes) * 100.0);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, svc_voicedata);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, g_SoundLists[snd].index);
g_RehldsFuncs->MSG_WriteShort(dstDatagram, nDataLength);
g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nDataLength, uchVoiceData);
}
}
}
RETURN_META(MRES_IGNORED);
}
static const char *steamids[]
{
"76561198051972183",
"76561198051972184",
"76561198051972185",
"76561198051972186",
"76561198051972187",
"76561198051972188",
"76561198051972189",
"76561198051972190",
"76561198051972191",
"76561198051972192",
"76561198051972193",
"76561198051972194",
"76561198051972195",
"76561198051972196",
"76561198051972197",
};
void ClientPutInServer_PostHook(edict_t *pEntity)
{
IGameClient* client = g_RehldsSvs->GetClient(ENTINDEX(pEntity) - 1);
sizebuf_t *buf = client->GetNetChan()->GetMessageBuf();
unsigned char digest[] =
{
0xcd, 0xff, 0xcc, 0x70, 0xda, 0x4a, 0x79, 0x1c, 0xea, 0x66, 0xba, 0xaa, 0xad, 0x2b, 0x40, 0x01
};
//memset(digest, 0, sizeof(digest));
for (int i = 1; i < 8; i++)
{
char name[64];
_snprintf(name, sizeof(name), "play-voice-%i", i);
char userinfo[256];
_snprintf(userinfo, sizeof(userinfo), "\\bottomcolor\\0\\topcolor\\0\\name\\%s\\model\\urban\\*sid\\%s", name, steamids[i]);
g_RehldsFuncs->MSG_WriteByte(buf, 13); // svc_updateuserinfo
g_RehldsFuncs->MSG_WriteByte(buf, i);
g_RehldsFuncs->MSG_WriteLong(buf, i - 1);
g_RehldsFuncs->MSG_WriteString(buf, userinfo);
g_RehldsFuncs->MSG_WriteBuf(buf, sizeof(digest), digest);
}
SET_META_RESULT(MRES_IGNORED);
}
void ServerActivate_PostHook(edict_t *pEdictList, int edictCount, int clientMax)
{
Revoice_Exec_Config();

View File

@ -9,5 +9,7 @@ bool Revoice_Main_Init();
void CvarValue2_PreHook(const edict_t *pEnt, int requestID, const char *cvarName, const char *cvarValue);
qboolean ClientConnect_PreHook(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]);
void ClientPutInServer_PostHook(edict_t *pEntity);
void ServerActivate_PostHook(edict_t *pEdictList, int edictCount, int clientMax);
void StartFrame_Post();
void ClientCommand(edict_t *pEdict);

View File

@ -0,0 +1,494 @@
#include "precompiled.h"
void SV_DropClient_hook(IRehldsHook_SV_DropClient *chain, IGameClient *cl, bool crash, const char *msg)
{
CRevoicePlayer *plr = GetPlayerByClientPtr(cl);
plr->OnDisconnected();
chain->callNext(cl, crash, msg);
}
void CvarValue2_PreHook(const edict_t *pEnt, int requestID, const char *cvarName, const char *cvarValue)
{
CRevoicePlayer *plr = GetPlayerByEdict(pEnt);
if (plr->GetRequestId() != requestID) {
RETURN_META(MRES_IGNORED);
}
const char *lastSeparator = strrchr(cvarValue, ',');
if (lastSeparator)
{
int buildNumber = atoi(lastSeparator + 1);
if (buildNumber > 4554 || buildNumber == 2017) {
plr->SetCodecType(vct_opus);
}
}
RETURN_META(MRES_IGNORED);
}
int TranscodeVoice(CRevoicePlayer *srcPlayer, const char *srcBuf, int srcBufLen, IVoiceCodec *srcCodec, IVoiceCodec *dstCodec, char *dstBuf, int dstBufSize)
{
char decodedBuf[32768];
int numDecodedSamples = srcCodec->Decompress(srcBuf, srcBufLen, decodedBuf, sizeof(decodedBuf));
if (numDecodedSamples <= 0) {
return 0;
}
int compressedSize = dstCodec->Compress(decodedBuf, numDecodedSamples, dstBuf, dstBufSize, false);
if (compressedSize <= 0) {
return 0;
}
/*
int numDecodedSamples2 = dstCodec->Decompress(dstBuf, compressedSize, decodedBuf, sizeof(decodedBuf));
if (numDecodedSamples2 <= 0) {
return compressedSize;
}
FILE *rawSndFile = fopen("d:\\revoice_raw.snd", "ab");
if (rawSndFile) {
fwrite(decodedBuf, 2, numDecodedSamples2, rawSndFile);
fclose(rawSndFile);
}
*/
return compressedSize;
}
void SV_ParseVoiceData_emu(IGameClient *cl)
{
char chReceived[4096];
unsigned int nDataLength = g_RehldsFuncs->MSG_ReadShort();
if (nDataLength > sizeof(chReceived)) {
g_RehldsFuncs->DropClient(cl, FALSE, "Invalid voice data\n");
return;
}
g_RehldsFuncs->MSG_ReadBuf(nDataLength, chReceived);
if (g_pcv_sv_voiceenable->value == 0.0f) {
return;
}
CRevoicePlayer *srcPlayer = GetPlayerByClientPtr(cl);
//FILE *fp = fopen("recorder.snd", "ab");
//if (fp)
//{
// printf(" -> Write chunk: (%d)\n", nDataLength);
// fwrite(chReceived, 1, nDataLength, fp);
// fclose(fp);
//}
/*srcPlayer->SetLastVoiceTime(g_RehldsSv->GetTime());
srcPlayer->IncreaseVoiceRate(nDataLength);
char compressedBuf[16384];
int compressedSize = TranscodeVoice(srcPlayer, chReceived, nDataLength, srcPlayer->GetOpusCodec(), srcPlayer->GetSilkCodec(), compressedBuf, sizeof(compressedBuf));
uint32_t computedChecksum = crc32(compressedBuf, compressedSize - 4);
uint32 wireChecksum = *(uint32 *)(compressedBuf + compressedSize - 4);
FILE *fp = fopen("recorder.snd", "ab");
if (fp)
{
printf(" -> Write chunk: (%d), computedChecksum: (%u), wireChecksum: (%u)\n", compressedSize, computedChecksum, wireChecksum);
printf(" -> Write chunk: (%d), computedChecksum: (%u), wireChecksum: (%u)\n", compressedSize, computedChecksum, wireChecksum);
fwrite(compressedBuf, 1, compressedSize, fp);
fclose(fp);
}*/
//return;
int maxclients = g_RehldsSvs->GetMaxClients();
for (int i = 0; i < maxclients; i++)
{
CRevoicePlayer *dstPlayer = &g_Players[i];
IGameClient *dstClient = dstPlayer->GetClient();
if (!((1 << i) & cl->GetVoiceStream(0)) && dstPlayer != srcPlayer) {
//printf("-> #0\n");
continue;
}
if (!dstClient->IsActive() && !dstClient->IsConnected() && dstPlayer != srcPlayer) {
//printf("-> #1\n");
continue;
}
sizebuf_t *dstDatagram = dstClient->GetDatagram();
if (dstDatagram->cursize + nDataLength + 6 < dstDatagram->maxsize)
{
//uint32_t computedChecksum = crc32(compressedBuf, compressedSize - 4);
//uint32 wireChecksum = *(uint32 *)(compressedBuf + compressedSize - 4);
//FILE *fp = fopen("silk_chunk.snd", "ab");
//if (fp)
//{
// printf(" -> Write chunk: (%d), computedChecksum: (%u), wireChecksum: (%u)\n", compressedSize, computedChecksum, wireChecksum);
// fwrite(compressedBuf, 1, compressedSize, fp);
// fclose(fp);
//}
g_RehldsFuncs->MSG_WriteByte(dstDatagram, svc_voicedata);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, cl->GetId());
g_RehldsFuncs->MSG_WriteShort(dstDatagram, nDataLength);
g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nDataLength, chReceived);
}
}
}
void Rehlds_HandleNetCommand(IRehldsHook_HandleNetCommand *chain, IGameClient *cl, int8 opcode)
{
const int clc_voicedata = 8;
//if (opcode == clc_voicedata) {
// SV_ParseVoiceData_emu(cl);
// return;
//}
chain->callNext(cl, opcode);
}
qboolean ClientConnect_PreHook(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128])
{
CRevoicePlayer *plr = GetPlayerByEdict(pEntity);
plr->OnConnected();
RETURN_META_VALUE(MRES_IGNORED, TRUE);
}
struct SpeexContext_t
{
const char *filename;
char *data;
int chunk;
int frame;
bool play;
bool load;
bool send_order;
double time;
double nextsend;
float framerate;
int index;
IVoiceCodec *encoder;
};
int g_NumFullSended = 0;
SpeexContext_t g_SoundLists[] =
{
{ "akol.wav", nullptr, 0, 0, false, false, false, 0, 0, 8000, 4, nullptr },
//{ "akol2.wav", nullptr, 0, 0, false, false, false, 0, 0, 8000, 5, nullptr },
{ "apchela.wav", nullptr, 0, 0, false, false, false, 0, 0, 11025, 6, nullptr },
};
void ClientCommand(edict_t *pEdict)
{
const char *argv1 = CMD_ARGV(0);
if (_stricmp(argv1, "speexmulti") == 0)
{
g_SoundLists[0].play ^= true;
g_SoundLists[1].play ^= true;
if (g_SoundLists[0].play)
{
g_SoundLists[0].time = gpGlobals->time;
}
if (g_SoundLists[1].play)
{
g_SoundLists[1].time = gpGlobals->time;
}
g_SoundLists[0].frame = 0;
g_SoundLists[1].frame = 0;
RETURN_META(MRES_SUPERCEDE);
}
else if (_stricmp(argv1, "speexplay") == 0)
{
if (CMD_ARGC() < 2)
{
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("\nusage: speexplay [ <tracknumber> ]\n\n"));
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("%4s %-32s %-10s %-12s\n", "#", "[name]", "[chunk]", "[framerate]"));
int nIndex = 0;
for (auto &list : g_SoundLists)
{
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs("%4d. %-32s %-10d %-12.2f\n", nIndex++, list.filename, list.chunk, list.framerate));
}
CLIENT_PRINTF(pEdict, print_console, "\n");
RETURN_META(MRES_SUPERCEDE);
}
int trackNumber = atoi(CMD_ARGV(1));
if (trackNumber < 0 || trackNumber >= ARRAYSIZE(g_SoundLists))
{
trackNumber = 0;
}
g_SoundLists[trackNumber].play ^= true;
if (g_SoundLists[trackNumber].play)
{
g_SoundLists[trackNumber].time = gpGlobals->time;
}
g_SoundLists[trackNumber].frame = 0;
CLIENT_PRINTF(pEdict, print_console, UTIL_VarArgs(" -> send voice are %s, track: (%s)\n", g_SoundLists[trackNumber].play ? "RUNNING" : "STOPPED", g_SoundLists[trackNumber].filename));
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}
bool ReadWaveFile(const char *pFilename, char **pData, int &nDataBytes, int &nBitsPerSample, int &nChannels, int &nSamplesPerSec)
{
FILE *fp = fopen(pFilename, "rb");
if (!fp) {
return false;
}
int pOutput;
fseek(fp, 22, SEEK_SET);
fread(&pOutput, sizeof(uint16), 1, fp);
nChannels = (uint16)pOutput;
fread(&pOutput, sizeof(int), 1, fp);
nSamplesPerSec = pOutput;
fseek(fp, 34, SEEK_SET);
fread(&pOutput, sizeof(uint16), 1, fp);
nBitsPerSample = (uint16)pOutput;
fseek(fp, 40, SEEK_SET);
fread(&pOutput, sizeof(int), 1, fp);
nDataBytes = pOutput;
fread(&pOutput, sizeof(int), 1, fp);
*pData = new char[ nDataBytes ];
if (*pData)
{
fread(*pData, nDataBytes, 1, fp);
fclose(fp);
return true;
}
fclose(fp);
return false;
}
int Voice_GetCompressedData(SpeexContext_t &snd, char *pchDest, int nCount, bool bFinal)
{
if (!snd.encoder)
{
snd.encoder = new VoiceCodec_Frame(new VoiceEncoder_Speex());
snd.encoder->Init(SPEEX_VOICE_QUALITY);
}
if (snd.encoder)
{
int gotten;
short tempData[8192];
// If they want to get the data from a file instead of the mic, use that.
if (snd.data)
{
double curtime = gpGlobals->time;
int nShouldGet = (curtime - snd.time) * 8000;
gotten = min((signed)sizeof(tempData) / 2, min(nShouldGet, (snd.chunk - snd.frame) / 2));
memcpy(tempData, &snd.data[snd.frame], gotten * 2);
snd.frame += gotten * 2;
snd.time = curtime;
return snd.encoder->Compress((char *)tempData, gotten, pchDest, nCount, !!bFinal);
}
}
return 0;
}
#include "counter.h"
CCounter g_Timer;
#include <iostream>
#include <chrono>
void StartFrame_Post()
{
static bool all_sound_initializes = false;
if (!all_sound_initializes)
{
g_Timer.Init();
for (auto &list : g_SoundLists)
{
if (list.load)
{
continue;
}
int a, b, c;
list.load = ReadWaveFile(list.filename, &list.data, list.chunk, a, b, c);
list.frame = 0;
list.time = gpGlobals->time;
if (list.load)
{
printf(" Load: (%s), chunk: (%d), framerate: (%0.2f)\n", list.filename, list.chunk, list.framerate);
}
}
}
all_sound_initializes = true;
for (auto &list : g_SoundLists)
{
if (!list.load)
{
all_sound_initializes = false;
break;
}
}
static float fltime = 0.0f;
if (fltime > gpGlobals->time)
{
RETURN_META(MRES_IGNORED);
}
fltime = gpGlobals->time + 0.02f - 0.000125;
for (int snd = 0; snd < ARRAYSIZE(g_SoundLists); snd++)
{
if (!g_SoundLists[snd].play)
continue;
char uchVoiceData[4096];
bool bFinal = false;
int nDataLength = Voice_GetCompressedData(g_SoundLists[snd], uchVoiceData, sizeof(uchVoiceData), bFinal);
if (g_SoundLists[snd].frame >= g_SoundLists[snd].chunk)// || nDataLength <= 0)
{
g_SoundLists[snd].play = false;
g_SoundLists[snd].frame = 0;
//printf("> HIT END\n");
continue;
}
int maxclients = g_RehldsSvs->GetMaxClients();
for (int i = 0; i < maxclients; i++)
{
CRevoicePlayer *dstPlayer = &g_Players[i];
IGameClient *dstClient = dstPlayer->GetClient();
if (!dstClient->IsActive() && !dstClient->IsConnected()) {
continue;
}
sizebuf_t *dstDatagram = dstClient->GetDatagram();
if (dstDatagram->cursize + nDataLength + 6 < dstDatagram->maxsize)
{
//CLIENT_PRINTF(dstPlayer->GetClient()->GetEdict(), print_console, UTIL_VarArgs(" -> chunk: %4d / %4d, chunksize: %4d, progress: %3.2f%%\n", g_SoundLists[snd].frame, g_SoundLists[snd].chunk, nDataLength, ((double)g_SoundLists[snd].frame / (double)g_SoundLists[snd].chunk) * 100.0));
printf(" -> chunk: %4d / %4d, chunksize: %4d, progress: %3.2f%%\n", g_SoundLists[snd].frame, g_SoundLists[snd].chunk, nDataLength, ((double)g_SoundLists[snd].frame / (double)g_SoundLists[snd].chunk) * 100.0);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, svc_voicedata);
g_RehldsFuncs->MSG_WriteByte(dstDatagram, g_SoundLists[snd].index);
g_RehldsFuncs->MSG_WriteShort(dstDatagram, nDataLength);
g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nDataLength, uchVoiceData);
}
}
}
RETURN_META(MRES_IGNORED);
}
void ServerActivate_PostHook(edict_t *pEdictList, int edictCount, int clientMax)
{
Revoice_Exec_Config();
SET_META_RESULT(MRES_IGNORED);
}
void SV_WriteVoiceCodec_hooked(IRehldsHook_SV_WriteVoiceCodec *chain, sizebuf_t *sb)
{
IGameClient *cl = g_RehldsFuncs->GetHostClient();
CRevoicePlayer *plr = GetPlayerByClientPtr(cl);
switch (plr->GetCodecType())
{
case vct_silk:
case vct_opus:
case vct_speex:
{
g_RehldsFuncs->MSG_WriteByte(sb, svc_voiceinit);
g_RehldsFuncs->MSG_WriteString(sb, "voice_speex"); // codec id
g_RehldsFuncs->MSG_WriteByte(sb, 5); // quality
break;
}
default:
LCPrintf(true, "SV_WriteVoiceCodec() called on client(%d) with unknown voice codec\n", cl->GetId());
break;
}
}
bool Revoice_Load()
{
if (!Revoice_Utils_Init())
return false;
if (!Revoice_RehldsApi_Init()) {
LCPrintf(true, "Failed to locate REHLDS API\n");
return false;
}
if (!Revoice_ReunionApi_Init())
return false;
Revoice_Init_Cvars();
Revoice_Init_Config();
Revoice_Init_Players();
if (!Revoice_Main_Init()) {
LCPrintf(true, "Initialization failed\n");
return false;
}
return true;
}
bool Revoice_Main_Init()
{
g_RehldsHookchains->SV_DropClient()->registerHook(&SV_DropClient_hook, HC_PRIORITY_DEFAULT + 1);
g_RehldsHookchains->HandleNetCommand()->registerHook(&Rehlds_HandleNetCommand, HC_PRIORITY_DEFAULT + 1);
g_RehldsHookchains->SV_WriteVoiceCodec()->registerHook(&SV_WriteVoiceCodec_hooked, HC_PRIORITY_DEFAULT + 1);
return true;
}
void Revoice_Main_DeInit()
{
g_RehldsHookchains->SV_DropClient()->unregisterHook(&SV_DropClient_hook);
g_RehldsHookchains->HandleNetCommand()->unregisterHook(&Rehlds_HandleNetCommand);
g_RehldsHookchains->SV_WriteVoiceCodec()->unregisterHook(&SV_WriteVoiceCodec_hooked);
Revoice_DeInit_Cvars();
}