WaveFile.cpp (17450B)
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 31 /* 32 ================================================================================================ 33 Contains the WaveFile implementation. 34 ================================================================================================ 35 */ 36 37 #include "WaveFile.h" 38 39 /* 40 ======================== 41 idWaveFile::Open 42 43 Returns true if the Open was successful and the file matches the expected format. If this 44 returns false, there is no need to call Close. 45 ======================== 46 */ 47 bool idWaveFile::Open( const char * filename ) { 48 Close(); 49 50 if ( filename == NULL || filename[0] == 0 ) { 51 return false; 52 } 53 54 if ( file == NULL ) { 55 file = fileSystem->OpenFileReadMemory( filename ); 56 if ( file == NULL ) { 57 return false; 58 } 59 } 60 61 if ( file->Length() == 0 ) { 62 Close(); 63 return false; 64 } 65 66 struct header_t { 67 uint32 id; 68 uint32 size; 69 uint32 format; 70 } header; 71 72 file->Read( &header, sizeof( header ) ); 73 idSwap::Big( header.id ); 74 idSwap::Little( header.size ); 75 idSwap::Big( header.format ); 76 77 if ( header.id != 'RIFF' || header.format != 'WAVE' || header.size < 4 ) { 78 Close(); 79 idLib::Warning( "Header is not RIFF WAVE in %s", filename ); 80 return false; 81 } 82 83 uint32 riffSize = header.size + 8; 84 uint32 offset = sizeof( header ); 85 86 // Scan the file collecting chunks 87 while ( offset < riffSize ) { 88 struct chuckHeader_t { 89 uint32 id; 90 uint32 size; 91 } chunkHeader; 92 if ( file->Read( &chunkHeader, sizeof( chunkHeader ) ) != sizeof( chunkHeader ) ) { 93 // It seems like some tools have extra data after the last chunk for no apparent reason 94 // so don't treat this as an error 95 return true; 96 } 97 idSwap::Big( chunkHeader.id ); 98 idSwap::Little( chunkHeader.size ); 99 offset += sizeof( chunkHeader ); 100 101 if ( chunks.Num() >= chunks.Max() ) { 102 Close(); 103 idLib::Warning( "More than %d chunks in %s", chunks.Max(), filename ); 104 return false; 105 } 106 107 chunk_t * chunk = chunks.Alloc(); 108 chunk->id = chunkHeader.id; 109 chunk->size = chunkHeader.size; 110 chunk->offset = offset; 111 offset += chunk->size; 112 113 file->Seek( offset, FS_SEEK_SET ); 114 } 115 116 return true; 117 } 118 119 /* 120 ======================== 121 idWaveFile::SeekToChunk 122 123 Seeks to the specified chunk and returns the size of the chunk or 0 if the chunk wasn't found. 124 ======================== 125 */ 126 uint32 idWaveFile::SeekToChunk( uint32 id ) { 127 for ( int i = 0; i < chunks.Num(); i++ ) { 128 if ( chunks[i].id == id ) { 129 file->Seek( chunks[i].offset, FS_SEEK_SET ); 130 return chunks[i].size; 131 } 132 } 133 return 0; 134 } 135 136 /* 137 ======================== 138 idWaveFile::GetChunkOffset 139 140 Seeks to the specified chunk and returns the size of the chunk or 0 if the chunk wasn't found. 141 ======================== 142 */ 143 uint32 idWaveFile::GetChunkOffset( uint32 id ) { 144 for ( int i = 0; i < chunks.Num(); i++ ) { 145 if ( chunks[i].id == id ) { 146 return chunks[i].offset; 147 } 148 } 149 return 0; 150 } 151 152 // Used in XMA2WAVEFORMAT for per-stream data 153 typedef struct XMA2STREAMFORMAT { 154 byte Channels; // Number of channels in the stream (1 or 2) 155 byte RESERVED; // Reserved for future use 156 uint16 ChannelMask; // Spatial positions of the channels in the stream 157 } XMA2STREAMFORMAT; 158 159 // Legacy XMA2 format structure (big-endian byte ordering) 160 typedef struct XMA2WAVEFORMAT { 161 byte Version; // XMA encoder version that generated the file. 162 // Always 3 or higher for XMA2 files. 163 byte NumStreams; // Number of interleaved audio streams 164 byte RESERVED; // Reserved for future use 165 byte LoopCount; // Number of loop repetitions; 255 = infinite 166 uint32 LoopBegin; // Loop begin point, in samples 167 uint32 LoopEnd; // Loop end point, in samples 168 uint32 SampleRate; // The file's decoded sample rate 169 uint32 EncodeOptions; // Options for the XMA encoder/decoder 170 uint32 PsuedoBytesPerSec; // Used internally by the XMA encoder 171 uint32 BlockSizeInBytes; // Size in bytes of this file's XMA blocks (except 172 // possibly the last one). Always a multiple of 173 // 2Kb, since XMA blocks are arrays of 2Kb packets. 174 uint32 SamplesEncoded; // Total number of PCM samples encoded in this file 175 uint32 SamplesInSource; // Actual number of PCM samples in the source 176 // material used to generate this file 177 uint32 BlockCount; // Number of XMA blocks in this file (and hence 178 // also the number of entries in its seek table) 179 } XMA2WAVEFORMAT; 180 181 /* 182 ======================== 183 idWaveFile::ReadWaveFormat 184 185 Reads a wave format header, returns NULL if it found one and read it. 186 otherwise, returns a human-readable error message. 187 ======================== 188 */ 189 const char * idWaveFile::ReadWaveFormat( waveFmt_t & format ) { 190 memset( &format, 0, sizeof( format ) ); 191 192 uint32 formatSize = SeekToChunk( waveFmt_t::id ); 193 if ( formatSize == 0 ) { 194 return "No format chunk"; 195 } 196 if ( formatSize < sizeof( format.basic ) ) { 197 return "Format chunk too small"; 198 } 199 200 Read( &format.basic, sizeof( format.basic ) ); 201 202 idSwapClass<waveFmt_t::basic_t> swap; 203 swap.Little( format.basic.formatTag ); 204 swap.Little( format.basic.numChannels ); 205 swap.Little( format.basic.samplesPerSec ); 206 swap.Little( format.basic.avgBytesPerSec ); 207 swap.Little( format.basic.blockSize ); 208 swap.Little( format.basic.bitsPerSample ); 209 210 if ( format.basic.formatTag == FORMAT_PCM ) { 211 } else if ( format.basic.formatTag == FORMAT_ADPCM ) { 212 Read( &format.extraSize, sizeof( format.extraSize ) ); 213 idSwap::Little( format.extraSize ); 214 if ( format.extraSize != sizeof( waveFmt_t::extra_t::adpcm_t ) ) { 215 return "Incorrect number of coefficients in ADPCM file"; 216 } 217 Read( &format.extra.adpcm, sizeof( format.extra.adpcm ) ); 218 idSwapClass<waveFmt_t::extra_t::adpcm_t> swap; 219 swap.Little( format.extra.adpcm.samplesPerBlock ); 220 swap.Little( format.extra.adpcm.numCoef ); 221 for ( int i = 0; i < format.extra.adpcm.numCoef; i++ ) { 222 swap.Little( format.extra.adpcm.aCoef[ i ].coef1 ); 223 swap.Little( format.extra.adpcm.aCoef[ i ].coef2 ); 224 } 225 } else if ( format.basic.formatTag == FORMAT_XMA2 ) { 226 Read( &format.extraSize, sizeof( format.extraSize ) ); 227 idSwap::Little( format.extraSize ); 228 if ( format.extraSize != sizeof( waveFmt_t::extra_t::xma2_t ) ) { 229 return "Incorrect chunk size in XMA2 file"; 230 } 231 Read( &format.extra.xma2, sizeof( format.extra.xma2 ) ); 232 idSwapClass<waveFmt_t::extra_t::xma2_t> swap; 233 swap.Little( format.extra.xma2.numStreams ); 234 swap.Little( format.extra.xma2.channelMask ); 235 swap.Little( format.extra.xma2.samplesEncoded ); 236 swap.Little( format.extra.xma2.bytesPerBlock ); 237 swap.Little( format.extra.xma2.playBegin ); 238 swap.Little( format.extra.xma2.playLength ); 239 swap.Little( format.extra.xma2.loopBegin ); 240 swap.Little( format.extra.xma2.loopLength ); 241 swap.Little( format.extra.xma2.loopCount ); 242 swap.Little( format.extra.xma2.encoderVersion ); 243 swap.Little( format.extra.xma2.blockCount ); 244 } else if ( format.basic.formatTag == FORMAT_EXTENSIBLE ) { 245 Read( &format.extraSize, sizeof( format.extraSize ) ); 246 idSwap::Little( format.extraSize ); 247 if ( format.extraSize != sizeof( waveFmt_t::extra_t::extensible_t ) ) { 248 return "Incorrect chunk size in extensible wave file"; 249 } 250 Read( &format.extra.extensible, sizeof( format.extra.extensible ) ); 251 idSwapClass<waveFmt_t::extra_t::extensible_t> swap; 252 swap.Little( format.extra.extensible.validBitsPerSample ); 253 swap.Little( format.extra.extensible.channelMask ); 254 swap.Little( format.extra.extensible.subFormat.data1 ); 255 swap.Little( format.extra.extensible.subFormat.data2 ); 256 swap.Little( format.extra.extensible.subFormat.data3 ); 257 swap.Little( format.extra.extensible.subFormat.data4 ); 258 swap.LittleArray( format.extra.extensible.subFormat.data5, 6 ); 259 waveFmt_t::extra_t::extensible_t::guid_t pcmGuid = { 260 FORMAT_PCM, 261 0x0000, 262 0x0010, 263 0x8000, 264 { 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } 265 }; 266 if ( memcmp( &pcmGuid, &format.extra.extensible.subFormat, sizeof( pcmGuid ) ) != 0 ) { 267 return "Unsupported Extensible format"; 268 } 269 } else { 270 return "Unknown wave format tag"; 271 } 272 273 return NULL; 274 } 275 276 /* 277 ======================== 278 idWaveFile::ReadWaveFormatDirect 279 280 Reads a wave format header from a file ptr, 281 ======================== 282 */ 283 bool idWaveFile::ReadWaveFormatDirect( waveFmt_t & format, idFile *file ) { 284 285 file->Read( &format.basic, sizeof( format.basic ) ); 286 idSwapClass<waveFmt_t::basic_t> swap; 287 swap.Little( format.basic.formatTag ); 288 swap.Little( format.basic.numChannels ); 289 swap.Little( format.basic.samplesPerSec ); 290 swap.Little( format.basic.avgBytesPerSec ); 291 swap.Little( format.basic.blockSize ); 292 swap.Little( format.basic.bitsPerSample ); 293 294 if ( format.basic.formatTag == FORMAT_PCM ) { 295 } else if ( format.basic.formatTag == FORMAT_ADPCM ) { 296 file->Read( &format.extraSize, sizeof( format.extraSize ) ); 297 idSwap::Little( format.extraSize ); 298 if ( format.extraSize != sizeof( waveFmt_t::extra_t::adpcm_t ) ) { 299 return false; 300 } 301 file->Read( &format.extra.adpcm, sizeof( format.extra.adpcm ) ); 302 idSwapClass<waveFmt_t::extra_t::adpcm_t> swap; 303 swap.Little( format.extra.adpcm.samplesPerBlock ); 304 swap.Little( format.extra.adpcm.numCoef ); 305 for ( int i = 0; i < format.extra.adpcm.numCoef; i++ ) { 306 swap.Little( format.extra.adpcm.aCoef[ i ].coef1 ); 307 swap.Little( format.extra.adpcm.aCoef[ i ].coef2 ); 308 } 309 } else if ( format.basic.formatTag == FORMAT_XMA2 ) { 310 file->Read( &format.extraSize, sizeof( format.extraSize ) ); 311 idSwap::Little( format.extraSize ); 312 if ( format.extraSize != sizeof( waveFmt_t::extra_t::xma2_t ) ) { 313 return false; 314 } 315 file->Read( &format.extra.xma2, sizeof( format.extra.xma2 ) ); 316 idSwapClass<waveFmt_t::extra_t::xma2_t> swap; 317 swap.Little( format.extra.xma2.numStreams ); 318 swap.Little( format.extra.xma2.channelMask ); 319 swap.Little( format.extra.xma2.samplesEncoded ); 320 swap.Little( format.extra.xma2.bytesPerBlock ); 321 swap.Little( format.extra.xma2.playBegin ); 322 swap.Little( format.extra.xma2.playLength ); 323 swap.Little( format.extra.xma2.loopBegin ); 324 swap.Little( format.extra.xma2.loopLength ); 325 swap.Little( format.extra.xma2.loopCount ); 326 swap.Little( format.extra.xma2.encoderVersion ); 327 swap.Little( format.extra.xma2.blockCount ); 328 } else if ( format.basic.formatTag == FORMAT_EXTENSIBLE ) { 329 file->Read( &format.extraSize, sizeof( format.extraSize ) ); 330 idSwap::Little( format.extraSize ); 331 if ( format.extraSize != sizeof( waveFmt_t::extra_t::extensible_t ) ) { 332 return false; 333 } 334 file->Read( &format.extra.extensible, sizeof( format.extra.extensible ) ); 335 idSwapClass<waveFmt_t::extra_t::extensible_t> swap; 336 swap.Little( format.extra.extensible.validBitsPerSample ); 337 swap.Little( format.extra.extensible.channelMask ); 338 swap.Little( format.extra.extensible.subFormat.data1 ); 339 swap.Little( format.extra.extensible.subFormat.data2 ); 340 swap.Little( format.extra.extensible.subFormat.data3 ); 341 swap.Little( format.extra.extensible.subFormat.data4 ); 342 swap.LittleArray( format.extra.extensible.subFormat.data5, 6 ); 343 waveFmt_t::extra_t::extensible_t::guid_t pcmGuid = { 344 FORMAT_PCM, 345 0x0000, 346 0x0010, 347 0x8000, 348 { 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } 349 }; 350 if ( memcmp( &pcmGuid, &format.extra.extensible.subFormat, sizeof( pcmGuid ) ) != 0 ) { 351 return false; 352 } 353 } else { 354 return false; 355 } 356 357 return true; 358 } 359 360 /* 361 ======================== 362 idWaveFile::WriteWaveFormatDirect 363 364 Writes a wave format header to a file ptr, 365 ======================== 366 */ 367 bool idWaveFile::WriteWaveFormatDirect( waveFmt_t & format, idFile *file ) { 368 //idSwapClass<waveFmt_t::basic_t> swap; 369 //swap.Little( format.basic.formatTag ); 370 //swap.Little( format.basic.numChannels ); 371 //swap.Little( format.basic.samplesPerSec ); 372 //swap.Little( format.basic.avgBytesPerSec ); 373 //swap.Little( format.basic.blockSize ); 374 //swap.Little( format.basic.bitsPerSample ); 375 file->Write( &format.basic, sizeof( format.basic ) ); 376 if ( format.basic.formatTag == FORMAT_PCM ) { 377 //file->Write( &format.basic, sizeof( format.basic ) ); 378 } else if ( format.basic.formatTag == FORMAT_ADPCM ) { 379 //file->Write( &format.basic, sizeof( format.basic ) ); 380 file->Write( &format.extraSize, sizeof( format.extraSize ) ); 381 file->Write( &format.extra.adpcm, sizeof( format.extra.adpcm ) ); 382 } else if ( format.basic.formatTag == FORMAT_XMA2 ) { 383 //file->Write( &format.basic, sizeof( format.basic ) ); 384 file->Write( &format.extraSize, sizeof( format.extraSize ) ); 385 file->Write( &format.extra.xma2, sizeof( format.extra.xma2 ) ); 386 } else if ( format.basic.formatTag == FORMAT_EXTENSIBLE ) { 387 //file->Write( &format.basic, sizeof( format.basic ) ); 388 file->Write( &format.extraSize, sizeof( format.extraSize ) ); 389 file->Write( &format.extra.extensible, sizeof( format.extra.extensible ) ); 390 } else { 391 return false; 392 } 393 return true; 394 } 395 396 /* 397 ======================== 398 idWaveFile::WriteWaveFormatDirect 399 400 Writes a wave format header to a file ptr, 401 ======================== 402 */ 403 404 bool idWaveFile::WriteSampleDataDirect( idList< sampleData_t > & sampleData, idFile * file ) { 405 static const uint32 sample = 'smpl'; 406 file->WriteBig( sample ); 407 uint32 samplerData = sampleData.Num() * 24; 408 uint32 chunkSize = 36 + samplerData; 409 uint32 zero = 0; 410 uint32 numSamples = sampleData.Num(); 411 412 file->Write( &chunkSize, sizeof( uint32 ) ); 413 file->Write( &zero, sizeof( uint32 ) ); 414 file->Write( &zero, sizeof( uint32 ) ); 415 file->Write( &zero, sizeof( uint32 ) ); 416 file->Write( &zero, sizeof( uint32 ) ); 417 file->Write( &zero, sizeof( uint32 ) ); 418 file->Write( &zero, sizeof( uint32 ) ); 419 file->Write( &zero, sizeof( uint32 ) ); 420 file->Write( &numSamples, sizeof( uint32 ) ); 421 file->Write( &samplerData, sizeof( uint32 ) ); 422 423 for ( int i = 0; i < sampleData.Num(); ++i ) { 424 file->Write( &zero, sizeof( uint32 ) ); 425 file->Write( &zero, sizeof( uint32 ) ); 426 file->Write( &sampleData[ i ].start, sizeof( uint32 ) ); 427 file->Write( &sampleData[ i ].end, sizeof( uint32 ) ); 428 file->Write( &zero, sizeof( uint32 ) ); 429 file->Write( &zero, sizeof( uint32 ) ); 430 } 431 return true; 432 } 433 434 /* 435 ======================== 436 idWaveFile::WriteWaveFormatDirect 437 438 Writes a data chunk to a file ptr 439 ======================== 440 */ 441 442 bool idWaveFile::WriteDataDirect( char * _data, uint32 size, idFile * file ) { 443 static const uint32 data = 'data'; 444 file->WriteBig( data ); 445 file->Write( &size, sizeof( uint32 ) ); 446 file->WriteBigArray( _data, size ); 447 return true; 448 } 449 450 /* 451 ======================== 452 idWaveFile::WriteWaveFormatDirect 453 454 Writes a wave header to a file ptr, 455 ======================== 456 */ 457 458 bool idWaveFile::WriteHeaderDirect( uint32 fileSize, idFile * file ) { 459 static const uint32 riff = 'RIFF'; 460 static const uint32 wave = 'WAVE'; 461 file->WriteBig( riff ); 462 file->WriteBig( fileSize ); 463 file->WriteBig( wave ); 464 return true; 465 } 466 467 /* 468 ======================== 469 idWaveFile::ReadLoopPoint 470 471 Reads a loop point from a 'smpl' chunk in a wave file, returns 0 if none are found. 472 ======================== 473 */ 474 bool idWaveFile::ReadLoopData( int & start, int & end ) { 475 uint32 chunkSize = SeekToChunk( samplerChunk_t::id ); 476 if ( chunkSize < sizeof( samplerChunk_t ) ) { 477 return false; 478 } 479 480 samplerChunk_t smpl; 481 Read( &smpl, sizeof( smpl ) ); 482 idSwap::Little( smpl.numSampleLoops ); 483 484 if ( smpl.numSampleLoops < 1 ) { 485 return false; // this is possible returning false lets us know there are more then 1 sample look in the file and is not appropriate for traditional looping 486 } 487 488 sampleData_t smplData; 489 Read( &smplData, sizeof( smplData ) ); 490 idSwap::Little( smplData.start ); 491 idSwap::Little( smplData.end ); 492 493 if ( smplData.type != 0 ) { 494 idLib::Warning( "Invalid loop type in %s", file->GetName() ); 495 return false; 496 } 497 498 start = smplData.start; 499 end = smplData.end; 500 return true; 501 } 502 503 /* 504 ======================== 505 idWaveFile::Close 506 507 Closes the file and frees resources. 508 ======================== 509 */ 510 void idWaveFile::Close() { 511 if ( file != NULL ) { 512 delete file; 513 file = NULL; 514 } 515 chunks.SetNum( 0 ); 516 }