diff options
Diffstat (limited to 'utils/hlfaceposer/sound.cpp')
| -rw-r--r-- | utils/hlfaceposer/sound.cpp | 1800 |
1 files changed, 1800 insertions, 0 deletions
diff --git a/utils/hlfaceposer/sound.cpp b/utils/hlfaceposer/sound.cpp new file mode 100644 index 0000000..3d9f6e5 --- /dev/null +++ b/utils/hlfaceposer/sound.cpp @@ -0,0 +1,1800 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#pragma warning( disable : 4201 ) +#include <mmsystem.h> +#include <stdio.h> +#include <math.h> +#include "snd_audio_source.h" +#include "AudioWaveOutput.h" +#include "ifaceposersound.h" +#include "StudioModel.h" +#include "hlfaceposer.h" +#include "expressions.h" +#include "expclass.h" +#include "PhonemeConverter.h" +#include "utlvector.h" +#include "filesystem.h" +#include "sentence.h" +#include "faceposer_models.h" +#include "iclosecaptionmanager.h" +#include "phonemeeditor.h" +#include "wavebrowser.h" +#include "choreoscene.h" +#include "choreoview.h" +#include "KeyValues.h" + +extern ISoundEmitterSystemBase *soundemitter; + +typedef struct channel_s +{ + int leftvol; + int rightvol; + int rleftvol; + int rrightvol; + float pitch; +} channel_t; + +#define INPUT_BUFFER_COUNT 32 + +class CAudioWaveInput : public CAudioInput +{ +public: + CAudioWaveInput( void ); + ~CAudioWaveInput( void ); + + // Returns the current count of available samples + int SampleCount( void ); + + // returns the size of each sample in bytes + int SampleSize( void ) { return m_sampleSize; } + + // returns the sampling rate of the data + int SampleRate( void ) { return m_sampleRate; } + + // returns a pointer to the actual data + void *SampleData( void ); + + // release the available data (mark as done) + void SampleRelease( void ); + + // returns the mono/stereo status of this device (true if stereo) + bool IsStereo( void ) { return m_isStereo; } + + // begin sampling + void Start( void ); + + // stop sampling + void Stop( void ); + + void WaveMessage( HWAVEIN hdevice, UINT uMsg, DWORD dwParam1, DWORD dwParam2 ); + +private: + void OpenDevice( void ); + bool ValidDevice( void ) { return m_deviceId != 0xFFFFFFFF; } + void ClearDevice( void ) { m_deviceId = 0xFFFFFFFF; } + + // returns true if the new format is better + bool BetterFormat( DWORD dwNewFormat, DWORD dwOldFormat ); + + void InitReadyList( void ); + void AddToReadyList( WAVEHDR *pBuffer ); + void PopReadyList( void ); + + WAVEHDR *m_pReadyList; + + int m_sampleSize; + int m_sampleRate; + bool m_isStereo; + + UINT m_deviceId; + HWAVEIN m_deviceHandle; + + WAVEHDR *m_buffers[ INPUT_BUFFER_COUNT ]; +}; + +extern "C" void CALLBACK WaveData( HWAVEIN hwi, UINT uMsg, CAudioWaveInput *pAudio, DWORD dwParam1, DWORD dwParam2 ); + +CAudioWaveInput::CAudioWaveInput( void ) +{ + memset( m_buffers, 0, sizeof( m_buffers ) ); + int deviceCount = (int)waveInGetNumDevs(); + UINT deviceId = 0; + DWORD deviceFormat = 0; + + int i; + for ( i = 0; i < deviceCount; i++ ) + { + WAVEINCAPS waveCaps; + MMRESULT errorCode = waveInGetDevCaps( (UINT)i, &waveCaps, sizeof(waveCaps) ); + if ( errorCode == MMSYSERR_NOERROR ) + { + // valid device + if ( BetterFormat( waveCaps.dwFormats, deviceFormat ) ) + { + deviceId = i; + deviceFormat = waveCaps.dwFormats; + } + } + } + + if ( !deviceFormat ) + { + m_deviceId = 0xFFFFFFFF; + m_sampleSize = 0; + m_sampleRate = 0; + m_isStereo = false; + } + else + { + m_deviceId = deviceId; + m_sampleRate = 44100; + m_isStereo = false; + if ( deviceFormat & WAVE_FORMAT_4M16 ) + { + m_sampleSize = 2; + } + else if ( deviceFormat & WAVE_FORMAT_4M08 ) + { + m_sampleSize = 1; + } + else + { + // ERROR! + } + + OpenDevice(); + } + + InitReadyList(); +} + +CAudioWaveInput::~CAudioWaveInput( void ) +{ + if ( ValidDevice() ) + { + Stop(); + waveInReset( m_deviceHandle ); + waveInClose( m_deviceHandle ); + for ( int i = 0; i < INPUT_BUFFER_COUNT; i++ ) + { + if ( m_buffers[i] ) + { + waveInUnprepareHeader( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) ); + delete[] m_buffers[i]->lpData; + delete m_buffers[i]; + } + m_buffers[i] = NULL; + } + ClearDevice(); + } +} + +void CALLBACK WaveData( HWAVEIN hwi, UINT uMsg, CAudioWaveInput *pAudio, DWORD dwParam1, DWORD dwParam2 ) +{ + if ( pAudio ) + { + pAudio->WaveMessage( hwi, uMsg, dwParam1, dwParam2 ); + } +} + +void CAudioWaveInput::WaveMessage( HWAVEIN hdevice, UINT uMsg, DWORD dwParam1, DWORD dwParam2 ) +{ + if ( hdevice != m_deviceHandle ) + return; + switch( uMsg ) + { + case WIM_DATA: + WAVEHDR *pHeader = (WAVEHDR *)dwParam1; + AddToReadyList( pHeader ); + break; + } +} + +void CAudioWaveInput::OpenDevice( void ) +{ + if ( !ValidDevice() ) + return; + + WAVEFORMATEX format; + + memset( &format, 0, sizeof(format) ); + format.nAvgBytesPerSec = m_sampleRate * m_sampleSize; + format.nChannels = 1; + format.wBitsPerSample = m_sampleSize * 8; + format.nSamplesPerSec = m_sampleRate; + format.wFormatTag = WAVE_FORMAT_PCM; + format.nBlockAlign = m_sampleSize; + + MMRESULT errorCode = waveInOpen( &m_deviceHandle, m_deviceId, &format, (DWORD)WaveData, (DWORD)this, CALLBACK_FUNCTION ); + if ( errorCode == MMSYSERR_NOERROR ) + { + // valid device opened + int bufferSize = m_sampleSize * m_sampleRate / INPUT_BUFFER_COUNT; // total of one second of data + + // allocate buffers + for ( int i = 0; i < INPUT_BUFFER_COUNT; i++ ) + { + m_buffers[i] = new WAVEHDR; + m_buffers[i]->lpData = new char[ bufferSize ]; + m_buffers[i]->dwBufferLength = bufferSize; + m_buffers[i]->dwUser = 0; + m_buffers[i]->dwFlags = 0; + + waveInPrepareHeader( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) ); + waveInAddBuffer( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) ); + } + } + else + { + ClearDevice(); + } +} + +void CAudioWaveInput::Start( void ) +{ + if ( !ValidDevice() ) + return; + + waveInStart( m_deviceHandle ); +} + +void CAudioWaveInput::Stop( void ) +{ + if ( !ValidDevice() ) + return; + + waveInStop( m_deviceHandle ); +} + +void CAudioWaveInput::InitReadyList( void ) +{ + m_pReadyList = NULL; +} + +void CAudioWaveInput::AddToReadyList( WAVEHDR *pBuffer ) +{ + WAVEHDR **pList = &m_pReadyList; + + waveInUnprepareHeader( m_deviceHandle, pBuffer, sizeof(*pBuffer) ); + // insert at the tail of the list + while ( *pList ) + { + pList = reinterpret_cast<WAVEHDR **>(&((*pList)->dwUser)); + } + pBuffer->dwUser = NULL; + *pList = pBuffer; +} + + +void CAudioWaveInput::PopReadyList( void ) +{ + if ( m_pReadyList ) + { + WAVEHDR *pBuffer = m_pReadyList; + m_pReadyList = reinterpret_cast<WAVEHDR *>(m_pReadyList->dwUser); + waveInPrepareHeader( m_deviceHandle, pBuffer, sizeof(*pBuffer) ); + waveInAddBuffer( m_deviceHandle, pBuffer, sizeof(*pBuffer) ); + } +} + + + +#define WAVE_FORMAT_STEREO (WAVE_FORMAT_1S08|WAVE_FORMAT_1S16|WAVE_FORMAT_2S08|WAVE_FORMAT_2S16|WAVE_FORMAT_4S08|WAVE_FORMAT_4S16) +#define WAVE_FORMATS_UNDERSTOOD (0xFFF) +#define WAVE_FORMAT_11K (WAVE_FORMAT_1M08|WAVE_FORMAT_1M16) +#define WAVE_FORMAT_22K (WAVE_FORMAT_2M08|WAVE_FORMAT_2M16) +#define WAVE_FORMAT_44K (WAVE_FORMAT_4M08|WAVE_FORMAT_4M16) + +static int HighestBit( DWORD dwFlags ) +{ + int i = 31; + while ( i ) + { + if ( dwFlags & (1<<i) ) + return i; + i--; + } + + return 0; +} + +bool CAudioWaveInput::BetterFormat( DWORD dwNewFormat, DWORD dwOldFormat ) +{ + dwNewFormat &= WAVE_FORMATS_UNDERSTOOD & (~WAVE_FORMAT_STEREO); + dwOldFormat &= WAVE_FORMATS_UNDERSTOOD & (~WAVE_FORMAT_STEREO); + + // our target format is 44.1KHz, mono, 16-bit + if ( HighestBit(dwOldFormat) >= HighestBit(dwNewFormat) ) + return false; + + return true; +} + + +int CAudioWaveInput::SampleCount( void ) +{ + if ( !ValidDevice() ) + return 0; + + if ( m_pReadyList ) + { + switch( SampleSize() ) + { + case 2: + return m_pReadyList->dwBytesRecorded >> 1; + case 1: + return m_pReadyList->dwBytesRecorded; + default: + break; + } + } + return 0; +} + +void *CAudioWaveInput::SampleData( void ) +{ + if ( !ValidDevice() ) + return NULL; + + if ( m_pReadyList ) + { + return m_pReadyList->lpData; + } + + return NULL; +} + + +// release the available data (mark as done) +void CAudioWaveInput::SampleRelease( void ) +{ + PopReadyList(); +} + + +// factory to create a suitable audio input for this system +CAudioInput *CAudioInput::Create( void ) +{ + // sound source is a singleton for now + static CAudioInput *pSource = NULL; + + if ( !pSource ) + { + pSource = new CAudioWaveInput; + } + + return pSource; +} + +void CAudioDeviceSWMix::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex]; + m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex]; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac); + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceSWMix::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex]; + m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex+1]; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac)<<1; + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceSWMix::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8; + m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex])>>8; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac); + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceSWMix::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8; + m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex+1])>>8; + + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac)<<1; + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +int CAudioDeviceSWMix::MaxSampleCount( void ) +{ + return PAINTBUFFER_SIZE; +} + +void CAudioDeviceSWMix::MixBegin( void ) +{ + memset( m_paintbuffer, 0, sizeof(m_paintbuffer) ); +} + +void CAudioDeviceSWMix::TransferBufferStereo16( short *pOutput, int sampleCount ) +{ + for ( int i = 0; i < sampleCount; i++ ) + { + if ( m_paintbuffer[i].left > 32767 ) + m_paintbuffer[i].left = 32767; + else if ( m_paintbuffer[i].left < -32768 ) + m_paintbuffer[i].left = -32768; + + if ( m_paintbuffer[i].right > 32767 ) + m_paintbuffer[i].right = 32767; + else if ( m_paintbuffer[i].right < -32768 ) + m_paintbuffer[i].right = -32768; + + *pOutput++ = (short)m_paintbuffer[i].left; + *pOutput++ = (short)m_paintbuffer[i].right; + } +} + +CAudioWaveOutput::CAudioWaveOutput( void ) +{ + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + CAudioBuffer *buffer = &m_buffers[ i ]; + Assert( buffer ); + buffer->hdr = NULL; + buffer->submitted = false; + buffer->submit_sample_count = false; + } + + ClearDevice(); + OpenDevice(); + + m_mixTime = -1; + m_sampleIndex = 0; + memset( m_sourceList, 0, sizeof(m_sourceList) ); + + m_nEstimatedSamplesAhead = (int)( ( float ) OUTPUT_SAMPLE_RATE / 10.0f ); +} + +void CAudioWaveOutput::RemoveMixerChannelReferences( CAudioMixer *mixer ) +{ + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + RemoveFromReferencedList( mixer, &m_buffers[ i ] ); + } +} + +void CAudioWaveOutput::AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + // Already in list + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + { + return; + } + } + + // Just remove it + int idx = buffer->m_Referenced.AddToTail(); + + CAudioMixerState *state = &buffer->m_Referenced[ idx ]; + state->mixer = mixer; + state->submit_mixer_sample = mixer->GetSamplePosition(); + +} + +void CAudioWaveOutput::RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + { + buffer->m_Referenced.Remove( i ); + break; + } + } +} + +bool CAudioWaveOutput::IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + { + return true; + } + } + return false; +} + +bool CAudioWaveOutput::IsSourceReferencedByActiveBuffer( CAudioMixer *mixer ) +{ + if ( !ValidDevice() ) + return false; + + CAudioBuffer *buffer; + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + buffer = &m_buffers[ i ]; + if ( !buffer->submitted ) + continue; + + if ( buffer->hdr->dwFlags & WHDR_DONE ) + 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 ) +{ + 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; + 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 + { + if ( g_pPhonemeEditor->IsActiveTool() || g_pWaveBrowser->IsActiveTool() ) + { + model = models->GetActiveStudioModel(); + + } + } + + if ( model && !model->m_mouth.IsSourceReferenced( pSource->GetSource() ) ) + { + CChoreoScene *pScene = g_pChoreoView->GetScene(); + bool bIgnorePhonemes = pScene ? pScene->ShouldIgnorePhonemes() : false; + model->m_mouth.AddSource( pSource->GetSource(), bIgnorePhonemes ); + if ( modelindex < 0 ) + { + pSource->SetModelIndex( models->GetIndexForStudioModel( model ) ); + } + } + + int currentsample = pSource->GetSamplePosition(); + bool forward = pSource->GetDirection(); + + if ( pSource->GetActive() ) + { + if ( !pSource->MixDataToDevice( &m_audioDevice, pSource->GetChannel(), 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( pSource->GetChannel(), 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, float volume, const char *wavfile, CAudioMixer **ppMixer ); + void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ); + void PlayPartialSound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample ); + + 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(); + + // Load SoundOverrides for Faceposer + + KeyValues *manifest = new KeyValues( "scripts/game_sounds_manifest.txt" ); + if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDEMITTER, "scripts/game_sounds_manifest.txt", "GAME" ) ) + { + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( !Q_stricmp( sub->GetName(), "faceposer_file" ) ) + { + soundemitter->AddSoundOverrides( sub->GetString() ); + continue; + } + } + } + manifest->deleteThis(); +} + +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, float volume, const char *wavfile, CAudioMixer **ppMixer ) +{ + if ( m_pAudio ) + { + CAudioSource *wave = FindOrAddSound( wavfile ); + if ( !wave ) + return; + + CAudioMixer *pMixer = wave->CreateMixer(); + if ( ppMixer ) + { + *ppMixer = pMixer; + } + pMixer->SetVolume( volume ); + m_pAudio->AddSource( pMixer ); + if ( model ) + { + pMixer->SetModelIndex( models->GetIndexForStudioModel( model ) ); + } + } +} + +void CFacePoserSound::PlayPartialSound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample ) +{ + if ( !m_pAudio ) + return; + + StopAll(); + CAudioSource *wave = FindOrAddSound( wavfile ); + if ( !wave ) + return; + + CAudioMixer *mixer = wave->CreateMixer(); + if ( ppMixer ) + { + *ppMixer = mixer; + } + + mixer->SetSamplePosition( startSample ); + mixer->SetLoopPosition( endSample ); + mixer->SetVolume( volume ); + m_pAudio->AddSource( mixer ); +} + +void CFacePoserSound::PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ) +{ + if ( ppMixer ) + { + *ppMixer = NULL; + } + + if ( m_pAudio ) + { + CAudioMixer *mixer = source->CreateMixer(); + if ( ppMixer ) + { + *ppMixer = mixer; + } + mixer->SetVolume( volume ); + m_pAudio->AddSource( mixer ); + } +} + +enum +{ + PHONEME_CLASS_WEAK = 0, + PHONEME_CLASS_NORMAL, + PHONEME_CLASS_STRONG, + + NUM_PHONEME_CLASSES +}; + +struct Emphasized_Phoneme +{ + char *classname; + bool required; + bool valid; + CExpClass *cl; + CExpression *exp; + float *settings; + float amount; +}; + +static Emphasized_Phoneme g_PhonemeClasses[ NUM_PHONEME_CLASSES ] = +{ + { "phonemes_weak", false }, + { "phonemes", true }, + { "phonemes_strong", false }, +}; + +#define STRONG_CROSSFADE_START 0.60f +#define WEAK_CROSSFADE_START 0.40f + +void ComputeBlendedSetting( Emphasized_Phoneme *classes, float emphasis_intensity ) +{ + // Here's the formula + // 0.5 is neutral 100 % of the default setting + + // Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END + // If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START + // so we don't get huge numbers + + bool has_weak = classes[ PHONEME_CLASS_WEAK ].valid; + bool has_strong = classes[ PHONEME_CLASS_STRONG ].valid; + + Assert( classes[ PHONEME_CLASS_NORMAL ].valid ); + + if ( emphasis_intensity > STRONG_CROSSFADE_START ) + { + if ( has_strong ) + { + // Blend in some of strong + float dist_remaining = 1.0f - emphasis_intensity; + float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START ); + + classes[ PHONEME_CLASS_NORMAL ].amount = (frac) * 2.0f * STRONG_CROSSFADE_START; + classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac; + } + else + { + emphasis_intensity = min( emphasis_intensity, STRONG_CROSSFADE_START ); + classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity; + } + } + else if ( emphasis_intensity < WEAK_CROSSFADE_START ) + { + if ( has_weak ) + { + // Blend in some weak + float dist_remaining = WEAK_CROSSFADE_START - emphasis_intensity; + float frac = dist_remaining / ( WEAK_CROSSFADE_START ); + + classes[ PHONEME_CLASS_NORMAL ].amount = (1.0f - frac) * 2.0f * WEAK_CROSSFADE_START; + classes[ PHONEME_CLASS_WEAK ].amount = frac; + } + else + { + emphasis_intensity = max( emphasis_intensity, WEAK_CROSSFADE_START ); + classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity; + } + } + else + { + classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity; + } +} + +void CFacePoserSound::AddViseme( float intensity, StudioModel *model, int phoneme, float scale ) +{ + int i; + + Assert( model ); + CStudioHdr *hdr = model->GetStudioHdr(); + Assert( hdr ); + if ( !hdr ) + return; + + for ( i = 0; i < NUM_PHONEME_CLASSES; i++ ) + { + Emphasized_Phoneme *info = &g_PhonemeClasses[ i ]; + + info->valid = false; + info->exp = NULL; + info->settings = NULL; + info->amount = 0.0f; + + info->cl = expressions->FindClass( info->classname, true ); + if ( info->cl ) + { + info->exp = info->cl->FindExpression( ConvertPhoneme( phoneme ) ); + } + + if ( info->required && ( !info->cl || !info->exp ) ) + { + return; + } + + if ( info->exp ) + { + info->valid = true; + + info->settings = info->exp->GetSettings(); + Assert( info->settings ); + } + } + + ComputeBlendedSetting( g_PhonemeClasses, intensity ); + + // Look up the phoneme + for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++) + { + int j = hdr->pFlexcontroller( i )->localToGlobal; + + float add = 0.0f; + + for ( int k = 0 ; k < NUM_PHONEME_CLASSES; k++ ) + { + Emphasized_Phoneme *info = &g_PhonemeClasses[ k ]; + if ( !info->valid || !info->amount ) + continue; + + add += info->amount * info->settings[ j ]; + } + + if ( add == 0.0f ) + continue; + + float curvalue = model->GetFlexController( i ); + curvalue += add * scale; + model->SetFlexController( i, curvalue ); + } +} + + +#define PHONEME_FILTER 0.08f +#define PHONEME_DELAY 0.0f + +void CFacePoserSound::SetupWeights( void ) +{ + StudioModel *model; + int c = models->Count(); + for ( int i = 0; i < c; i++ ) + { + model = models->GetStudioModel( i ); + if ( !model ) + continue; + + // Reset flexes + CStudioHdr *hdr = model->GetStudioHdr(); + if ( !hdr ) + continue; + + for ( int s = 0; s < model->m_mouth.GetNumVoiceSources(); s++ ) + { + CVoiceData *vd = model->m_mouth.GetVoiceSource( s ); + if ( !vd || vd->ShouldIgnorePhonemes() ) + continue; + + CAudioSource *source = vd->GetSource(); + // check for phoneme flexes + if ( !source ) + continue; + + CAudioMixer *mixer = FindMixer( source ); + if ( !mixer ) + continue; + + CSentence *sentence = source->GetSentence(); + if ( !sentence ) + continue; + + // Zero faces if needed + models->CheckResetFlexes(); + + float pos = (float)mixer->GetScrubPosition(); + + // Con_Printf( "pos %f for mixer %p\n", pos, mixer ); + + float soundtime = pos / source->SampleRate(); + + float t = soundtime - PHONEME_DELAY; + float dt = PHONEME_FILTER; + + float sentence_duration = source->GetRunningLength(); + float emphasis_intensity = sentence->GetIntensity( t, sentence_duration ); + + if ( t > 0.0f ) + { + for ( int w = 0 ; w < sentence->m_Words.Size(); w++ ) + { + CWordTag *word = sentence->m_Words[ w ]; + if ( !word ) + continue; + + for ( int k = 0; k < word->m_Phonemes.Size(); k++) + { + CPhonemeTag *phoneme = word->m_Phonemes[ k ]; + if ( !phoneme ) + continue; + + // if the filter starts within this phoneme, make sure the filter size is + // at least least as long as the current phoneme, or until the end of the next phoneme, + // whichever is smaller + if (t > phoneme->GetStartTime() && t < phoneme->GetEndTime()) + { + CPhonemeTag *next = NULL; + // try next phoneme, or first phoneme of next word + if (k < word->m_Phonemes.Size()-1) + { + next = word->m_Phonemes[ k+1 ]; + } + else if ( w < sentence->m_Words.Size() - 1 && sentence->m_Words[ w+1 ]->m_Phonemes.Size() ) + { + next = sentence->m_Words[ w+1 ]->m_Phonemes[ 0 ]; + } + + // if I have a neighbor + if (next) + { + // and they're touching + if (next->GetStartTime() == phoneme->GetEndTime()) + { + // no gap, so increase the blend length to the end of the next phoneme, as long as it's not longer than the current phoneme + dt = max( dt, min( next->GetEndTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) ); + } + else + { + // dead space, so increase the blend length to the start of the next phoneme, as long as it's not longer than the current phoneme + dt = max( dt, min( next->GetStartTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) ); + } + } + else + { + // last phoneme in list, increase the blend length to the length of the current phoneme + dt = max( dt, phoneme->GetEndTime() - phoneme->GetStartTime() ); + } + } + + float t1 = ( phoneme->GetStartTime() - t) / dt; + float t2 = ( phoneme->GetEndTime() - t) / dt; + + if (t1 < 1.0 && t2 > 0) + { + float scale; + + // clamp + if (t2 > 1) + t2 = 1; + if (t1 < 0) + t1 = 0; + + // FIXME: simple box filter. Should use something fancier + scale = (t2 - t1); + + AddViseme( emphasis_intensity, model, phoneme->GetPhonemeCode(), scale ); + } + } + } + ProcessCloseCaptionData( model, t, sentence ); + } + } + } +} + +static int g_nSoundFrameCount = 0; + +void CFacePoserSound::ProcessCloseCaptionData( StudioModel *model, float curtime, CSentence* sentence ) +{ +// closecaptionmanager->Process( g_nSoundFrameCount, model, curtime, sentence, GetCloseCaptionLanguageId() ); +} + +void CFacePoserSound::Update( float dt ) +{ +// closecaptionmanager->PreProcess( g_nSoundFrameCount ); + + if ( m_pAudio ) + { + SetupWeights(); + m_pAudio->Update( m_flElapsedTime ); + } + +// closecaptionmanager->PostProcess( g_nSoundFrameCount, dt ); + + m_flElapsedTime += dt; + g_nSoundFrameCount++; +} + +void CFacePoserSound::Flush( void ) +{ + if ( m_pAudio ) + { + m_pAudio->Flush(); + } +} + +void CFacePoserSound::StopAll( void ) +{ + int c = models->Count(); + for ( int i = 0; i < c; i++ ) + { + StudioModel *model = models->GetStudioModel( i ); + if ( model ) + { + model->m_mouth.ClearVoiceSources(); + } + } + + if ( m_pAudio ) + { + m_pAudio->StopSounds(); + } +} + +void CFacePoserSound::StopSound( CAudioMixer *mixer ) +{ + int idx = m_pAudio->FindSourceIndex( mixer ); + if ( idx != -1 ) + { + m_pAudio->FreeChannel( idx ); + } +} + +void CFacePoserSound::RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr, + float starttime, float endtime, CAudioSource *pWave, + bool selected /*= false*/, int selectionstart /*= 0*/, int selectionend /*= 0*/ ) +{ + channel_t channel; + + channel.leftvol = 127; + channel.rightvol = 127; + channel.pitch = 1.0; + + if ( !pWave ) + return; + + CAudioWaveOutput *pWaveOutput = ( CAudioWaveOutput * )m_pAudio; + + CAudioMixer *pMixer = pWave->CreateMixer(); + + float timeperpixel = ( endtime - starttime ) / (float)( outrect.right - outrect.left ); + + float samplesperpixel = timeperpixel * pWave->SampleRate(); + + samplesperpixel = min( samplesperpixel, (float)PAINTBUFFER_SIZE ); + + int intsamplesperpixel = (int)samplesperpixel; + + // Determine start/stop positions + int totalsamples = (int)( pWave->GetRunningLength() * pWave->SampleRate() ); + + if ( totalsamples <= 0 ) + return; + + float selectionstarttime = pWave->GetRunningLength() * ( float )selectionstart / ( float )totalsamples; + float selectionendtime = pWave->GetRunningLength() * ( float )selectionend / ( float )totalsamples; + + + HPEN oldPen, pen, pen2, pen3, pen4; + + pen = CreatePen( PS_SOLID, 1, RGB( 175, 175, 250 ) ); + pen2 = CreatePen( PS_SOLID, 1, clr ); + pen3 = CreatePen( PS_SOLID, 1, RGB( 127, 200, 249 ) ); + pen4 = CreatePen( PS_SOLID, 2, RGB( 0, 0, 200 ) ); + + oldPen = (HPEN)SelectObject( dc, pen ); + + MoveToEx( dc, outrect.left, ( outrect.bottom + outrect.top ) / 2, NULL ); + LineTo( dc, outrect.right, ( outrect.bottom + outrect.top ) / 2 ); + + SelectObject( dc, pen2 ); + + // Now iterate the samples + float currenttime = 0.0f; + int pixel = 0; + int height = ( outrect.bottom - outrect.top ) / 2; + int midy = ( outrect.bottom + outrect.top ) / 2; + int bufferlen = ( intsamplesperpixel + 3 ) & ~3; + short *samples = new short[ 2 * bufferlen ]; + bool drawingselection = false; + int maxsamples = max( 32, intsamplesperpixel / 16 ); + int currentsample = 0; + + while ( currenttime < endtime ) + { + + pWaveOutput->m_audioDevice.MixBegin(); + + int samplecount = min( maxsamples, intsamplesperpixel ); + + if ( !pMixer->MixDataToDevice( &pWaveOutput->m_audioDevice, &channel, currentsample, samplecount, pWave->SampleRate(), true ) ) + break; + + currentsample = pMixer->GetSamplePosition(); + + // Jump ahead by diff + int diff = intsamplesperpixel - samplecount; + if ( diff > 0 ) + { + if ( !pMixer->SkipSamples( &channel, currentsample, diff, pWave->SampleRate(), true ) ) + break; + } + + currentsample = pMixer->GetSamplePosition(); + + pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, samplecount ); + + if ( currenttime >= starttime ) + { + if ( selected ) + { + bool boundary = false; + bool inselection = ( currenttime >= selectionstarttime && + currenttime <= selectionendtime ); + + if ( inselection ) + { + if ( !drawingselection ) + { + drawingselection = true; + boundary = true; + } + } + else if ( drawingselection ) + { + boundary = true; + drawingselection = false; + } + + if ( inselection || boundary ) + { + int top, bottom; + + bottom = outrect.bottom; + + HPEN *usePen; + if ( boundary ) + { + usePen = &pen4; + top = outrect.top; + } + else + { + usePen = &pen3; + top = outrect.bottom - 19; + } + + HPEN old = (HPEN)SelectObject( dc, *usePen ); + + MoveToEx( dc, outrect.left + pixel, top, NULL ); + LineTo( dc, outrect.left + pixel, bottom-1 ); + + SelectObject( dc, old ); + } + } + + + int maxvalue = -65536; + int minvalue = 65536; + + short *pData = samples; + + // only take fix samples + int step = 2; + int count = 2 * samplecount; + + for ( int i = 0; i < count; i+=step ) + { + int val = (int)( pData[i] + pData[i+1] ) / 2; + + if ( val > maxvalue ) + { + maxvalue = val; + } + + if ( val < minvalue ) + { + minvalue = val; + } + } + + float maxv = (float)( maxvalue ) / 32768.0f; + float minv = (float)( minvalue ) / 32768.0f; + + MoveToEx( dc, outrect.left + pixel, midy + ( int ) ( maxv * height ), NULL ); + LineTo( dc, outrect.left + pixel, midy + ( int ) ( minv * height ) ); + + pixel++; + } + currenttime += timeperpixel; + } + + delete[] samples; + + SelectObject( dc, oldPen ); + DeleteObject( pen ); + DeleteObject( pen2 ); + DeleteObject( pen3 ); + + delete pMixer; +} + +bool CFacePoserSound::IsSoundPlaying( CAudioMixer *pMixer ) +{ + if ( !m_pAudio || !pMixer ) + { + return false; + } + + // + int index = m_pAudio->FindSourceIndex( pMixer ); + if ( index != -1 ) + return true; + + return false; +} + +CAudioMixer *CFacePoserSound::FindMixer( CAudioSource *source ) +{ + if ( !m_pAudio ) + return NULL; + + return m_pAudio->GetMixerForSource( source ); +} + + +void CFacePoserSound::EnsureNoModelReferences( CAudioSource *source ) +{ + int c = models->Count(); + for ( int i = 0; i < c; i++ ) + { + StudioModel *model = models->GetStudioModel( i ); + if ( model->m_mouth.IsSourceReferenced( source ) ) + { + model->m_mouth.RemoveSource( source ); + } + } +}
\ No newline at end of file |