📄 sound.cpp
字号:
continue;
// See if it's referenced
if ( IsSoundInReferencedList( mixer, buffer ) )
return true;
}
return false;
}
CAudioWaveOutput::~CAudioWaveOutput( void )
{
if ( ValidDevice() )
{
waveOutReset( m_deviceHandle );
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
{
if ( m_buffers[i].hdr )
{
waveOutUnprepareHeader( m_deviceHandle, m_buffers[i].hdr, sizeof(*m_buffers[i].hdr) );
delete[] m_buffers[i].hdr->lpData;
delete m_buffers[i].hdr;
}
m_buffers[i].hdr = NULL;
m_buffers[i].submitted = false;
m_buffers[i].submit_sample_count = 0;
m_buffers[i].m_Referenced.Purge();
}
waveOutClose( m_deviceHandle );
ClearDevice();
}
}
CAudioBuffer *CAudioWaveOutput::GetEmptyBuffer( void )
{
CAudioBuffer *pOutput = NULL;
if ( ValidDevice() )
{
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
{
if ( !(m_buffers[ i ].submitted ) ||
m_buffers[i].hdr->dwFlags & WHDR_DONE )
{
pOutput = &m_buffers[i];
pOutput->submitted = true;
pOutput->m_Referenced.Purge();
break;
}
}
}
return pOutput;
}
void CAudioWaveOutput::SilenceBuffer( short *pSamples, int sampleCount )
{
int i;
for ( i = 0; i < sampleCount; i++ )
{
// left
*pSamples++ = 0;
// right
*pSamples++ = 0;
}
}
void CAudioWaveOutput::Flush( void )
{
waveOutReset( m_deviceHandle );
}
// mix a buffer up to time (time is absolute)
void CAudioWaveOutput::Update( float time )
{
channel_t channel;
channel.leftvol = 200;
channel.rightvol = 200;
channel.pitch = 1.0;
if ( !ValidDevice() )
return;
// reset the system
if ( m_mixTime < 0 || time < m_baseTime )
{
m_baseTime = time;
m_mixTime = 0;
}
// put time in our coordinate frame
time -= m_baseTime;
if ( time > m_mixTime )
{
CAudioBuffer *pBuffer = GetEmptyBuffer();
// no free buffers, mixing is ahead of the playback!
if ( !pBuffer || !pBuffer->hdr )
{
//Con_Printf( "out of buffers\n" );
return;
}
// UNDONE: These numbers are constants
// calc number of samples (2 channels * 2 bytes per sample)
int sampleCount = pBuffer->hdr->dwBufferLength >> 2;
float oldTime = m_mixTime;
m_mixTime += sampleCount * (1.0f / OUTPUT_SAMPLE_RATE);
short *pSamples = reinterpret_cast<short *>(pBuffer->hdr->lpData);
SilenceBuffer( pSamples, sampleCount );
int tempCount = sampleCount;
while ( tempCount > 0 )
{
if ( tempCount > m_audioDevice.MaxSampleCount() )
sampleCount = m_audioDevice.MaxSampleCount();
else
sampleCount = tempCount;
m_audioDevice.MixBegin();
for ( int i = 0; i < MAX_CHANNELS; i++ )
{
CAudioMixer *pSource = m_sourceList[i];
if ( !pSource )
continue;
StudioModel *model = NULL;
int modelindex = pSource->GetModelIndex();
if ( modelindex >= 0 )
{
model = models->GetStudioModel( modelindex );
}
else
{
model = models->GetActiveStudioModel();
}
if ( model && !model->m_mouth.IsSourceReferenced( pSource->GetSource() ) )
{
model->m_mouth.AddSource( pSource->GetSource(), 0.0f );
}
int currentsample = pSource->GetSamplePosition();
bool forward = pSource->GetDirection();
if ( pSource->GetActive() )
{
if ( !pSource->MixDataToDevice( &m_audioDevice, &channel, currentsample, sampleCount, SampleRate(), forward ) )
{
// Source becomes inactive when last submitted sample is finally
// submitted. But it lingers until it's no longer referenced
pSource->SetActive( false );
}
else
{
AddToReferencedList( pSource, pBuffer );
}
}
else
{
if ( !IsSourceReferencedByActiveBuffer( pSource ) )
{
if ( !pSource->GetAutoDelete() )
{
FreeChannel( i );
}
}
else
{
pSource->IncrementSamples( &channel, currentsample, sampleCount, SampleRate(), forward );
}
}
}
m_audioDevice.TransferBufferStereo16( pSamples, sampleCount );
m_sampleIndex += sampleCount;
tempCount -= sampleCount;
pSamples += sampleCount * 2;
}
// if the buffers aren't aligned on sample boundaries, this will hard-lock the machine!
pBuffer->submit_sample_count = GetOutputPosition();
waveOutWrite( m_deviceHandle, pBuffer->hdr, sizeof(*(pBuffer->hdr)) );
}
}
int CAudioWaveOutput::GetNumberofSamplesAhead( void )
{
ComputeSampleAheadAmount();
return m_nEstimatedSamplesAhead;
}
float CAudioWaveOutput::GetAmountofTimeAhead( void )
{
ComputeSampleAheadAmount();
return ( (float)m_nEstimatedSamplesAhead / (float)OUTPUT_SAMPLE_RATE );
}
// Find the most recent submitted sample that isn't flagged as whdr_done
void CAudioWaveOutput::ComputeSampleAheadAmount( void )
{
m_nEstimatedSamplesAhead = 0;
int newest_sample_index = -1;
int newest_sample_count = 0;
CAudioBuffer *buffer;
if ( ValidDevice() )
{
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
{
buffer = &m_buffers[ i ];
if ( !buffer->submitted )
continue;
if ( buffer->hdr->dwFlags & WHDR_DONE )
continue;
if ( buffer->submit_sample_count > newest_sample_count )
{
newest_sample_index = i;
newest_sample_count = buffer->submit_sample_count;
}
}
}
if ( newest_sample_index == -1 )
return;
buffer = &m_buffers[ newest_sample_index ];
int currentPos = GetOutputPosition() ;
m_nEstimatedSamplesAhead = currentPos - buffer->submit_sample_count;
}
int CAudioWaveOutput::FindSourceIndex( CAudioMixer *pSource )
{
for ( int i = 0; i < MAX_CHANNELS; i++ )
{
if ( pSource == m_sourceList[i] )
{
return i;
}
}
return -1;
}
CAudioMixer *CAudioWaveOutput::GetMixerForSource( CAudioSource *source )
{
for ( int i = 0; i < MAX_CHANNELS; i++ )
{
if ( !m_sourceList[i] )
continue;
if ( source == m_sourceList[i]->GetSource() )
{
return m_sourceList[i];
}
}
return NULL;
}
void CAudioWaveOutput::AddSource( CAudioMixer *pSource )
{
int slot = 0;
for ( int i = 0; i < MAX_CHANNELS; i++ )
{
if ( !m_sourceList[i] )
{
slot = i;
break;
}
}
if ( m_sourceList[slot] )
{
FreeChannel( slot );
}
SetChannel( slot, pSource );
pSource->SetActive( true );
}
void CAudioWaveOutput::StopSounds( void )
{
for ( int i = 0; i < MAX_CHANNELS; i++ )
{
if ( m_sourceList[i] )
{
FreeChannel( i );
}
}
}
void CAudioWaveOutput::SetChannel( int channelIndex, CAudioMixer *pSource )
{
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
return;
m_sourceList[channelIndex] = pSource;
}
void CAudioWaveOutput::FreeChannel( int channelIndex )
{
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
return;
if ( m_sourceList[channelIndex] )
{
StudioModel *model = NULL;
int modelindex = m_sourceList[channelIndex]->GetModelIndex();
if ( modelindex >= 0)
{
model = models->GetStudioModel( modelindex );
}
if ( model )
{
model->m_mouth.RemoveSource( m_sourceList[channelIndex]->GetSource() );
}
RemoveMixerChannelReferences( m_sourceList[channelIndex] );
delete m_sourceList[channelIndex];
m_sourceList[channelIndex] = NULL;
}
}
int CAudioWaveOutput::GetOutputPosition( void )
{
if ( !m_deviceHandle )
return 0;
MMTIME mmtime;
mmtime.wType = TIME_SAMPLES;
waveOutGetPosition( m_deviceHandle, &mmtime, sizeof( MMTIME ) );
// Convert time to sample count
return ( mmtime.u.sample );
}
void CAudioWaveOutput::OpenDevice( void )
{
WAVEFORMATEX waveFormat;
memset( &waveFormat, 0, sizeof(waveFormat) );
// Select a PCM, 16-bit stereo playback device
waveFormat.cbSize = sizeof(waveFormat);
waveFormat.nAvgBytesPerSec = OUTPUT_SAMPLE_RATE * 2 * 2;
waveFormat.nBlockAlign = 2 * 2; // channels * sample size
waveFormat.nChannels = 2; // stereo
waveFormat.nSamplesPerSec = OUTPUT_SAMPLE_RATE;
waveFormat.wBitsPerSample = 16;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
MMRESULT errorCode = waveOutOpen( &m_deviceHandle, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL );
if ( errorCode == MMSYSERR_NOERROR )
{
int bufferSize = 4 * ( OUTPUT_SAMPLE_RATE / OUTPUT_BUFFER_COUNT ); // total of 1 second of data
// Got one!
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
{
m_buffers[i].hdr = new WAVEHDR;
m_buffers[i].hdr->lpData = new char[ bufferSize ];
long align = (long)m_buffers[i].hdr->lpData;
if ( align & 3 )
{
m_buffers[i].hdr->lpData = (char *) ( (align+3) &~3 );
}
m_buffers[i].hdr->dwBufferLength = bufferSize - (align&3);
m_buffers[i].hdr->dwFlags = 0;
if ( waveOutPrepareHeader( m_deviceHandle, m_buffers[i].hdr, sizeof(*m_buffers[i].hdr) ) != MMSYSERR_NOERROR )
{
ClearDevice();
return;
}
}
}
else
{
ClearDevice();
}
}
// factory to create a suitable audio output for this system
CAudioOutput *CAudioOutput::Create( void )
{
// sound device is a singleton for now
static CAudioOutput *pWaveOut = NULL;
if ( !pWaveOut )
{
pWaveOut = new CAudioWaveOutput;
}
return pWaveOut;
}
struct CSoundFile
{
char filename[ 512 ];
CAudioSource *source;
long filetime;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CFacePoserSound : public IFacePoserSound
{
public:
~CFacePoserSound( void );
void Init( void );
void Shutdown( void );
void Update( float dt );
void Flush( void );
CAudioSource *LoadSound( const char *wavfile );
void PlaySound( StudioModel *source, const char *wavfile, CAudioMixer **ppMixer );
void PlayPartialSound( StudioModel *model, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample );
void PlaySound( CAudioSource *source, CAudioMixer **ppMixer );
bool IsSoundPlaying( CAudioMixer *pMixer );
CAudioMixer *FindMixer( CAudioSource *source );
void StopAll( void );
void StopSound( CAudioMixer *mixer );
void RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr, float starttime, float endtime,
CAudioSource *pWave, bool selected = false, int selectionstart = 0, int selectionend = 0 );
// void InstallPhonemecallback( IPhonemeTag *pTagInterface );
float GetAmountofTimeAhead( void );
int GetNumberofSamplesAhead( void );
CAudioOuput *GetAudioOutput( void );
virtual void EnsureNoModelReferences( CAudioSource *source );
private:
void AddViseme( float intensity, StudioModel *model, int phoneme, float scale );
void ProcessCloseCaptionData( StudioModel *model, float curtime, CSentence* sentence );
void SetupWeights( void );
CAudioSource *FindOrAddSound( const char *filename );
CAudioOutput *m_pAudio;
float m_flElapsedTime;
CUtlVector < CSoundFile > m_ActiveSounds;
};
static CFacePoserSound g_FacePoserSound;
IFacePoserSound *sound = ( IFacePoserSound * )&g_FacePoserSound;
CFacePoserSound::~CFacePoserSound( void )
{
OutputDebugString( va( "Removing %i sounds\n", m_ActiveSounds.Size() ) );
for ( int i = 0 ; i < m_ActiveSounds.Size(); i++ )
{
CSoundFile *p = &m_ActiveSounds[ i ];
OutputDebugString( va( "Removing sound: %s\n", p->filename ) );
delete p->source;
}
m_ActiveSounds.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAudioOuput *CFacePoserSound::GetAudioOutput( void )
{
return (CAudioOuput *)m_pAudio;
}
CAudioSource *CFacePoserSound::FindOrAddSound( const char *filename )
{
CSoundFile *s;
int i;
for ( i = 0; i < m_ActiveSounds.Size(); i++ )
{
s = &m_ActiveSounds[ i ];
Assert( s );
if ( !stricmp( s->filename, filename ) )
{
long filetime = filesystem->GetFileTime( filename );
if ( filetime != s->filetime )
{
Con_Printf( "Reloading sound %s\n", filename );
delete s->source;
s->source = LoadSound( filename );
s->filetime = filetime;
}
return s->source;
}
}
i = m_ActiveSounds.AddToTail();
s = &m_ActiveSounds[ i ];
strcpy( s->filename, filename );
s->source = LoadSound( filename );
s->filetime = filesystem->GetFileTime( filename );
return s->source;
}
void CFacePoserSound::Init( void )
{
m_flElapsedTime = 0.0f;
m_pAudio = CAudioOutput::Create();
}
void CFacePoserSound::Shutdown( void )
{
}
float CFacePoserSound::GetAmountofTimeAhead( void )
{
if ( !m_pAudio )
return 0.0f;
return m_pAudio->GetAmountofTimeAhead();
}
int CFacePoserSound::GetNumberofSamplesAhead( void )
{
if ( !m_pAudio )
return 0;
return m_pAudio->GetNumberofSamplesAhead();
}
CAudioSource *CFacePoserSound::LoadSound( const char *wavfile )
{
if ( !m_pAudio )
return NULL;
CAudioSource *wave = AudioSource_Create( wavfile );
return wave;
}
void CFacePoserSound::PlaySound( StudioModel *model, const char *wavfile, CAudioMixer **ppMixer )
{
if ( m_pAudio )
{
CAudioSource *wave = FindOrAddSound( wavfile );
if ( !wave )
return;
CAudioMixer *pMixer = wave->CreateMixer();
if ( ppMixer )
{
*ppMixer = pMixer;
}
m_pAudio->AddSource( pMixer );
if ( model )
{
pMixer->SetModelIndex( models->GetIndexForStudioModel( model ) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -