diff --git a/dep/silk/include/SKP_Silk_typedef.h b/dep/silk/include/SKP_Silk_typedef.h index 5e1d2e7..088d5ed 100644 --- a/dep/silk/include/SKP_Silk_typedef.h +++ b/dep/silk/include/SKP_Silk_typedef.h @@ -1,27 +1,27 @@ /*********************************************************************** -Copyright (c) 2006-2012, Skype Limited. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, (subject to the limitations in the disclaimer below) +Copyright (c) 2006-2012, Skype Limited. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, (subject to the limitations in the disclaimer below) are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of Skype Limited, nor the names of specific -contributors, may be used to endorse or promote products derived from +- Neither the name of Skype Limited, nor the names of specific +contributors, may be used to endorse or promote products derived from this software without specific prior written permission. -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED -BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED +BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ @@ -73,7 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define SKP_STR_CASEINSENSITIVE_COMPARE(x, y) _stricmp(x, y) #else # define SKP_STR_CASEINSENSITIVE_COMPARE(x, y) strcasecmp(x, y) -#endif +#endif #define SKP_int64_MAX ((SKP_int64)0x7FFFFFFFFFFFFFFFLL) /* 2^63 - 1 */ #define SKP_int64_MIN ((SKP_int64)0x8000000000000000LL) /* -2^63 */ diff --git a/dep/silk/src/SKP_Silk_enc_API.c b/dep/silk/src/SKP_Silk_enc_API.c index 4876958..a29dc54 100644 --- a/dep/silk/src/SKP_Silk_enc_API.c +++ b/dep/silk/src/SKP_Silk_enc_API.c @@ -1,27 +1,27 @@ /*********************************************************************** -Copyright (c) 2006-2012, Skype Limited. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, (subject to the limitations in the disclaimer below) +Copyright (c) 2006-2012, Skype Limited. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, (subject to the limitations in the disclaimer below) are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of Skype Limited, nor the names of specific -contributors, may be used to endorse or promote products derived from +- Neither the name of Skype Limited, nor the names of specific +contributors, may be used to endorse or promote products derived from this software without specific prior written permission. -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED -BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED +BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ @@ -41,9 +41,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SKP_int SKP_Silk_SDK_Get_Encoder_Size( SKP_int32 *encSizeBytes ) { SKP_int ret = 0; - + *encSizeBytes = sizeof( SKP_Silk_encoder_state_FLP ); - + return ret; } @@ -83,7 +83,7 @@ SKP_int SKP_Silk_SDK_InitEncoder( SKP_Silk_encoder_state_FLP *psEnc; SKP_int ret = 0; - + psEnc = ( SKP_Silk_encoder_state_FLP* )encState; /* Reset Encoder */ @@ -103,7 +103,7 @@ SKP_int SKP_Silk_SDK_InitEncoder( /**************************/ /* Encode frame with Silk */ /**************************/ -SKP_int SKP_Silk_SDK_Encode( +SKP_int SKP_Silk_SDK_Encode( void *encState, /* I/O: State */ const SKP_Silk_EncodeControlStruct *encControl, /* I: Control structure */ const SKP_int16 *samplesIn, /* I: Speech sample input vector */ @@ -124,7 +124,7 @@ SKP_int SKP_Silk_SDK_Encode( if( ( ( encControl->API_sampleRate != 8000 ) && ( encControl->API_sampleRate != 12000 ) && ( encControl->API_sampleRate != 16000 ) && - ( encControl->API_sampleRate != 24000 ) && + ( encControl->API_sampleRate != 24000 ) && ( encControl->API_sampleRate != 32000 ) && ( encControl->API_sampleRate != 44100 ) && ( encControl->API_sampleRate != 48000 ) ) || @@ -161,7 +161,7 @@ SKP_int SKP_Silk_SDK_Encode( } TargetRate_bps = SKP_LIMIT( TargetRate_bps, MIN_TARGET_RATE_BPS, MAX_TARGET_RATE_BPS ); - if( ( ret = SKP_Silk_control_encoder_FLP( psEnc, PacketSize_ms, TargetRate_bps, + if( ( ret = SKP_Silk_control_encoder_FLP( psEnc, PacketSize_ms, TargetRate_bps, PacketLoss_perc, UseDTX, Complexity) ) != 0 ) { SKP_assert( 0 ); return( ret ); @@ -176,8 +176,8 @@ SKP_int SKP_Silk_SDK_Encode( #if MAX_FS_KHZ > 16 /* Detect energy above 8 kHz */ - if( SKP_min( API_fs_Hz, 1000 * max_internal_fs_kHz ) == 24000 && - psEnc->sCmn.sSWBdetect.SWB_detected == 0 && + if( SKP_min( API_fs_Hz, 1000 * max_internal_fs_kHz ) == 24000 && + psEnc->sCmn.sSWBdetect.SWB_detected == 0 && psEnc->sCmn.sSWBdetect.WB_detected == 0 ) { SKP_Silk_detect_SWB_input( &psEnc->sCmn.sSWBdetect, samplesIn, ( SKP_int )nSamplesIn ); } @@ -187,18 +187,18 @@ SKP_int SKP_Silk_SDK_Encode( MaxBytesOut = 0; /* return 0 output bytes if no encoder called */ while( 1 ) { nSamplesToBuffer = psEnc->sCmn.frame_length - psEnc->sCmn.inputBufIx; - if( API_fs_Hz == SKP_SMULBB( 1000, psEnc->sCmn.fs_kHz ) ) { + if( API_fs_Hz == SKP_SMULBB( 1000, psEnc->sCmn.fs_kHz ) ) { nSamplesToBuffer = SKP_min_int( nSamplesToBuffer, nSamplesIn ); nSamplesFromInput = nSamplesToBuffer; /* Copy to buffer */ SKP_memcpy( &psEnc->sCmn.inputBuf[ psEnc->sCmn.inputBufIx ], samplesIn, nSamplesFromInput * sizeof( SKP_int16 ) ); - } else { + } else { nSamplesToBuffer = SKP_min( nSamplesToBuffer, 10 * input_10ms * psEnc->sCmn.fs_kHz ); nSamplesFromInput = SKP_DIV32_16( nSamplesToBuffer * API_fs_Hz, psEnc->sCmn.fs_kHz * 1000 ); /* Resample and write to buffer */ - ret += SKP_Silk_resampler( &psEnc->sCmn.resampler_state, + ret += SKP_Silk_resampler( &psEnc->sCmn.resampler_state, &psEnc->sCmn.inputBuf[ psEnc->sCmn.inputBufIx ], samplesIn, nSamplesFromInput ); - } + } samplesIn += nSamplesFromInput; nSamplesIn -= nSamplesFromInput; psEnc->sCmn.inputBufIx += nSamplesToBuffer; diff --git a/dep/silk/src/SKP_Silk_structs.h b/dep/silk/src/SKP_Silk_structs.h index 4cb1db5..c7a43c0 100644 --- a/dep/silk/src/SKP_Silk_structs.h +++ b/dep/silk/src/SKP_Silk_structs.h @@ -1,27 +1,27 @@ /*********************************************************************** -Copyright (c) 2006-2012, Skype Limited. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, (subject to the limitations in the disclaimer below) +Copyright (c) 2006-2012, Skype Limited. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, (subject to the limitations in the disclaimer below) are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of Skype Limited, nor the names of specific -contributors, may be used to endorse or promote products derived from +- Neither the name of Skype Limited, nor the names of specific +contributors, may be used to endorse or promote products derived from this software without specific prior written permission. -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED -BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED +BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ @@ -57,7 +57,7 @@ typedef struct { /* Struct for Low BitRate Redundant (LBRR) information */ typedef struct { - SKP_uint8 payload[ MAX_ARITHM_BYTES ]; + SKP_uint8 payload[ MAX_ARITHM_BYTES ]; SKP_int nBytes; /* Number of bytes in payload */ SKP_int usage; /* Tells how the payload should be used as FEC */ } SKP_SILK_LBRR_struct; @@ -190,7 +190,7 @@ typedef struct { const SKP_Silk_NLSF_CB_struct *psNLSF_CB[ 2 ]; /* Pointers to voiced/unvoiced NLSF codebooks */ - /* Struct for Inband LBRR */ + /* Struct for Inband LBRR */ SKP_SILK_LBRR_struct LBRR_buffer[ MAX_LBRR_DELAY ]; SKP_int oldest_LBRR_idx; SKP_int useInBandFEC; /* Saves the API setting for query */ @@ -310,7 +310,7 @@ typedef struct { /* Parameters used to investigate if inband FEC is used */ SKP_int vadFlag; SKP_int no_FEC_counter; /* Counts number of frames wo inband FEC */ - SKP_int inband_FEC_offset; /* 0: no FEC, 1: FEC with 1 packet offset, 2: FEC w 2 packets offset */ + SKP_int inband_FEC_offset; /* 0: no FEC, 1: FEC with 1 packet offset, 2: FEC w 2 packets offset */ /* CNG state */ SKP_Silk_CNG_struct sCNG; diff --git a/dep/silk/src/SKP_Silk_structs_FLP.h b/dep/silk/src/SKP_Silk_structs_FLP.h index 35dd7ae..4b68425 100644 --- a/dep/silk/src/SKP_Silk_structs_FLP.h +++ b/dep/silk/src/SKP_Silk_structs_FLP.h @@ -1,27 +1,27 @@ /*********************************************************************** -Copyright (c) 2006-2012, Skype Limited. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, (subject to the limitations in the disclaimer below) +Copyright (c) 2006-2012, Skype Limited. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, (subject to the limitations in the disclaimer below) are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of Skype Limited, nor the names of specific -contributors, may be used to endorse or promote products derived from +- Neither the name of Skype Limited, nor the names of specific +contributors, may be used to endorse or promote products derived from this software without specific prior written permission. -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED -BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED +BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ diff --git a/revoice/dist/c2a2_ba_launch.wav b/revoice/dist/c2a2_ba_launch.wav new file mode 100644 index 0000000..b0206b9 Binary files /dev/null and b/revoice/dist/c2a2_ba_launch.wav differ diff --git a/revoice/dist/cslig_nuages.wav b/revoice/dist/cslig_nuages.wav new file mode 100644 index 0000000..56a32ce Binary files /dev/null and b/revoice/dist/cslig_nuages.wav differ diff --git a/revoice/dist/dance.wav b/revoice/dist/dance.wav new file mode 100644 index 0000000..2c63d9d Binary files /dev/null and b/revoice/dist/dance.wav differ diff --git a/revoice/dist/kolshik.wav b/revoice/dist/kolshik.wav new file mode 100644 index 0000000..c87c4ec Binary files /dev/null and b/revoice/dist/kolshik.wav differ diff --git a/revoice/dist/pchela.wav b/revoice/dist/pchela.wav new file mode 100644 index 0000000..114294d Binary files /dev/null and b/revoice/dist/pchela.wav differ diff --git a/revoice/msvc/revoice.vcxproj b/revoice/msvc/revoice.vcxproj index 01ec10a..3646ffe 100644 --- a/revoice/msvc/revoice.vcxproj +++ b/revoice/msvc/revoice.vcxproj @@ -145,7 +145,7 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - $(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) + $(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) precompiled.h diff --git a/revoice/public/IVoiceCodec.h b/revoice/public/IVoiceCodec.h index 632c45b..79721f1 100644 --- a/revoice/public/IVoiceCodec.h +++ b/revoice/public/IVoiceCodec.h @@ -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. diff --git a/revoice/public/counter.h b/revoice/public/counter.h new file mode 100644 index 0000000..0b55e02 --- /dev/null +++ b/revoice/public/counter.h @@ -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 + #include + #include +#else + #include + #include + #include + #include + #ifdef OSX + #include + #else + #include + #endif + #include +#endif + +#include +#include +#include +#include +#include + +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 +} diff --git a/revoice/src/SteamP2PCodec.cpp b/revoice/src/SteamP2PCodec.cpp index b1cc45f..9c5122a 100644 --- a/revoice/src/SteamP2PCodec.cpp +++ b/revoice/src/SteamP2PCodec.cpp @@ -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) { diff --git a/revoice/src/VoiceEncoder_Silk.cpp b/revoice/src/VoiceEncoder_Silk.cpp index 6a61820..6ec5698 100644 --- a/revoice/src/VoiceEncoder_Silk.cpp +++ b/revoice/src/VoiceEncoder_Silk.cpp @@ -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); + } + + nSamplesToUse = GetNumQueuedEncodingSamples(); + nSamplesRemaining = nSamplesToUse % nSamplesPerFrame; + } + + psRead = (const int16_t *)m_bufOverflowBytes.Base(); + Assert(!bFinal || nSamplesRemaining == 0); } - nSamplesPerFrame = inSampleRate / 50; - nSamplesRemaining = nSamplesToUse % nSamplesPerFrame; - pWritePosMax = pCompressed + maxCompressedBytes; - nSamples = nSamplesToUse - nSamplesRemaining; - pWritePos = pCompressed; + 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; } } diff --git a/revoice/src/VoiceEncoder_Silk.h b/revoice/src/VoiceEncoder_Silk.h index c3c0d35..420667b 100644 --- a/revoice/src/VoiceEncoder_Silk.h +++ b/revoice/src/VoiceEncoder_Silk.h @@ -23,7 +23,7 @@ public: virtual bool Init(int quality); virtual void Release(); - virtual bool ResetState(); + virtual bool ResetState(); virtual int Compress(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal); virtual int Decompress(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes); diff --git a/revoice/src/dllapi.cpp b/revoice/src/dllapi.cpp index 8b5e149..8b86924 100644 --- a/revoice/src/dllapi.cpp +++ b/revoice/src/dllapi.cpp @@ -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 diff --git a/revoice/src/revoice_cfg.cpp b/revoice/src/revoice_cfg.cpp index 12d8286..e84734d 100644 --- a/revoice/src/revoice_cfg.cpp +++ b/revoice/src/revoice_cfg.cpp @@ -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++; } } diff --git a/revoice/src/revoice_main.cpp b/revoice/src/revoice_main.cpp index c1530ba..a251299 100644 --- a/revoice/src/revoice_main.cpp +++ b/revoice/src/revoice_main.cpp @@ -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 [ ]\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(); diff --git a/revoice/src/revoice_main.h b/revoice/src/revoice_main.h index b920130..9d7d071 100644 --- a/revoice/src/revoice_main.h +++ b/revoice/src/revoice_main.h @@ -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); diff --git a/revoice/src/revoice_main_.cpp b/revoice/src/revoice_main_.cpp new file mode 100644 index 0000000..6f7dcde --- /dev/null +++ b/revoice/src/revoice_main_.cpp @@ -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 [ ]\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 +#include + +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(); +}