📄 voice.cpp
字号:
return;
char uchVoiceData[4096];
bool bFinal = false;
int nDataLength = Voice_GetCompressedData(uchVoiceData, sizeof(uchVoiceData), bFinal);
Voice_AddIncomingData(TWEAKMODE_CHANNELINDEX, uchVoiceData, nDataLength, 0);
}
void Voice_Idle(float frametime)
{
if( voice_enable.GetInt() == 0 )
{
Voice_Deinit();
return;
}
if(g_bLocalPlayerTalkingAck)
{
g_LocalPlayerTalkingTimeout += frametime;
if(g_LocalPlayerTalkingTimeout > LOCALPLAYERTALKING_TIMEOUT)
{
g_bLocalPlayerTalkingAck = false;
// Tell the client DLL.
g_pSoundServices->OnChangeVoiceStatus(-2, FALSE);
}
}
// Precalculate these to speedup the voice fadeout.
g_nVoiceFadeSamples = max((int)(voice_fadeouttime.GetFloat() * VOICE_OUTPUT_SAMPLE_RATE), 2);
g_VoiceFadeMul = 1.0f / (g_nVoiceFadeSamples - 1);
if(g_pVoiceRecord)
g_pVoiceRecord->Idle();
// If we're in voice tweak mode, feed our own data back to us.
Voice_UpdateVoiceTweakMode();
// Age the channels.
int nActive = 0;
for(int i=0; i < VOICE_NUM_CHANNELS; i++)
{
CVoiceChannel *pChannel = &g_VoiceChannels[i];
if(pChannel->m_iEntity != -1)
{
if(pChannel->m_bStarved)
{
// Kill the channel. It's done playing.
Voice_EndChannel(i);
}
else
{
float oldpad = pChannel->m_TimePad;
pChannel->m_TimePad -= frametime;
if(oldpad > 0 && pChannel->m_TimePad <= 0)
{
// Start its audio.
VoiceSE_StartChannel(i);
g_pSoundServices->OnChangeVoiceStatus(pChannel->m_iEntity, TRUE);
VoiceSE_InitMouth(pChannel->m_iEntity);
}
++nActive;
}
}
}
if(nActive == 0)
VoiceSE_EndOverdrive();
VoiceSE_Idle(frametime);
// voice_showchannels.
if( voice_showchannels.GetInt() >= 1 )
{
for(int i=0; i < VOICE_NUM_CHANNELS; i++)
{
CVoiceChannel *pChannel = &g_VoiceChannels[i];
if(pChannel->m_iEntity == -1)
continue;
Msg("Voice - chan %d, ent %d, bufsize: %d\n", i, pChannel->m_iEntity, pChannel->m_Buffer.GetReadAvailable());
}
}
// Show profiling data?
if( voice_profile.GetInt() )
{
Msg("Voice - compress: %7.2fu, decompress: %7.2fu, gain: %7.2fu, upsample: %7.2fu, total: %7.2fu\n",
g_CompressTime*1000000.0,
g_DecompressTime*1000000.0,
g_GainTime*1000000.0,
g_UpsampleTime*1000000.0,
(g_CompressTime+g_DecompressTime+g_GainTime+g_UpsampleTime)*1000000.0
);
g_CompressTime = g_DecompressTime = g_GainTime = g_UpsampleTime = 0;
}
}
bool Voice_IsRecording()
{
return g_bVoiceRecording && !g_bInTweakMode;
}
bool Voice_RecordStart(
const char *pUncompressedFile,
const char *pDecompressedFile,
const char *pMicInputFile)
{
if(!g_pEncodeCodec)
return false;
Voice_RecordStop();
g_pEncodeCodec->ResetState();
if(pMicInputFile)
{
int a, b, c;
ReadWaveFile(pMicInputFile, g_pMicInputFileData, g_nMicInputFileBytes, a, b, c);
g_CurMicInputFileByte = 0;
g_MicStartTime = g_pSoundServices->GetRealTime();
}
if(pUncompressedFile)
{
g_pUncompressedFileData = new char[MAX_WAVEFILEDATA_LEN];
g_nUncompressedDataBytes = 0;
g_pUncompressedDataFilename = pUncompressedFile;
}
if(pDecompressedFile)
{
g_pDecompressedFileData = new char[MAX_WAVEFILEDATA_LEN];
g_nDecompressedDataBytes = 0;
g_pDecompressedDataFilename = pDecompressedFile;
}
g_bVoiceRecording = false;
if(g_pVoiceRecord)
{
g_bVoiceRecording = g_pVoiceRecord->RecordStart();
if(g_bVoiceRecording)
g_pSoundServices->OnChangeVoiceStatus(-1, TRUE); // Tell the client DLL.
}
return g_bVoiceRecording;
}
bool Voice_RecordStop()
{
// Write the files out for debugging.
if(g_pMicInputFileData)
{
delete [] g_pMicInputFileData;
g_pMicInputFileData = NULL;
}
if(g_pUncompressedFileData)
{
WriteWaveFile(g_pUncompressedDataFilename, g_pUncompressedFileData, g_nUncompressedDataBytes, g_VoiceSampleFormat.wBitsPerSample, g_VoiceSampleFormat.nChannels, g_VoiceSampleFormat.nSamplesPerSec);
delete [] g_pUncompressedFileData;
g_pUncompressedFileData = NULL;
}
if(g_pDecompressedFileData)
{
WriteWaveFile(g_pDecompressedDataFilename, g_pDecompressedFileData, g_nDecompressedDataBytes, g_VoiceSampleFormat.wBitsPerSample, g_VoiceSampleFormat.nChannels, g_VoiceSampleFormat.nSamplesPerSec);
delete [] g_pDecompressedFileData;
g_pDecompressedFileData = NULL;
}
if(g_pVoiceRecord)
g_pVoiceRecord->RecordStop();
if(g_bVoiceRecording)
g_pSoundServices->OnChangeVoiceStatus(-1, FALSE); // Tell the client DLL.
g_bVoiceRecording = false;
return(true);
}
int Voice_GetCompressedData(char *pchDest, int nCount, bool bFinal)
{
IVoiceCodec *pCodec = g_pEncodeCodec;
if(g_pVoiceRecord && pCodec)
{
short tempData[8192];
int gotten = g_pVoiceRecord->GetRecordedData(tempData, min(nCount/BYTES_PER_SAMPLE, (int)sizeof(tempData)/BYTES_PER_SAMPLE));
// If they want to get the data from a file instead of the mic, use that.
if(g_pMicInputFileData)
{
double curtime = g_pSoundServices->GetRealTime();
int nShouldGet = (curtime - g_MicStartTime) * g_VoiceSampleFormat.nSamplesPerSec;
gotten = min(sizeof(tempData)/BYTES_PER_SAMPLE, min(nShouldGet, (g_nMicInputFileBytes - g_CurMicInputFileByte) / BYTES_PER_SAMPLE));
memcpy(tempData, &g_pMicInputFileData[g_CurMicInputFileByte], gotten*BYTES_PER_SAMPLE);
g_CurMicInputFileByte += gotten * BYTES_PER_SAMPLE;
g_MicStartTime = curtime;
}
int nCompressedBytes = pCodec->Compress((char*)tempData, gotten, pchDest, nCount, !!bFinal);
// Write to our file buffers..
if(g_pUncompressedFileData)
{
int nToWrite = min(gotten*BYTES_PER_SAMPLE, MAX_WAVEFILEDATA_LEN - g_nUncompressedDataBytes);
memcpy(&g_pUncompressedFileData[g_nUncompressedDataBytes], tempData, nToWrite);
g_nUncompressedDataBytes += nToWrite;
}
return nCompressedBytes;
}
else
{
return 0;
}
}
//------------------ Copyright (c) 1999 Valve, LLC. ----------------------------
// Purpose: Assigns a channel to an entity by searching for either a channel
// already assigned to that entity or picking the least recently used
// channel. If the LRU channel is picked, it is flushed and all other
// channels are aged.
// Input : nEntity - entity number to assign to a channel.
// Output : A channel index to which the entity has been assigned.
//------------------------------------------------------------------------------
int Voice_AssignChannel(int nEntity)
{
if(g_bInTweakMode)
return VOICE_CHANNEL_IN_TWEAK_MODE;
// See if a channel already exists for this entity and if so, just return it.
int iFree = -1;
for(int i=0; i < VOICE_NUM_CHANNELS; i++)
{
CVoiceChannel *pChannel = &g_VoiceChannels[i];
if(pChannel->m_iEntity == nEntity)
{
return i;
}
else if(pChannel->m_iEntity == -1 && pChannel->m_pVoiceCodec)
{
pChannel->m_pVoiceCodec->ResetState();
iFree = i;
break;
}
}
// If they're all used, then don't allow them to make a new channel.
if(iFree == -1)
{
return VOICE_CHANNEL_ERROR;
}
CVoiceChannel *pChannel = &g_VoiceChannels[iFree];
pChannel->Init(nEntity);
VoiceSE_StartOverdrive();
return iFree;
}
//------------------ Copyright (c) 1999 Valve, LLC. ----------------------------
// Purpose: Determines which channel has been assigened to a given entity.
// Input : nEntity - entity number.
// Output : The index of the channel assigned to the entity, VOICE_CHANNEL_ERROR
// if no channel is currently assigned to the given entity.
//------------------------------------------------------------------------------
int Voice_GetChannel(int nEntity)
{
for(int i=0; i < VOICE_NUM_CHANNELS; i++)
if(g_VoiceChannels[i].m_iEntity == nEntity)
return i;
return VOICE_CHANNEL_ERROR;
}
double UpsampleIntoBuffer(
const short *pSrc,
int nSrcSamples,
CCircularBuffer *pBuffer,
double startFraction,
double rate)
{
double maxFraction = nSrcSamples - 1;
while(1)
{
if(startFraction >= maxFraction)
break;
int iSample = (int)startFraction;
double frac = startFraction - floor(startFraction);
double val1 = pSrc[iSample];
double val2 = pSrc[iSample+1];
short newSample = (short)(val1 + (val2 - val1) * frac);
pBuffer->Write(&newSample, sizeof(newSample));
startFraction += rate;
}
return startFraction - floor(startFraction);
}
//------------------ Copyright (c) 1999 Valve, LLC. ----------------------------
// Purpose: Adds received voice data to
// Input :
// Output :
//------------------------------------------------------------------------------
int Voice_AddIncomingData(int nChannel, const char *pchData, int nCount, int iSequenceNumber)
{
CVoiceChannel *pChannel;
// If in tweak mode, we call this during Idle with -1 as the channel, so any channel data from the network
// gets rejected.
if(g_bInTweakMode)
{
if(nChannel == TWEAKMODE_CHANNELINDEX)
nChannel = 0;
else
return 0;
}
if(!(pChannel = GetVoiceChannel(nChannel)) || !pChannel->m_pVoiceCodec)
{
return(0);
}
pChannel->m_bStarved = false; // This only really matters if you call Voice_AddIncomingData between the time the mixer
// asks for data and Voice_Idle is called.
// Decompress.
char decompressed[8192];
int nDecompressed = pChannel->m_pVoiceCodec->Decompress(pchData, nCount, decompressed, sizeof(decompressed));
pChannel->m_AutoGain.ProcessSamples((short*)decompressed, nDecompressed);
// Upsample into the dest buffer. We could do this in a mixer but it complicates the mixer.
pChannel->m_LastFraction = UpsampleIntoBuffer(
(short*)decompressed,
nDecompressed,
&pChannel->m_Buffer,
pChannel->m_LastFraction,
(double)g_VoiceSampleFormat.nSamplesPerSec/VOICE_OUTPUT_SAMPLE_RATE);
pChannel->m_LastSample = decompressed[nDecompressed];
// Write to our file buffer..
if(g_pDecompressedFileData)
{
int nToWrite = min(nDecompressed*2, MAX_WAVEFILEDATA_LEN - g_nDecompressedDataBytes);
memcpy(&g_pDecompressedFileData[g_nDecompressedDataBytes], decompressed, nToWrite);
g_nDecompressedDataBytes += nToWrite;
}
if( voice_showincoming.GetInt() != 0 )
{
Msg("Voice - %d incoming samples added to channel %d\n", nDecompressed, nChannel);
}
return(nChannel);
}
#if DEAD
//------------------ Copyright (c) 1999 Valve, LLC. ----------------------------
// Purpose: Flushes a given receive channel.
// Input : nChannel - index of channel to flush.
//------------------------------------------------------------------------------
void Voice_FlushChannel(int nChannel)
{
if ((nChannel < 0) || (nChannel >= VOICE_NUM_CHANNELS))
{
assert(false);
return;
}
g_VoiceChannels[nChannel].m_Buffer.Flush();
}
#endif
//------------------------------------------------------------------------------
// IVoiceTweak implementation.
//------------------------------------------------------------------------------
int VoiceTweak_StartVoiceTweakMode()
{
// If we're already in voice tweak mode, return an error.
if(g_bInTweakMode)
{
assert(!"VoiceTweak_StartVoiceTweakMode called while already in tweak mode.");
return 0;
}
if( !g_pMixerControls )
return 0;
Voice_EndAllChannels();
Voice_RecordStart(NULL, NULL, NULL);
Voice_AssignChannel(TWEAKMODE_ENTITYINDEX);
g_bInTweakMode = true;
g_pMixerControls = GetMixerControls();
return 1;
}
void VoiceTweak_EndVoiceTweakMode()
{
if(!g_bInTweakMode)
{
assert(!"VoiceTweak_EndVoiceTweakMode called when not in tweak mode.");
return;
}
g_bInTweakMode = false;
Voice_RecordStop();
}
void VoiceTweak_SetControlFloat(VoiceTweakControl iControl, float value)
{
if(!g_pMixerControls)
return;
if(iControl == MicrophoneVolume)
{
g_pMixerControls->SetValue_Float(IMixerControls::Control::MicVolume, value);
}
else if(iControl == OtherSpeakerScale)
{
voice_scale.SetValue( value );
}
}
float VoiceTweak_GetControlFloat(VoiceTweakControl iControl)
{
if(!g_pMixerControls)
return 0;
if(iControl == MicrophoneVolume)
{
float value = 1;
g_pMixerControls->GetValue_Float(IMixerControls::Control::MicVolume, value);
return value;
}
else if(iControl == OtherSpeakerScale)
{
return voice_scale.GetFloat();
}
else
{
return 1;
}
}
IVoiceTweak g_VoiceTweakAPI =
{
VoiceTweak_StartVoiceTweakMode,
VoiceTweak_EndVoiceTweakMode,
VoiceTweak_SetControlFloat,
VoiceTweak_GetControlFloat
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -