further attempts at improving the voice chat quality for nosteamers from steam players, but still not good
This commit is contained in:
parent
2184fd917a
commit
7d46e3b9eb
291
extension.cpp
291
extension.cpp
@ -196,8 +196,6 @@ DETOUR_DECL_MEMBER1(ProcessVoiceData, bool, void *, msg)
|
||||
if (g_aFrameVoiceBytes[clientIndex] > NET_MAX_VOICE_BYTES_FRAME)
|
||||
return true;
|
||||
|
||||
g_fLastVoiceData[clientIndex] = gpGlobals->curtime;
|
||||
|
||||
// Get IClient for sending
|
||||
IClient *pClient = iserver->GetClient(playerSlot);
|
||||
if (!pClient)
|
||||
@ -233,6 +231,7 @@ DETOUR_DECL_MEMBER1(ProcessVoiceData, bool, void *, msg)
|
||||
{
|
||||
//convert steam OPUS packet to CELT for no steam clients
|
||||
g_Interface.PushPlayerVoiceData(playerSlot, nBytes, voiceDataBuffer);
|
||||
g_Interface.HandlePlayerVoiceData(playerSlot); //send celt packets to nosteamers.
|
||||
// Send steam Opus packet to Steam clients
|
||||
int maxClients = iserver->GetClientCount();
|
||||
for (int i = 0; i < maxClients; i++)
|
||||
@ -253,18 +252,12 @@ DETOUR_DECL_MEMBER1(ProcessVoiceData, bool, void *, msg)
|
||||
SendVoiceDataMsg(playerSlot, pToClient, (unsigned char *)voiceDataBuffer, nBytes, voiceMsg->m_xuid);
|
||||
}
|
||||
}
|
||||
g_fLastVoiceData[clientIndex] = gpGlobals->curtime;
|
||||
return true;
|
||||
}
|
||||
|
||||
DETOUR_DECL_STATIC4(SV_BroadcastVoiceData, void, IClient *, pClient, int, nBytes, char *, data, int64, xuid)
|
||||
{
|
||||
//if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data))
|
||||
/*
|
||||
if (1 == 2) //we dont do dis no more
|
||||
{
|
||||
DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, data, xuid);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -505,8 +498,7 @@ bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||
m_CeltEncoderSettings.FrameSize = 512;
|
||||
m_CeltEncoderSettings.PacketSize = 64;
|
||||
m_CeltEncoderSettings.Complexity = 10;
|
||||
m_CeltEncoderSettings.FrameTime = (double)m_CeltEncoderSettings.FrameSize
|
||||
/ (double)m_CeltEncoderSettings.SampleRate_Hz;
|
||||
m_CeltEncoderSettings.FrameTime = (double)m_CeltEncoderSettings.FrameSize / (double)m_CeltEncoderSettings.SampleRate_Hz;
|
||||
|
||||
int theError;
|
||||
//torchlight celt
|
||||
@ -812,8 +804,7 @@ void CVoice::SDK_OnUnload()
|
||||
void CVoice::OnGameFrame(bool simulating)
|
||||
{
|
||||
HandleNetwork();
|
||||
HandleVoiceData();
|
||||
HandlePlayerVoiceData(); //send celt packets to nosteamers.
|
||||
HandleVoiceData(); //torchlight audio emitting
|
||||
HandleNoSteamVoiceData(); //send opus packets to steamers.
|
||||
|
||||
// Reset per-client voice byte counter to 0 every frame.
|
||||
@ -822,28 +813,7 @@ void CVoice::OnGameFrame(bool simulating)
|
||||
|
||||
bool CVoice::OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data)
|
||||
{
|
||||
// Reject empty packets
|
||||
if(nBytes < 1)
|
||||
return false;
|
||||
|
||||
int client = pClient->GetPlayerSlot() + 1;
|
||||
|
||||
if (client < 1 || client > SM_MAXPLAYERS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame.
|
||||
// 5 = SVC_VoiceData header/overhead
|
||||
g_aFrameVoiceBytes[client] += 5 + nBytes;
|
||||
|
||||
if (g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
g_fLastVoiceData[client] = gpGlobals->curtime;
|
||||
|
||||
//not actually used anymore anyways
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1244,20 +1214,16 @@ void CVoice::PushPlayerVoiceData(int playerSlot, int nBytes, char *data)
|
||||
|
||||
if (!m_pCeltCodecPlayer[playerSlot])
|
||||
{
|
||||
// First time this player speaks - create encoder
|
||||
int theError;
|
||||
m_pCeltCodecPlayer[playerSlot] = celt_encoder_create_custom(
|
||||
m_pCeltModePlayer, 1, &theError);
|
||||
m_pCeltCodecPlayer[playerSlot] = celt_encoder_create_custom(m_pCeltModePlayer, 1, &theError);
|
||||
if (!m_pCeltCodecPlayer[playerSlot])
|
||||
{
|
||||
smutils->LogError(myself, "PushPlayerVoiceData: celt_encoder_create_custom failed: %d", theError);
|
||||
return;
|
||||
}
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_RESET_STATE_REQUEST, NULL);
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_BITRATE(
|
||||
m_CeltEncoderSettings.TargetBitRate_Kbps * 1000));
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_COMPLEXITY(
|
||||
m_CeltEncoderSettings.Complexity));
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_BITRATE(m_CeltEncoderSettings.TargetBitRate_Kbps * 1000));
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_COMPLEXITY(m_CeltEncoderSettings.Complexity));
|
||||
}
|
||||
|
||||
if (!m_PlayerOpusDecoder[playerSlot])
|
||||
@ -1266,32 +1232,14 @@ void CVoice::PushPlayerVoiceData(int playerSlot, int nBytes, char *data)
|
||||
m_PlayerOpusDecoder[playerSlot] = opus_decoder_create(24000, 1, &err);
|
||||
if (err < 0)
|
||||
{
|
||||
smutils->LogError(myself, "PushPlayerVoiceData: opus_decoder_create failed: %s",
|
||||
opus_strerror(err));
|
||||
smutils->LogError(myself, "PushPlayerVoiceData: opus_decoder_create failed: %s", opus_strerror(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const int OPUS_SAMPLE_RATE = 24000;
|
||||
const unsigned char *p = (const unsigned char *)data;
|
||||
|
||||
/*
|
||||
// debugging real steam clients voice packets.
|
||||
int dumpLen = nBytes < 32 ? nBytes : 32;
|
||||
char hexBuf[32 * 3 + 1];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < dumpLen; i++)
|
||||
{
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
hexBuf[pos++] = hex[(p[i] >> 4) & 0xF];
|
||||
hexBuf[pos++] = hex[p[i] & 0xF];
|
||||
hexBuf[pos++] = ' ';
|
||||
}
|
||||
hexBuf[pos] = '\0';
|
||||
smutils->LogMessage(myself, "Steam packet in PushPlayerVoiceData: nBytes=%d first %d bytes: %s", nBytes, dumpLen, hexBuf);
|
||||
*/
|
||||
|
||||
//if its not opus packets we dont process it
|
||||
// Verify raw network packets match standard Valve Opus voice signatures
|
||||
if (nBytes < 18 || p[8] != 0x0B || (p[11] != 0x05 && p[11] != 0x06))
|
||||
return;
|
||||
|
||||
@ -1320,140 +1268,161 @@ void CVoice::PushPlayerVoiceData(int playerSlot, int nBytes, char *data)
|
||||
|
||||
const unsigned char *opusFrame = p + offset;
|
||||
|
||||
int16_t pcmBuf[5760];
|
||||
// Maximum safe decoded PCM space buffer allocation (120ms frame at 24kHz = 2880 samples)
|
||||
int16_t pcmBuf[2880];
|
||||
int decoded = opus_decode(m_PlayerOpusDecoder[playerSlot],
|
||||
opusFrame, frameLen - 2,
|
||||
pcmBuf, 5760, 0);
|
||||
pcmBuf, 2880, 0);
|
||||
if (decoded <= 0)
|
||||
{
|
||||
smutils->LogError(myself, "PushPlayerVoiceData: opus_decode failed: %s",
|
||||
opus_strerror(decoded));
|
||||
smutils->LogError(myself, "PushPlayerVoiceData: opus_decode failed: %s", opus_strerror(decoded));
|
||||
return;
|
||||
}
|
||||
|
||||
const int STEP = m_CeltEncoderSettings.SampleRate_Hz;
|
||||
const int DIV = OPUS_SAMPLE_RATE;
|
||||
// PHASE-PERFECT FIXED POINT RESAMPLING (24000 Hz -> 22050 Hz)
|
||||
// Shift left into 16.16 fixed-point space to prevent rounding/truncation drift errors
|
||||
uint32_t step_fp = ((uint32_t)24000 << 16) / (uint32_t)m_CeltEncoderSettings.SampleRate_Hz;
|
||||
uint32_t curr_fp = m_playerResampleAccum[playerSlot]; // Treat tracking value storage as raw fixed-point register
|
||||
|
||||
for (int i = 0; i < decoded; i++)
|
||||
// Allocate temporary staging stack vector array to drop into ring-buffer cleanly in one pass
|
||||
int16_t resampledStaging[2880];
|
||||
int outSamplesCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
m_playerResampleAccum[playerSlot] += STEP;
|
||||
while (m_playerResampleAccum[playerSlot] >= DIV)
|
||||
{
|
||||
m_playerResampleAccum[playerSlot] -= DIV;
|
||||
int16_t sample = pcmBuf[i];
|
||||
if (m_playerVoiceBuffer[playerSlot].CurrentFree() > 0)
|
||||
m_playerVoiceBuffer[playerSlot].Push(&sample, 1);
|
||||
}
|
||||
uint32_t srcIndex = curr_fp >> 16;
|
||||
if (srcIndex >= (uint32_t)decoded)
|
||||
break;
|
||||
|
||||
// Linear Interpolation over adjacent samples to prevent digital harmonic hiss
|
||||
int16_t s1 = pcmBuf[srcIndex];
|
||||
int16_t s2 = (srcIndex + 1 < (uint32_t)decoded) ? pcmBuf[srcIndex + 1] : s1;
|
||||
|
||||
uint32_t frac = curr_fp & 0xFFFF;
|
||||
int32_t interpolatedSample = s1 + (((int32_t)(s2 - s1) * (int32_t)frac) >> 16);
|
||||
|
||||
resampledStaging[outSamplesCount++] = (int16_t)interpolatedSample;
|
||||
curr_fp += step_fp;
|
||||
}
|
||||
|
||||
// If buffer still doesn't have enough for one CELT frame, use Opus PLC to fill
|
||||
if (m_playerVoiceBuffer[playerSlot].TotalLength() < (size_t)m_CeltEncoderSettings.FrameSize)
|
||||
// Normalize state trackers back relative to zero base offset
|
||||
m_playerResampleAccum[playerSlot] = curr_fp - ((uint32_t)decoded << 16);
|
||||
|
||||
// Bulk push data down onto RingBuffer payload safely in one operation
|
||||
if (outSamplesCount > 0 && (size_t)outSamplesCount <= m_playerVoiceBuffer[playerSlot].CurrentFree())
|
||||
{
|
||||
int16_t plcBuf[5760];
|
||||
int plcDecoded = opus_decode(m_PlayerOpusDecoder[playerSlot],
|
||||
NULL, 0, // NULL = PLC mode
|
||||
plcBuf, 480, 0);
|
||||
if (plcDecoded > 0)
|
||||
{
|
||||
for (int i = 0; i < plcDecoded; i++)
|
||||
{
|
||||
m_playerResampleAccum[playerSlot] += m_CeltEncoderSettings.SampleRate_Hz;
|
||||
while (m_playerResampleAccum[playerSlot] >= 24000)
|
||||
{
|
||||
m_playerResampleAccum[playerSlot] -= 24000;
|
||||
int16_t sample = plcBuf[i];
|
||||
if (m_playerVoiceBuffer[playerSlot].CurrentFree() > 0)
|
||||
m_playerVoiceBuffer[playerSlot].Push(&sample, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_playerVoiceBuffer[playerSlot].Push(resampledStaging, outSamplesCount);
|
||||
}
|
||||
}
|
||||
//smutils->LogMessage(myself, "PushPlayerVoiceData: pushed, bufLen=%d", (int)m_playerVoiceBuffer[playerSlot].TotalLength());
|
||||
}
|
||||
|
||||
void CVoice::HandlePlayerVoiceData()
|
||||
void CVoice::HandlePlayerVoiceData(int playerSlot)
|
||||
{
|
||||
const int CELT_FRAME_SIZE = m_CeltEncoderSettings.FrameSize;
|
||||
const int CELT_PACKET_SIZE = m_CeltEncoderSettings.PacketSize;
|
||||
int maxClients = iserver->GetClientCount();
|
||||
|
||||
for (int playerSlot = 0; playerSlot < maxClients; playerSlot++)
|
||||
if (!m_pCeltCodecPlayer[playerSlot])
|
||||
return;
|
||||
|
||||
size_t currentBufferLength = m_playerVoiceBuffer[playerSlot].TotalLength();
|
||||
if (currentBufferLength < (size_t)CELT_FRAME_SIZE)
|
||||
return;
|
||||
|
||||
// Use Engine Time instead of System Time to stay perfectly in sync with server tickrates
|
||||
double now = (double)gpGlobals->curtime;
|
||||
|
||||
double timeSinceLastVoice = now - g_fLastVoiceData[playerSlot + 1];
|
||||
if (timeSinceLastVoice > 0.5) // Reduced to 500ms for responsiveness
|
||||
{
|
||||
if (!m_pCeltCodecPlayer[playerSlot])
|
||||
continue;
|
||||
m_playerAvailableTime[playerSlot + 1] = 0.0;
|
||||
|
||||
// Completely clear internal codec history matrices to ensure clean starts
|
||||
if (m_pCeltCodecPlayer[playerSlot]) {
|
||||
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_RESET_STATE_REQUEST, NULL);
|
||||
}
|
||||
if (m_PlayerOpusDecoder[playerSlot]) {
|
||||
opus_decoder_ctl(m_PlayerOpusDecoder[playerSlot], OPUS_RESET_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_playerVoiceBuffer[playerSlot].TotalLength() < (size_t)CELT_FRAME_SIZE)
|
||||
continue;
|
||||
if (m_playerAvailableTime[playerSlot + 1] == 0.0)
|
||||
{
|
||||
m_playerAvailableTime[playerSlot + 1] = now;
|
||||
return;
|
||||
}
|
||||
|
||||
float timeAvailable = (float)m_playerVoiceBuffer[playerSlot].TotalLength()
|
||||
/ (float)m_CeltEncoderSettings.SampleRate_Hz;
|
||||
double elapsed = now - m_playerAvailableTime[playerSlot + 1];
|
||||
int framesToEmit = (int)(elapsed / m_CeltEncoderSettings.FrameTime);
|
||||
|
||||
// DYNAMIC JITTER BUFFER CATCH-UP
|
||||
// If the server lag spikes and calculates a massive frame burst, check what is actually in the buffer.
|
||||
// There's no point trying to emit 37 frames if the client has only sent 4 frames of real data!
|
||||
int framesInRealBuffer = (int)(currentBufferLength / (size_t)CELT_FRAME_SIZE);
|
||||
|
||||
//warmup encoder with 5 frames
|
||||
if (m_playerAvailableTime[playerSlot] == 0.0 &&
|
||||
m_playerVoiceBuffer[playerSlot].TotalLength() >= (size_t)CELT_FRAME_SIZE * 11)
|
||||
if (framesToEmit > framesInRealBuffer)
|
||||
{
|
||||
framesToEmit = framesInRealBuffer;
|
||||
}
|
||||
//smutils->LogMessage(myself, "framesToEmit: %d", framesToEmit);
|
||||
|
||||
// Smooth-cap the maximum frames processed per server frame to avoid robotic bursts
|
||||
// 4 frames = ~92ms of audio, which is an ideal ceiling for single-frame catchups.
|
||||
if (framesToEmit > 4)
|
||||
{
|
||||
framesToEmit = 4;
|
||||
}
|
||||
|
||||
if (framesToEmit <= 0)
|
||||
return;
|
||||
|
||||
int framesProcessed = 0;
|
||||
|
||||
while (framesProcessed < framesToEmit &&
|
||||
m_playerVoiceBuffer[playerSlot].TotalLength() >= (size_t)CELT_FRAME_SIZE)
|
||||
{
|
||||
int16_t celtInput[CELT_FRAME_SIZE];
|
||||
if (!m_playerVoiceBuffer[playerSlot].Pop(celtInput, CELT_FRAME_SIZE))
|
||||
{
|
||||
for (int w = 0; w < 10; w++)
|
||||
{
|
||||
int16_t warmupInput[CELT_FRAME_SIZE];
|
||||
if (!m_playerVoiceBuffer[playerSlot].Pop(warmupInput, CELT_FRAME_SIZE))
|
||||
break;
|
||||
unsigned char discard[64];
|
||||
celt_encode(m_pCeltCodecPlayer[playerSlot], warmupInput, CELT_FRAME_SIZE, discard, 64);
|
||||
}
|
||||
m_playerAvailableTime[playerSlot] = getTime();
|
||||
break;
|
||||
}
|
||||
|
||||
//if (m_playerAvailableTime[playerSlot] < getTime() && timeAvailable < 0.2)
|
||||
if (m_playerAvailableTime[playerSlot] < getTime() && timeAvailable < 0.05)
|
||||
continue;
|
||||
|
||||
if (m_playerAvailableTime[playerSlot] > getTime() + 1.0)
|
||||
continue;
|
||||
|
||||
int framesEmitted = 0;
|
||||
while (m_playerVoiceBuffer[playerSlot].TotalLength() >= (size_t)CELT_FRAME_SIZE)
|
||||
unsigned char celtPacket[CELT_PACKET_SIZE];
|
||||
int celtBytes = celt_encode(m_pCeltCodecPlayer[playerSlot], celtInput,
|
||||
CELT_FRAME_SIZE, celtPacket, CELT_PACKET_SIZE);
|
||||
if (celtBytes > 0)
|
||||
{
|
||||
//smutils->LogMessage(myself, "framesEmitted: %d", framesEmitted);
|
||||
if (framesEmitted >= 1)
|
||||
break;
|
||||
|
||||
int16_t celtInput[CELT_FRAME_SIZE];
|
||||
if (!m_playerVoiceBuffer[playerSlot].Pop(celtInput, CELT_FRAME_SIZE))
|
||||
break;
|
||||
|
||||
unsigned char celtPacket[CELT_PACKET_SIZE];
|
||||
int celtBytes = celt_encode(m_pCeltCodecPlayer[playerSlot], celtInput,
|
||||
CELT_FRAME_SIZE, celtPacket, CELT_PACKET_SIZE);
|
||||
if (celtBytes > 0)
|
||||
for (int i = 0; i < maxClients; i++)
|
||||
{
|
||||
for (int i = 0; i < maxClients; i++)
|
||||
{
|
||||
IClient *pToClient = iserver->GetClient(i);
|
||||
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive())
|
||||
continue;
|
||||
if (!g_bIsNoSteam[i + 1])
|
||||
continue;
|
||||
if (g_bClientMuted[i + 1][playerSlot + 1])
|
||||
continue;
|
||||
SendVoiceDataMsg(playerSlot, pToClient, celtPacket, celtBytes, 0);
|
||||
}
|
||||
framesEmitted++;
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "HandlePlayerVoiceData: celt_encode failed: %d", celtBytes);
|
||||
break;
|
||||
IClient *pToClient = iserver->GetClient(i);
|
||||
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive())
|
||||
continue;
|
||||
if (!g_bIsNoSteam[i + 1])
|
||||
continue;
|
||||
if (g_bClientMuted[i + 1][playerSlot + 1])
|
||||
continue;
|
||||
SendVoiceDataMsg(playerSlot, pToClient, celtPacket, celtBytes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (framesEmitted > 0)
|
||||
else
|
||||
{
|
||||
if (m_playerAvailableTime[playerSlot] < getTime())
|
||||
m_playerAvailableTime[playerSlot] = getTime();
|
||||
m_playerAvailableTime[playerSlot] += (double)framesEmitted * m_CeltEncoderSettings.FrameTime;
|
||||
smutils->LogError(myself, "HandlePlayerVoiceData: celt_encode failed: %d", celtBytes);
|
||||
break;
|
||||
}
|
||||
|
||||
framesProcessed++;
|
||||
}
|
||||
|
||||
// Advance our tracking clock safely based on what we processed
|
||||
if (framesProcessed > 0)
|
||||
{
|
||||
m_playerAvailableTime[playerSlot + 1] += (double)framesProcessed * m_CeltEncoderSettings.FrameTime;
|
||||
}
|
||||
else if (framesToEmit > 0)
|
||||
{
|
||||
// If we wanted to emit frames but the buffer was empty, pull the clock forward
|
||||
// to 'now' so we don't build up a permanent structural timing lag.
|
||||
m_playerAvailableTime[playerSlot + 1] = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -143,6 +143,7 @@ public:
|
||||
void PushPlayerVoiceData(int playerSlot, int nBytes, char *data);
|
||||
void BroadcastVoiceDataCelt(IClient *pClient, int16_t *pPCM, int nSamples);
|
||||
void TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data);
|
||||
void HandlePlayerVoiceData(int playerSlot);
|
||||
|
||||
private:
|
||||
int m_ListenSocket;
|
||||
@ -215,7 +216,6 @@ private:
|
||||
void HandleNetwork();
|
||||
void OnDataReceived(CClient *pClient, int16_t *pData, size_t Samples);
|
||||
void HandleVoiceData();
|
||||
void HandlePlayerVoiceData();
|
||||
void HandleNoSteamVoiceData();
|
||||
};
|
||||
|
||||
|
||||
@ -65,6 +65,7 @@ void CRingBuffer::Mix(int16_t *pData, size_t Samples)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool CRingBuffer::Push(int16_t *pData, size_t Samples)
|
||||
{
|
||||
if(Samples > CurrentFree())
|
||||
@ -126,6 +127,35 @@ bool CRingBuffer::Push(int16_t *pData, size_t Samples)
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
bool CRingBuffer::Push(int16_t *pData, size_t Samples)
|
||||
{
|
||||
if(Samples > CurrentFree())
|
||||
return false; // Safely drop if buffer overflows, preventing distortion
|
||||
|
||||
if(m_WriteIndex + Samples > m_BufferSize)
|
||||
{
|
||||
size_t TowardsEnd = m_BufferSize - m_WriteIndex;
|
||||
memcpy(&m_aBuffer[m_WriteIndex], pData, TowardsEnd * sizeof(*m_aBuffer));
|
||||
m_WriteIndex = 0;
|
||||
|
||||
size_t Left = Samples - TowardsEnd;
|
||||
memcpy(m_aBuffer, &pData[TowardsEnd], Left * sizeof(*m_aBuffer));
|
||||
m_WriteIndex = Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&m_aBuffer[m_WriteIndex], pData, Samples * sizeof(*m_aBuffer));
|
||||
m_WriteIndex += Samples;
|
||||
}
|
||||
|
||||
if(m_WriteIndex == m_BufferSize)
|
||||
m_WriteIndex = 0;
|
||||
|
||||
m_Length += Samples;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CRingBuffer::TotalLength()
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user