XA2_SoundSample.cpp (14424B)
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 extern idCVar s_useCompression; 33 extern idCVar s_noSound; 34 35 #define GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( x ) x 36 37 const uint32 SOUND_MAGIC_IDMSA = 0x6D7A7274; 38 39 extern idCVar sys_lang; 40 41 /* 42 ======================== 43 AllocBuffer 44 ======================== 45 */ 46 static void * AllocBuffer( int size, const char * name ) { 47 return Mem_Alloc( size, TAG_AUDIO ); 48 } 49 50 /* 51 ======================== 52 FreeBuffer 53 ======================== 54 */ 55 static void FreeBuffer( void * p ) { 56 return Mem_Free( p ); 57 } 58 59 /* 60 ======================== 61 idSoundSample_XAudio2::idSoundSample_XAudio2 62 ======================== 63 */ 64 idSoundSample_XAudio2::idSoundSample_XAudio2() { 65 timestamp = FILE_NOT_FOUND_TIMESTAMP; 66 loaded = false; 67 neverPurge = false; 68 levelLoadReferenced = false; 69 70 memset( &format, 0, sizeof( format ) ); 71 72 totalBufferSize = 0; 73 74 playBegin = 0; 75 playLength = 0; 76 77 lastPlayedTime = 0; 78 } 79 80 /* 81 ======================== 82 idSoundSample_XAudio2::~idSoundSample_XAudio2 83 ======================== 84 */ 85 idSoundSample_XAudio2::~idSoundSample_XAudio2() { 86 FreeData(); 87 } 88 89 /* 90 ======================== 91 idSoundSample_XAudio2::WriteGeneratedSample 92 ======================== 93 */ 94 void idSoundSample_XAudio2::WriteGeneratedSample( idFile *fileOut ) { 95 fileOut->WriteBig( SOUND_MAGIC_IDMSA ); 96 fileOut->WriteBig( timestamp ); 97 fileOut->WriteBig( loaded ); 98 fileOut->WriteBig( playBegin ); 99 fileOut->WriteBig( playLength ); 100 idWaveFile::WriteWaveFormatDirect( format, fileOut ); 101 fileOut->WriteBig( ( int )amplitude.Num() ); 102 fileOut->Write( amplitude.Ptr(), amplitude.Num() ); 103 fileOut->WriteBig( totalBufferSize ); 104 fileOut->WriteBig( ( int )buffers.Num() ); 105 for ( int i = 0; i < buffers.Num(); i++ ) { 106 fileOut->WriteBig( buffers[ i ].numSamples ); 107 fileOut->WriteBig( buffers[ i ].bufferSize ); 108 fileOut->Write( buffers[ i ].buffer, buffers[ i ].bufferSize ); 109 }; 110 } 111 /* 112 ======================== 113 idSoundSample_XAudio2::WriteAllSamples 114 ======================== 115 */ 116 void idSoundSample_XAudio2::WriteAllSamples( const idStr &sampleName ) { 117 idSoundSample_XAudio2 * samplePC = new idSoundSample_XAudio2(); 118 { 119 idStrStatic< MAX_OSPATH > inName = sampleName; 120 inName.Append( ".msadpcm" ); 121 idStrStatic< MAX_OSPATH > inName2 = sampleName; 122 inName2.Append( ".wav" ); 123 124 idStrStatic< MAX_OSPATH > outName = "generated/"; 125 outName.Append( sampleName ); 126 outName.Append( ".idwav" ); 127 128 if ( samplePC->LoadWav( inName ) || samplePC->LoadWav( inName2 ) ) { 129 idFile *fileOut = fileSystem->OpenFileWrite( outName, "fs_basepath" ); 130 samplePC->WriteGeneratedSample( fileOut ); 131 delete fileOut; 132 } 133 } 134 delete samplePC; 135 } 136 137 /* 138 ======================== 139 idSoundSample_XAudio2::LoadGeneratedSound 140 ======================== 141 */ 142 bool idSoundSample_XAudio2::LoadGeneratedSample( const idStr &filename ) { 143 idFileLocal fileIn( fileSystem->OpenFileReadMemory( filename ) ); 144 if ( fileIn != NULL ) { 145 uint32 magic; 146 fileIn->ReadBig( magic ); 147 fileIn->ReadBig( timestamp ); 148 fileIn->ReadBig( loaded ); 149 fileIn->ReadBig( playBegin ); 150 fileIn->ReadBig( playLength ); 151 idWaveFile::ReadWaveFormatDirect( format, fileIn ); 152 int num; 153 fileIn->ReadBig( num ); 154 amplitude.Clear(); 155 amplitude.SetNum( num ); 156 fileIn->Read( amplitude.Ptr(), amplitude.Num() ); 157 fileIn->ReadBig( totalBufferSize ); 158 fileIn->ReadBig( num ); 159 buffers.SetNum( num ); 160 for ( int i = 0; i < num; i++ ) { 161 fileIn->ReadBig( buffers[ i ].numSamples ); 162 fileIn->ReadBig( buffers[ i ].bufferSize ); 163 buffers[ i ].buffer = AllocBuffer( buffers[ i ].bufferSize, GetName() ); 164 fileIn->Read( buffers[ i ].buffer, buffers[ i ].bufferSize ); 165 buffers[ i ].buffer = GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( buffers[ i ].buffer ); 166 } 167 return true; 168 } 169 return false; 170 } 171 /* 172 ======================== 173 idSoundSample_XAudio2::Load 174 ======================== 175 */ 176 void idSoundSample_XAudio2::LoadResource() { 177 FreeData(); 178 179 if ( idStr::Icmpn( GetName(), "_default", 8 ) == 0 ) { 180 MakeDefault(); 181 return; 182 } 183 184 if ( s_noSound.GetBool() ) { 185 MakeDefault(); 186 return; 187 } 188 189 loaded = false; 190 191 for ( int i = 0; i < 2; i++ ) { 192 idStrStatic< MAX_OSPATH > sampleName = GetName(); 193 if ( ( i == 0 ) && !sampleName.Replace( "/vo/", va( "/vo/%s/", sys_lang.GetString() ) ) ) { 194 i++; 195 } 196 idStrStatic< MAX_OSPATH > generatedName = "generated/"; 197 generatedName.Append( sampleName ); 198 199 { 200 if ( s_useCompression.GetBool() ) { 201 sampleName.Append( ".msadpcm" ); 202 } else { 203 sampleName.Append( ".wav" ); 204 } 205 generatedName.Append( ".idwav" ); 206 } 207 loaded = LoadGeneratedSample( generatedName ) || LoadWav( sampleName ); 208 209 if ( !loaded && s_useCompression.GetBool() ) { 210 sampleName.SetFileExtension( "wav" ); 211 loaded = LoadWav( sampleName ); 212 } 213 214 if ( loaded ) { 215 if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) { 216 fileSystem->AddSamplePreload( GetName() ); 217 WriteAllSamples( GetName() ); 218 219 if ( sampleName.Find( "/vo/" ) >= 0 ) { 220 for ( int i = 0; i < Sys_NumLangs(); i++ ) { 221 const char * lang = Sys_Lang( i ); 222 if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) { 223 continue; 224 } 225 idStrStatic< MAX_OSPATH > locName = GetName(); 226 locName.Replace( "/vo/", va( "/vo/%s/", Sys_Lang( i ) ) ); 227 WriteAllSamples( locName ); 228 } 229 } 230 } 231 return; 232 } 233 } 234 235 if ( !loaded ) { 236 // make it default if everything else fails 237 MakeDefault(); 238 } 239 return; 240 } 241 242 /* 243 ======================== 244 idSoundSample_XAudio2::LoadWav 245 ======================== 246 */ 247 bool idSoundSample_XAudio2::LoadWav( const idStr & filename ) { 248 249 // load the wave 250 idWaveFile wave; 251 if ( !wave.Open( filename ) ) { 252 return false; 253 } 254 255 idStrStatic< MAX_OSPATH > sampleName = filename; 256 sampleName.SetFileExtension( "amp" ); 257 LoadAmplitude( sampleName ); 258 259 const char * formatError = wave.ReadWaveFormat( format ); 260 if ( formatError != NULL ) { 261 idLib::Warning( "LoadWav( %s ) : %s", filename.c_str(), formatError ); 262 MakeDefault(); 263 return false; 264 } 265 timestamp = wave.Timestamp(); 266 267 totalBufferSize = wave.SeekToChunk( 'data' ); 268 269 if ( format.basic.formatTag == idWaveFile::FORMAT_PCM || format.basic.formatTag == idWaveFile::FORMAT_EXTENSIBLE ) { 270 271 if ( format.basic.bitsPerSample != 16 ) { 272 idLib::Warning( "LoadWav( %s ) : %s", filename.c_str(), "Not a 16 bit PCM wav file" ); 273 MakeDefault(); 274 return false; 275 } 276 277 playBegin = 0; 278 playLength = ( totalBufferSize ) / format.basic.blockSize; 279 280 buffers.SetNum( 1 ); 281 buffers[0].bufferSize = totalBufferSize; 282 buffers[0].numSamples = playLength; 283 buffers[0].buffer = AllocBuffer( totalBufferSize, GetName() ); 284 285 286 wave.Read( buffers[0].buffer, totalBufferSize ); 287 288 if ( format.basic.bitsPerSample == 16 ) { 289 idSwap::LittleArray( (short *)buffers[0].buffer, totalBufferSize / sizeof( short ) ); 290 } 291 292 buffers[0].buffer = GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( buffers[0].buffer ); 293 294 } else if ( format.basic.formatTag == idWaveFile::FORMAT_ADPCM ) { 295 296 playBegin = 0; 297 playLength = ( ( totalBufferSize / format.basic.blockSize ) * format.extra.adpcm.samplesPerBlock ); 298 299 buffers.SetNum( 1 ); 300 buffers[0].bufferSize = totalBufferSize; 301 buffers[0].numSamples = playLength; 302 buffers[0].buffer = AllocBuffer( totalBufferSize, GetName() ); 303 304 wave.Read( buffers[0].buffer, totalBufferSize ); 305 306 buffers[0].buffer = GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( buffers[0].buffer ); 307 308 } else if ( format.basic.formatTag == idWaveFile::FORMAT_XMA2 ) { 309 310 if ( format.extra.xma2.blockCount == 0 ) { 311 idLib::Warning( "LoadWav( %s ) : %s", filename.c_str(), "No data blocks in file" ); 312 MakeDefault(); 313 return false; 314 } 315 316 int bytesPerBlock = format.extra.xma2.bytesPerBlock; 317 assert( format.extra.xma2.blockCount == ALIGN( totalBufferSize, bytesPerBlock ) / bytesPerBlock ); 318 assert( format.extra.xma2.blockCount * bytesPerBlock >= totalBufferSize ); 319 assert( format.extra.xma2.blockCount * bytesPerBlock < totalBufferSize + bytesPerBlock ); 320 321 buffers.SetNum( format.extra.xma2.blockCount ); 322 for ( int i = 0; i < buffers.Num(); i++ ) { 323 if ( i == buffers.Num() - 1 ) { 324 buffers[i].bufferSize = totalBufferSize - ( i * bytesPerBlock ); 325 } else { 326 buffers[i].bufferSize = bytesPerBlock; 327 } 328 329 buffers[i].buffer = AllocBuffer( buffers[i].bufferSize, GetName() ); 330 wave.Read( buffers[i].buffer, buffers[i].bufferSize ); 331 buffers[i].buffer = GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( buffers[i].buffer ); 332 } 333 334 int seekTableSize = wave.SeekToChunk( 'seek' ); 335 if ( seekTableSize != 4 * buffers.Num() ) { 336 idLib::Warning( "LoadWav( %s ) : %s", filename.c_str(), "Wrong number of entries in seek table" ); 337 MakeDefault(); 338 return false; 339 } 340 341 for ( int i = 0; i < buffers.Num(); i++ ) { 342 wave.Read( &buffers[i].numSamples, sizeof( buffers[i].numSamples ) ); 343 idSwap::Big( buffers[i].numSamples ); 344 } 345 346 playBegin = format.extra.xma2.loopBegin; 347 playLength = format.extra.xma2.loopLength; 348 349 if ( buffers[buffers.Num()-1].numSamples < playBegin + playLength ) { 350 // This shouldn't happen, but it's not fatal if it does 351 playLength = buffers[buffers.Num()-1].numSamples - playBegin; 352 } else { 353 // Discard samples beyond playLength 354 for ( int i = 0; i < buffers.Num(); i++ ) { 355 if ( buffers[i].numSamples > playBegin + playLength ) { 356 buffers[i].numSamples = playBegin + playLength; 357 // Ideally, the following loop should always have 0 iterations because playBegin + playLength ends in the last block already 358 // But there is no guarantee for that, so to be safe, discard all buffers beyond this one 359 for ( int j = i + 1; j < buffers.Num(); j++ ) { 360 FreeBuffer( buffers[j].buffer ); 361 } 362 buffers.SetNum( i + 1 ); 363 break; 364 } 365 } 366 } 367 368 } else { 369 idLib::Warning( "LoadWav( %s ) : Unsupported wave format %d", filename.c_str(), format.basic.formatTag ); 370 MakeDefault(); 371 return false; 372 } 373 374 wave.Close(); 375 376 if ( format.basic.formatTag == idWaveFile::FORMAT_EXTENSIBLE ) { 377 // HACK: XAudio2 doesn't really support FORMAT_EXTENSIBLE so we convert it to a basic format after extracting the channel mask 378 format.basic.formatTag = format.extra.extensible.subFormat.data1; 379 } 380 381 // sanity check... 382 assert( buffers[buffers.Num()-1].numSamples == playBegin + playLength ); 383 384 return true; 385 } 386 387 388 /* 389 ======================== 390 idSoundSample_XAudio2::MakeDefault 391 ======================== 392 */ 393 void idSoundSample_XAudio2::MakeDefault() { 394 FreeData(); 395 396 static const int DEFAULT_NUM_SAMPLES = 256; 397 398 timestamp = FILE_NOT_FOUND_TIMESTAMP; 399 loaded = true; 400 401 memset( &format, 0, sizeof( format ) ); 402 format.basic.formatTag = idWaveFile::FORMAT_PCM; 403 format.basic.numChannels = 1; 404 format.basic.bitsPerSample = 16; 405 format.basic.samplesPerSec = XAUDIO2_MIN_SAMPLE_RATE; 406 format.basic.blockSize = format.basic.numChannels * format.basic.bitsPerSample / 8; 407 format.basic.avgBytesPerSec = format.basic.samplesPerSec * format.basic.blockSize; 408 409 assert( format.basic.blockSize == 2 ); 410 411 totalBufferSize = DEFAULT_NUM_SAMPLES * 2; 412 413 short * defaultBuffer = (short *)AllocBuffer( totalBufferSize, GetName() ); 414 for ( int i = 0; i < DEFAULT_NUM_SAMPLES; i += 2 ) { 415 defaultBuffer[i + 0] = SHRT_MIN; 416 defaultBuffer[i + 1] = SHRT_MAX; 417 } 418 419 buffers.SetNum( 1 ); 420 buffers[0].buffer = defaultBuffer; 421 buffers[0].bufferSize = totalBufferSize; 422 buffers[0].numSamples = DEFAULT_NUM_SAMPLES; 423 buffers[0].buffer = GPU_CONVERT_CPU_TO_CPU_CACHED_READONLY_ADDRESS( buffers[0].buffer ); 424 425 playBegin = 0; 426 playLength = DEFAULT_NUM_SAMPLES; 427 } 428 429 /* 430 ======================== 431 idSoundSample_XAudio2::FreeData 432 433 Called before deleting the object and at the start of LoadResource() 434 ======================== 435 */ 436 void idSoundSample_XAudio2::FreeData() { 437 if ( buffers.Num() > 0 ) { 438 soundSystemLocal.StopVoicesWithSample( (idSoundSample *)this ); 439 for ( int i = 0; i < buffers.Num(); i++ ) { 440 FreeBuffer( buffers[i].buffer ); 441 } 442 buffers.Clear(); 443 } 444 amplitude.Clear(); 445 446 timestamp = FILE_NOT_FOUND_TIMESTAMP; 447 memset( &format, 0, sizeof( format ) ); 448 loaded = false; 449 totalBufferSize = 0; 450 playBegin = 0; 451 playLength = 0; 452 } 453 454 /* 455 ======================== 456 idSoundSample_XAudio2::LoadAmplitude 457 ======================== 458 */ 459 bool idSoundSample_XAudio2::LoadAmplitude( const idStr & name ) { 460 amplitude.Clear(); 461 idFileLocal f( fileSystem->OpenFileRead( name ) ); 462 if ( f == NULL ) { 463 return false; 464 } 465 amplitude.SetNum( f->Length() ); 466 f->Read( amplitude.Ptr(), amplitude.Num() ); 467 return true; 468 } 469 470 /* 471 ======================== 472 idSoundSample_XAudio2::GetAmplitude 473 ======================== 474 */ 475 float idSoundSample_XAudio2::GetAmplitude( int timeMS ) const { 476 if ( timeMS < 0 || timeMS > LengthInMsec() ) { 477 return 0.0f; 478 } 479 if ( IsDefault() ) { 480 return 1.0f; 481 } 482 int index = timeMS * 60 / 1000; 483 if ( index < 0 || index >= amplitude.Num() ) { 484 return 0.0f; 485 } 486 return (float)amplitude[index] / 255.0f; 487 }