sfxWavStream.cpp
Engine/source/sfx/media/sfxWavStream.cpp
Classes:
class
WAV Chunk-header.
class
WAV File-header.
class
WAV FmtEx-header.
class
class
WAV Smpl-header.
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "sfx/media/sfxWavStream.h" 25#include "core/stream/stream.h" 26#include "core/strings/stringFunctions.h" 27 28 29/// WAV File-header 30struct WAVFileHdr 31{ 32 U8 id[4]; 33 U32 size; 34 U8 type[4]; 35}; 36 37//// WAV Fmt-header 38struct WAVFmtHdr 39{ 40 U16 format; 41 U16 channels; 42 U32 samplesPerSec; 43 U32 bytesPerSec; 44 U16 blockAlign; 45 U16 bitsPerSample; 46}; 47 48/// WAV FmtEx-header 49struct WAVFmtExHdr 50{ 51 U16 size; 52 U16 samplesPerBlock; 53}; 54 55/// WAV Smpl-header 56struct WAVSmplHdr 57{ 58 U32 manufacturer; 59 U32 product; 60 U32 samplePeriod; 61 U32 note; 62 U32 fineTune; 63 U32 SMPTEFormat; 64 U32 SMPTEOffest; 65 U32 loops; 66 U32 samplerData; 67 68 struct 69 { 70 U32 identifier; 71 U32 type; 72 U32 start; 73 U32 end; 74 U32 fraction; 75 U32 count; 76 } loop[1]; 77}; 78 79/// WAV Chunk-header 80struct WAVChunkHdr 81{ 82 U8 id[4]; 83 U32 size; 84}; 85 86 87SFXWavStream* SFXWavStream::create( Stream *stream ) 88{ 89 SFXWavStream *sfxStream = new SFXWavStream(); 90 if ( sfxStream->open( stream, true ) ) 91 return sfxStream; 92 93 delete sfxStream; 94 return NULL; 95} 96 97SFXWavStream::SFXWavStream() 98{ 99} 100 101SFXWavStream::SFXWavStream( const SFXWavStream& cloneFrom ) 102 : Parent( cloneFrom ), 103 mDataStart( cloneFrom.mDataStart ) 104{ 105} 106 107SFXWavStream::~SFXWavStream() 108{ 109 // We must call close from our own destructor 110 // and not the base class... as it causes a 111 // pure virtual runtime assertion. 112 close(); 113} 114 115void SFXWavStream::_close() 116{ 117 mDataStart = -1; 118} 119 120bool SFXWavStream::_readHeader() 121{ 122 // We read the wav chunks to gather than header info 123 // and find the start and end position of the data chunk. 124 mDataStart = -1; 125 126 WAVFileHdr fileHdr; 127 mStream->read( 4, &fileHdr.id[0] ); 128 mStream->read( &fileHdr.size ); 129 mStream->read( 4, &fileHdr.type[0] ); 130 131 fileHdr.size=((fileHdr.size+1)&~1)-4; 132 133 WAVChunkHdr chunkHdr; 134 mStream->read( 4, &chunkHdr.id[0] ); 135 mStream->read( &chunkHdr.size ); 136 137 // Unread chunk data rounded up to nearest WORD. 138 S32 chunkRemaining = chunkHdr.size + ( chunkHdr.size & 1 ); 139 140 WAVFmtHdr fmtHdr; 141 WAVFmtExHdr fmtExHdr; 142 WAVSmplHdr smplHdr; 143 144 dMemset(&fmtHdr, 0, sizeof(fmtHdr)); 145 146 while ((fileHdr.size!=0) && (mStream->getStatus() != Stream::EOS)) 147 { 148 // WAV format header chunk. 149 if ( !dStrncmp( (const char*)chunkHdr.id, "fmt ", 4 ) ) 150 { 151 mStream->read(&fmtHdr.format); 152 mStream->read(&fmtHdr.channels); 153 mStream->read(&fmtHdr.samplesPerSec); 154 mStream->read(&fmtHdr.bytesPerSec); 155 mStream->read(&fmtHdr.blockAlign); 156 mStream->read(&fmtHdr.bitsPerSample); 157 158 if ( fmtHdr.format == 0x0001 ) 159 { 160 mFormat.set( fmtHdr.channels, fmtHdr.bitsPerSample * fmtHdr.channels, fmtHdr.samplesPerSec ); 161 chunkRemaining -= sizeof( WAVFmtHdr ); 162 } 163 else 164 { 165 mStream->read(sizeof(WAVFmtExHdr), &fmtExHdr); 166 chunkRemaining -= sizeof(WAVFmtExHdr); 167 } 168 } 169 170 // WAV data chunk 171 else if (!dStrncmp((const char*)chunkHdr.id,"data",4)) 172 { 173 // TODO: Handle these other formats in a more graceful manner! 174 175 if (fmtHdr.format==0x0001) 176 { 177 mDataStart = mStream->getPosition(); 178 mStream->setPosition( mDataStart + chunkHdr.size ); 179 chunkRemaining -= chunkHdr.size; 180 mSamples = chunkHdr.size / mFormat.getBytesPerSample(); 181 } 182 else if (fmtHdr.format==0x0011) 183 { 184 //IMA ADPCM 185 } 186 else if (fmtHdr.format==0x0055) 187 { 188 //MP3 WAVE 189 } 190 } 191 192 // WAV sample header 193 else if (!dStrncmp((const char*)chunkHdr.id,"smpl",4)) 194 { 195 // this struct read is NOT endian safe but it is ok because 196 // we are only testing the loops field against ZERO 197 mStream->read(sizeof(WAVSmplHdr), &smplHdr); 198 199 // This has never been hooked up and its usefulness is 200 // dubious. Do we really want the audio file overriding 201 // the SFXDescription setting? 202 //mLooping = ( smplHdr.loops ? true : false ); 203 204 chunkRemaining -= sizeof(WAVSmplHdr); 205 } 206 207 // either we have unread chunk data or we found an unknown chunk type 208 // loop and read up to 1K bytes at a time until we have 209 // read to the end of this chunk 210 AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero."); 211 if ( chunkRemaining > 0 ) 212 { 213 U32 pos = mStream->getPosition(); 214 mStream->setPosition( pos + chunkRemaining ); 215 chunkRemaining = 0; 216 } 217 218 fileHdr.size-=(((chunkHdr.size+1)&~1)+8); 219 220 // read next chunk header... 221 mStream->read(4, &chunkHdr.id[0]); 222 mStream->read(&chunkHdr.size); 223 // unread chunk data rounded up to nearest WORD 224 chunkRemaining = chunkHdr.size + (chunkHdr.size&1); 225 } 226 227 return ( mDataStart != -1 ); 228} 229 230void SFXWavStream::reset() 231{ 232 AssertFatal( mStream, "SFXWavStream::reset() - Stream is null!" ); 233 AssertFatal( mDataStart != -1, "SFXWavStream::seek() - Data start offset is invalid!" ); 234 mStream->setPosition( mDataStart ); 235} 236 237U32 SFXWavStream::getPosition() const 238{ 239 AssertFatal( mStream, "SFXWavStream::getPosition() - Stream is null!" ); 240 return ( mStream->getPosition() - mDataStart ); 241} 242 243void SFXWavStream::setPosition( U32 offset ) 244{ 245 AssertFatal( mStream, "SFXWavStream::setPosition() - Stream is null!" ); 246 247 offset -= offset % mFormat.getBytesPerSample(); 248 const U32 dataLength = mSamples * mFormat.getBytesPerSample(); 249 if( offset > dataLength ) 250 offset = dataLength; 251 252 AssertFatal( mDataStart != -1, "SFXWavStream::getPosition() - Data start offset is invalid!" ); 253 254 U32 byte = mDataStart + offset; 255 256 mStream->setPosition( byte ); 257} 258 259U32 SFXWavStream::read( U8 *buffer, U32 bytes ) 260{ 261 AssertFatal( mStream, "SFXWavStream::seek() - Stream is null!" ); 262 263 // Read in even sample chunks. 264 265 bytes -= bytes % mFormat.getBytesPerSample(); 266 267 // Read the data and determine how much we've read. 268 // FileStreams apparently report positions past 269 // the actual stream length, so manually cap the 270 // numbers here. 271 272 const U32 oldPosition = mStream->getPosition(); 273 mStream->read( bytes, buffer ); 274 U32 newPosition = mStream->getPosition(); 275 const U32 maxPosition = getDataLength() + mDataStart; 276 if( newPosition > maxPosition ) 277 newPosition = maxPosition; 278 279 const U32 numBytesRead = newPosition - oldPosition; 280 281 // TODO: Is it *just* 16 bit samples that needs to 282 // be flipped? What about 32 bit samples? 283 #ifdef TORQUE_BIG_ENDIAN 284 285 // We need to endian-flip 16-bit data. 286 if ( getFormat().getBytesPerChannel() == 2 ) 287 { 288 U16 *ds = (U16*)buffer; 289 U16 *de = (U16*)(buffer+bytes); 290 while (ds<de) 291 { 292 *ds = convertLEndianToHost(*ds); 293 ds++; 294 } 295 } 296 297 #endif 298 299 return numBytesRead; 300} 301
