diff --git a/extension.cpp b/extension.cpp index 9e10990..d2d8a03 100644 --- a/extension.cpp +++ b/extension.cpp @@ -320,7 +320,10 @@ CVoice::CVoice() memset(m_nosteamResampleAccum, 0, sizeof(m_nosteamResampleAccum)); memset(m_nosteamAvailableTime, 0, sizeof(m_nosteamAvailableTime)); for (int i = 0; i <= SM_MAXPLAYERS; i++) + { m_nosteamOpusEncoder[i] = NULL; + m_nosteamLastSample[i] = 0; + } } bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late) @@ -1374,6 +1377,34 @@ void CVoice::TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data) opus_encoder_ctl(m_nosteamOpusEncoder[playerSlot], OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); } + + // Get current engine time + double now = (double)gpGlobals->curtime; + double timeSinceLastVoice = now - g_fLastVoiceData[playerSlot + 1]; + + if (timeSinceLastVoice > 0.5 && g_fLastVoiceData[playerSlot + 1] != 0.0) + { + // Clear out stale states + if (m_nosteamOpusEncoder[playerSlot]) + opus_encoder_ctl(m_nosteamOpusEncoder[playerSlot], OPUS_RESET_STATE); + if (m_pCeltDecoder[playerSlot]) + celt_decoder_ctl(m_pCeltDecoder[playerSlot], CELT_RESET_STATE_REQUEST, NULL); + + // Flush the PCM ringbuffer completely + while(m_nosteamOpusPCMBuffer[playerSlot].TotalLength() > 0) + { + int16_t trash[480]; + size_t toPop = m_nosteamOpusPCMBuffer[playerSlot].TotalLength() > 480 ? 480 : m_nosteamOpusPCMBuffer[playerSlot].TotalLength(); + m_nosteamOpusPCMBuffer[playerSlot].Pop(trash, toPop); + } + m_nosteamResampleAccum[playerSlot] = 0; + } + + // Mark current arrival time + g_fLastVoiceData[playerSlot + 1] = now; + + + int16_t pcmBuf[512]; int decoded = celt_decode(m_pCeltDecoder[playerSlot], (const unsigned char *)data, nBytes, @@ -1384,6 +1415,76 @@ void CVoice::TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data) return; } + + + + + + + // 1. Prepare a temporary buffer for the resampled frame + // Max output can be roughly ceil(decoded * 24000 / 22050) + 2. Allocate plenty of room. + int16_t resampledFrameBuffer[2048]; + int totalResampledSamples = 0; + + const int STEP = 24000; + const int DIV = 22050; + + // Track the previous sample for linear interpolation (Keep this in your class or use a player state!) + // For local tracking inside the block, we can fall back to 0 if i == 0, but ideally + // track m_nosteamLastSample[playerSlot] persistently across packets. + int16_t lastSample = m_nosteamLastSample[playerSlot]; + + for (int i = 0; i < decoded; i++) + { + int16_t currentSample = pcmBuf[i]; + + m_nosteamResampleAccum[playerSlot] += STEP; + while (m_nosteamResampleAccum[playerSlot] >= DIV) + { + // Calculate how far between lastSample and currentSample this new sample lands. + // We use the remaining fractional value in the accumulator. + float fraction = (float)(DIV - (m_nosteamResampleAccum[playerSlot] - STEP)) / (float)STEP; + if (fraction < 0.0f) fraction = 0.0f; + if (fraction > 1.0f) fraction = 1.0f; + + // Linearly blend the two points to eliminate stepping edge artifacts + int16_t interpolatedSample = (int16_t)((1.0f - fraction) * lastSample + fraction * currentSample); + + // Store into temporary buffer safely + if (totalResampledSamples < 2048) + { + resampledFrameBuffer[totalResampledSamples++] = interpolatedSample; + } + + m_nosteamResampleAccum[playerSlot] -= DIV; + } + + lastSample = currentSample; + } + + // Save the last sample of this packet so the next incoming packet blends flawlessly + m_nosteamLastSample[playerSlot] = lastSample; + + // 2. Perform a single high-efficiency push into the ring buffer + if (totalResampledSamples > 0) + { + if (m_nosteamOpusPCMBuffer[playerSlot].CurrentFree() >= (size_t)totalResampledSamples) + { + m_nosteamOpusPCMBuffer[playerSlot].Push(resampledFrameBuffer, totalResampledSamples); + } + } + + + + + + + + + + + + /* // Resample 22050 -> 24000 and push into ring buffer const int STEP = 24000; const int DIV = 22050; @@ -1399,6 +1500,7 @@ void CVoice::TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data) m_nosteamOpusPCMBuffer[playerSlot].Push(&sample, 1); } } + */ } void CVoice::HandleNoSteamVoiceData() @@ -1456,7 +1558,8 @@ void CVoice::HandleNoSteamVoiceData() size_t subframeStart = FinalSize; int framesEmitted = 0; - while (m_nosteamOpusPCMBuffer[playerSlot].TotalLength() >= 480) + //while (m_nosteamOpusPCMBuffer[playerSlot].TotalLength() >= 480) + while (m_nosteamOpusPCMBuffer[playerSlot].TotalLength() >= 480 && framesEmitted < 3) { int16_t opusInput[480]; if (!m_nosteamOpusPCMBuffer[playerSlot].Pop(opusInput, 480)) diff --git a/extension.h b/extension.h index 4c1c142..3ddf537 100644 --- a/extension.h +++ b/extension.h @@ -181,6 +181,7 @@ private: CRingBuffer m_nosteamOpusPCMBuffer[SM_MAXPLAYERS + 1]; int m_nosteamResampleAccum[SM_MAXPLAYERS + 1]; OpusEncoder *m_nosteamOpusEncoder[SM_MAXPLAYERS + 1]; + int16_t m_nosteamLastSample[SM_MAXPLAYERS + 1]; // CELT encoder (shared by both paths, same output format) struct CEncoderSettings