XA2_SoundVoice.cpp (14930B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../../idlib/precompiled.h" 30 #include "../snd_local.h" 31 32 idCVar s_skipHardwareSets( "s_skipHardwareSets", "0", CVAR_BOOL, "Do all calculation, but skip XA2 calls" ); 33 idCVar s_debugHardware( "s_debugHardware", "0", CVAR_BOOL, "Print a message any time a hardware voice changes" ); 34 35 // The whole system runs at this sample rate 36 static int SYSTEM_SAMPLE_RATE = 44100; 37 static float ONE_OVER_SYSTEM_SAMPLE_RATE = 1.0f / SYSTEM_SAMPLE_RATE; 38 39 /* 40 ======================== 41 idStreamingVoiceContext 42 ======================== 43 */ 44 class idStreamingVoiceContext : public IXAudio2VoiceCallback { 45 public: 46 STDMETHOD_(void, OnVoiceProcessingPassStart)( UINT32 BytesRequired ) {} 47 STDMETHOD_(void, OnVoiceProcessingPassEnd)() {} 48 STDMETHOD_(void, OnStreamEnd)() {} 49 STDMETHOD_(void, OnBufferStart)( void * pContext ) { 50 idSoundSystemLocal::bufferContext_t * bufferContext = (idSoundSystemLocal::bufferContext_t *) pContext; 51 bufferContext->voice->OnBufferStart( bufferContext->sample, bufferContext->bufferNumber ); 52 } 53 STDMETHOD_(void, OnLoopEnd)( void * ) {} 54 STDMETHOD_(void, OnVoiceError)( void *, HRESULT hr ) { idLib::Warning( "OnVoiceError( %d )", hr ); } 55 STDMETHOD_(void, OnBufferEnd)( void* pContext ) { 56 idSoundSystemLocal::bufferContext_t * bufferContext = (idSoundSystemLocal::bufferContext_t *) pContext; 57 soundSystemLocal.ReleaseStreamBufferContext( bufferContext ); 58 } 59 } streamContext; 60 61 /* 62 ======================== 63 idSoundVoice_XAudio2::idSoundVoice_XAudio2 64 ======================== 65 */ 66 idSoundVoice_XAudio2::idSoundVoice_XAudio2() 67 : pSourceVoice( NULL ), 68 leadinSample( NULL ), 69 loopingSample( NULL ), 70 formatTag( 0 ), 71 numChannels( 0 ), 72 sampleRate( 0 ), 73 paused( true ), 74 hasVUMeter( false ) { 75 76 } 77 78 /* 79 ======================== 80 idSoundVoice_XAudio2::~idSoundVoice_XAudio2 81 ======================== 82 */ 83 idSoundVoice_XAudio2::~idSoundVoice_XAudio2() { 84 DestroyInternal(); 85 } 86 87 /* 88 ======================== 89 idSoundVoice_XAudio2::CompatibleFormat 90 ======================== 91 */ 92 bool idSoundVoice_XAudio2::CompatibleFormat( idSoundSample_XAudio2 * s ) { 93 if ( pSourceVoice == NULL ) { 94 // If this voice has never been allocated, then it's compatible with everything 95 return true; 96 } 97 return false; 98 } 99 100 /* 101 ======================== 102 idSoundVoice_XAudio2::Create 103 ======================== 104 */ 105 void idSoundVoice_XAudio2::Create( const idSoundSample * leadinSample_, const idSoundSample * loopingSample_ ) { 106 if ( IsPlaying() ) { 107 // This should never hit 108 Stop(); 109 return; 110 } 111 leadinSample = (idSoundSample_XAudio2 *)leadinSample_; 112 loopingSample = (idSoundSample_XAudio2 *)loopingSample_; 113 114 if ( pSourceVoice != NULL && CompatibleFormat( leadinSample ) ) { 115 sampleRate = leadinSample->format.basic.samplesPerSec; 116 } else { 117 DestroyInternal(); 118 formatTag = leadinSample->format.basic.formatTag; 119 numChannels = leadinSample->format.basic.numChannels; 120 sampleRate = leadinSample->format.basic.samplesPerSec; 121 122 soundSystemLocal.hardware.pXAudio2->CreateSourceVoice( &pSourceVoice, (const WAVEFORMATEX *)&leadinSample->format, XAUDIO2_VOICE_USEFILTER, 4.0f, &streamContext ); 123 if ( pSourceVoice == NULL ) { 124 // If this hits, then we are most likely passing an invalid sample format, which should have been caught by the loader (and the sample defaulted) 125 return; 126 } 127 if ( s_debugHardware.GetBool() ) { 128 if ( loopingSample == NULL || loopingSample == leadinSample ) { 129 idLib::Printf( "%dms: %p created for %s\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>" ); 130 } else { 131 idLib::Printf( "%dms: %p created for %s and %s\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>", loopingSample ? loopingSample->GetName() : "<null>" ); 132 } 133 } 134 } 135 sourceVoiceRate = sampleRate; 136 pSourceVoice->SetSourceSampleRate( sampleRate ); 137 pSourceVoice->SetVolume( 0.0f ); 138 } 139 140 /* 141 ======================== 142 idSoundVoice_XAudio2::DestroyInternal 143 ======================== 144 */ 145 void idSoundVoice_XAudio2::DestroyInternal() { 146 if ( pSourceVoice != NULL ) { 147 if ( s_debugHardware.GetBool() ) { 148 idLib::Printf( "%dms: %p destroyed\n", Sys_Milliseconds(), pSourceVoice ); 149 } 150 pSourceVoice->DestroyVoice(); 151 pSourceVoice = NULL; 152 hasVUMeter = false; 153 } 154 } 155 156 /* 157 ======================== 158 idSoundVoice_XAudio2::Start 159 ======================== 160 */ 161 void idSoundVoice_XAudio2::Start( int offsetMS, int ssFlags ) { 162 163 if ( s_debugHardware.GetBool() ) { 164 idLib::Printf( "%dms: %p starting %s @ %dms\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>", offsetMS ); 165 } 166 167 if ( !leadinSample ) { 168 return; 169 } 170 if ( !pSourceVoice ) { 171 return; 172 } 173 174 if ( leadinSample->IsDefault() ) { 175 idLib::Warning( "Starting defaulted sound sample %s", leadinSample->GetName() ); 176 } 177 178 bool flicker = ( ssFlags & SSF_NO_FLICKER ) == 0; 179 180 if ( flicker != hasVUMeter ) { 181 hasVUMeter = flicker; 182 183 if ( flicker ) { 184 IUnknown * vuMeter = NULL; 185 if ( XAudio2CreateVolumeMeter( &vuMeter, 0 ) == S_OK ) { 186 187 XAUDIO2_EFFECT_DESCRIPTOR descriptor; 188 descriptor.InitialState = true; 189 descriptor.OutputChannels = leadinSample->NumChannels(); 190 descriptor.pEffect = vuMeter; 191 192 XAUDIO2_EFFECT_CHAIN chain; 193 chain.EffectCount = 1; 194 chain.pEffectDescriptors = &descriptor; 195 196 pSourceVoice->SetEffectChain( &chain ); 197 198 vuMeter->Release(); 199 } 200 } else { 201 pSourceVoice->SetEffectChain( NULL ); 202 } 203 } 204 205 assert( offsetMS >= 0 ); 206 int offsetSamples = MsecToSamples( offsetMS, leadinSample->SampleRate() ); 207 if ( loopingSample == NULL && offsetSamples >= leadinSample->playLength ) { 208 return; 209 } 210 211 RestartAt( offsetSamples ); 212 Update(); 213 UnPause(); 214 } 215 216 /* 217 ======================== 218 idSoundVoice_XAudio2::RestartAt 219 ======================== 220 */ 221 int idSoundVoice_XAudio2::RestartAt( int offsetSamples ) { 222 offsetSamples &= ~127; 223 224 idSoundSample_XAudio2 * sample = leadinSample; 225 if ( offsetSamples >= leadinSample->playLength ) { 226 if ( loopingSample != NULL ) { 227 offsetSamples %= loopingSample->playLength; 228 sample = loopingSample; 229 } else { 230 return 0; 231 } 232 } 233 234 int previousNumSamples = 0; 235 for ( int i = 0; i < sample->buffers.Num(); i++ ) { 236 if ( sample->buffers[i].numSamples > sample->playBegin + offsetSamples ) { 237 return SubmitBuffer( sample, i, sample->playBegin + offsetSamples - previousNumSamples ); 238 } 239 previousNumSamples = sample->buffers[i].numSamples; 240 } 241 242 return 0; 243 } 244 245 /* 246 ======================== 247 idSoundVoice_XAudio2::SubmitBuffer 248 ======================== 249 */ 250 int idSoundVoice_XAudio2::SubmitBuffer( idSoundSample_XAudio2 * sample, int bufferNumber, int offset ) { 251 252 if ( sample == NULL || ( bufferNumber < 0 ) || ( bufferNumber >= sample->buffers.Num() ) ) { 253 return 0; 254 } 255 idSoundSystemLocal::bufferContext_t * bufferContext = soundSystemLocal.ObtainStreamBufferContext(); 256 if ( bufferContext == NULL ) { 257 idLib::Warning( "No free buffer contexts!" ); 258 return 0; 259 } 260 261 bufferContext->voice = this; 262 bufferContext->sample = sample; 263 bufferContext->bufferNumber = bufferNumber; 264 265 XAUDIO2_BUFFER buffer = { 0 }; 266 if ( offset > 0 ) { 267 int previousNumSamples = 0; 268 if ( bufferNumber > 0 ) { 269 previousNumSamples = sample->buffers[bufferNumber-1].numSamples; 270 } 271 buffer.PlayBegin = offset; 272 buffer.PlayLength = sample->buffers[bufferNumber].numSamples - previousNumSamples - offset; 273 } 274 buffer.AudioBytes = sample->buffers[bufferNumber].bufferSize; 275 buffer.pAudioData = (BYTE *)sample->buffers[bufferNumber].buffer; 276 buffer.pContext = bufferContext; 277 if ( ( loopingSample == NULL ) && ( bufferNumber == sample->buffers.Num() - 1 ) ) { 278 buffer.Flags = XAUDIO2_END_OF_STREAM; 279 } 280 pSourceVoice->SubmitSourceBuffer( &buffer ); 281 282 return buffer.AudioBytes; 283 } 284 285 /* 286 ======================== 287 idSoundVoice_XAudio2::Update 288 ======================== 289 */ 290 bool idSoundVoice_XAudio2::Update() { 291 if ( pSourceVoice == NULL || leadinSample == NULL ) { 292 return false; 293 } 294 295 XAUDIO2_VOICE_STATE state; 296 pSourceVoice->GetState( &state ); 297 298 const int srcChannels = leadinSample->NumChannels(); 299 300 float pLevelMatrix[ MAX_CHANNELS_PER_VOICE * MAX_CHANNELS_PER_VOICE ] = { 0 }; 301 CalculateSurround( srcChannels, pLevelMatrix, 1.0f ); 302 303 if ( s_skipHardwareSets.GetBool() ) { 304 return true; 305 } 306 307 pSourceVoice->SetOutputMatrix( soundSystemLocal.hardware.pMasterVoice, srcChannels, dstChannels, pLevelMatrix, OPERATION_SET ); 308 309 assert( idMath::Fabs( gain ) <= XAUDIO2_MAX_VOLUME_LEVEL ); 310 pSourceVoice->SetVolume( gain, OPERATION_SET ); 311 312 SetSampleRate( sampleRate, OPERATION_SET ); 313 314 // we don't do this any longer because we pause and unpause explicitly when the soundworld is paused or unpaused 315 // UnPause(); 316 return true; 317 } 318 319 /* 320 ======================== 321 idSoundVoice_XAudio2::IsPlaying 322 ======================== 323 */ 324 bool idSoundVoice_XAudio2::IsPlaying() { 325 if ( pSourceVoice == NULL ) { 326 return false; 327 } 328 XAUDIO2_VOICE_STATE state; 329 pSourceVoice->GetState( &state ); 330 return ( state.BuffersQueued != 0 ); 331 } 332 333 /* 334 ======================== 335 idSoundVoice_XAudio2::FlushSourceBuffers 336 ======================== 337 */ 338 void idSoundVoice_XAudio2::FlushSourceBuffers() { 339 if ( pSourceVoice != NULL ) { 340 pSourceVoice->FlushSourceBuffers(); 341 } 342 } 343 344 /* 345 ======================== 346 idSoundVoice_XAudio2::Pause 347 ======================== 348 */ 349 void idSoundVoice_XAudio2::Pause() { 350 if ( !pSourceVoice || paused ) { 351 return; 352 } 353 if ( s_debugHardware.GetBool() ) { 354 idLib::Printf( "%dms: %p pausing %s\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>" ); 355 } 356 pSourceVoice->Stop( 0, OPERATION_SET ); 357 paused = true; 358 } 359 360 /* 361 ======================== 362 idSoundVoice_XAudio2::UnPause 363 ======================== 364 */ 365 void idSoundVoice_XAudio2::UnPause() { 366 if ( !pSourceVoice || !paused ) { 367 return; 368 } 369 if ( s_debugHardware.GetBool() ) { 370 idLib::Printf( "%dms: %p unpausing %s\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>" ); 371 } 372 pSourceVoice->Start( 0, OPERATION_SET ); 373 paused = false; 374 } 375 376 /* 377 ======================== 378 idSoundVoice_XAudio2::Stop 379 ======================== 380 */ 381 void idSoundVoice_XAudio2::Stop() { 382 if ( !pSourceVoice ) { 383 return; 384 } 385 if ( !paused ) { 386 if ( s_debugHardware.GetBool() ) { 387 idLib::Printf( "%dms: %p stopping %s\n", Sys_Milliseconds(), pSourceVoice, leadinSample ? leadinSample->GetName() : "<null>" ); 388 } 389 pSourceVoice->Stop( 0, OPERATION_SET ); 390 paused = true; 391 } 392 } 393 394 /* 395 ======================== 396 idSoundVoice_XAudio2::GetAmplitude 397 ======================== 398 */ 399 float idSoundVoice_XAudio2::GetAmplitude() { 400 if ( !hasVUMeter ) { 401 return 1.0f; 402 } 403 404 float peakLevels[ MAX_CHANNELS_PER_VOICE ]; 405 float rmsLevels[ MAX_CHANNELS_PER_VOICE ]; 406 407 XAUDIO2FX_VOLUMEMETER_LEVELS levels; 408 levels.ChannelCount = leadinSample->NumChannels(); 409 levels.pPeakLevels = peakLevels; 410 levels.pRMSLevels = rmsLevels; 411 412 if ( levels.ChannelCount > MAX_CHANNELS_PER_VOICE ) { 413 levels.ChannelCount = MAX_CHANNELS_PER_VOICE; 414 } 415 416 if ( pSourceVoice->GetEffectParameters( 0, &levels, sizeof( levels ) ) != S_OK ) { 417 return 0.0f; 418 } 419 420 if ( levels.ChannelCount == 1 ) { 421 return rmsLevels[0]; 422 } 423 424 float rms = 0.0f; 425 for ( uint32 i = 0; i < levels.ChannelCount; i++ ) { 426 rms += rmsLevels[i]; 427 } 428 429 return rms / (float)levels.ChannelCount; 430 } 431 432 /* 433 ======================== 434 idSoundVoice_XAudio2::ResetSampleRate 435 ======================== 436 */ 437 void idSoundVoice_XAudio2::SetSampleRate( uint32 newSampleRate, uint32 operationSet ){ 438 if ( pSourceVoice == NULL || leadinSample == NULL ) { 439 return; 440 } 441 442 sampleRate = newSampleRate; 443 444 XAUDIO2_FILTER_PARAMETERS filter; 445 filter.Type = LowPassFilter; 446 filter.OneOverQ = 1.0f; // [0.0f, XAUDIO2_MAX_FILTER_ONEOVERQ] 447 float cutoffFrequency = 1000.0f / Max( 0.01f, occlusion ); 448 if ( cutoffFrequency * 6.0f >= (float)sampleRate ) { 449 filter.Frequency = XAUDIO2_MAX_FILTER_FREQUENCY; 450 } else { 451 filter.Frequency = 2.0f * idMath::Sin( idMath::PI * cutoffFrequency / (float)sampleRate ); 452 } 453 assert( filter.Frequency >= 0.0f && filter.Frequency <= XAUDIO2_MAX_FILTER_FREQUENCY ); 454 filter.Frequency = idMath::ClampFloat( 0.0f, XAUDIO2_MAX_FILTER_FREQUENCY, filter.Frequency ); 455 456 pSourceVoice->SetFilterParameters( &filter, operationSet ); 457 458 float freqRatio = pitch * (float)sampleRate / (float)sourceVoiceRate; 459 assert( freqRatio >= XAUDIO2_MIN_FREQ_RATIO && freqRatio <= XAUDIO2_MAX_FREQ_RATIO ); 460 freqRatio = idMath::ClampFloat( XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_MAX_FREQ_RATIO, freqRatio ); 461 462 // if the value specified for maxFreqRatio is too high for the specified format, the call to CreateSourceVoice will fail 463 if ( numChannels == 1 ) { 464 assert( freqRatio * (float)SYSTEM_SAMPLE_RATE <= XAUDIO2_MAX_RATIO_TIMES_RATE_XMA_MONO ); 465 } else { 466 assert( freqRatio * (float)SYSTEM_SAMPLE_RATE <= XAUDIO2_MAX_RATIO_TIMES_RATE_XMA_MULTICHANNEL ); 467 } 468 pSourceVoice->SetFrequencyRatio( freqRatio, operationSet ); 469 } 470 471 /* 472 ======================== 473 idSoundVoice_XAudio2::OnBufferStart 474 ======================== 475 */ 476 void idSoundVoice_XAudio2::OnBufferStart( idSoundSample_XAudio2 * sample, int bufferNumber ) { 477 SetSampleRate( sample->SampleRate(), XAUDIO2_COMMIT_NOW ); 478 479 idSoundSample_XAudio2 * nextSample = sample; 480 int nextBuffer = bufferNumber + 1; 481 if ( nextBuffer == sample->buffers.Num() ) { 482 if ( sample == leadinSample ) { 483 if ( loopingSample == NULL ) { 484 return; 485 } 486 nextSample = loopingSample; 487 } 488 nextBuffer = 0; 489 } 490 491 SubmitBuffer( nextSample, nextBuffer, 0 ); 492 }