⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 voice.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 2 页
字号:
		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 + -