DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }