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 :mDataStart(-1) 99{ 100} 101 102SFXWavStream::SFXWavStream( const SFXWavStream& cloneFrom ) 103 : Parent( cloneFrom ), 104 mDataStart( cloneFrom.mDataStart ) 105{ 106} 107 108SFXWavStream::~SFXWavStream() 109{ 110 // We must call close from our own destructor 111 // and not the base class... as it causes a 112 // pure virtual runtime assertion. 113 close(); 114} 115 116void SFXWavStream::_close() 117{ 118 mDataStart = -1; 119} 120 121bool SFXWavStream::_readHeader() 122{ 123 // We read the wav chunks to gather than header info 124 // and find the start and end position of the data chunk. 125 mDataStart = -1; 126 127 WAVFileHdr fileHdr; 128 mStream->read( 4, &fileHdr.id[0] ); 129 mStream->read( &fileHdr.size ); 130 mStream->read( 4, &fileHdr.type[0] ); 131 132 fileHdr.size=((fileHdr.size+1)&~1)-4; 133 134 WAVChunkHdr chunkHdr; 135 mStream->read( 4, &chunkHdr.id[0] ); 136 mStream->read( &chunkHdr.size ); 137 138 // Unread chunk data rounded up to nearest WORD. 139 S32 chunkRemaining = chunkHdr.size + ( chunkHdr.size & 1 ); 140 141 WAVFmtHdr fmtHdr; 142 WAVFmtExHdr fmtExHdr; 143 WAVSmplHdr smplHdr; 144 145 dMemset(&fmtHdr, 0, sizeof(fmtHdr)); 146 147 while ((fileHdr.size!=0) && (mStream->getStatus() != Stream::EOS)) 148 { 149 // WAV format header chunk. 150 if ( !dStrncmp( (const char*)chunkHdr.id, "fmt ", 4 ) ) 151 { 152 mStream->read(&fmtHdr.format); 153 mStream->read(&fmtHdr.channels); 154 mStream->read(&fmtHdr.samplesPerSec); 155 mStream->read(&fmtHdr.bytesPerSec); 156 mStream->read(&fmtHdr.blockAlign); 157 mStream->read(&fmtHdr.bitsPerSample); 158 159 if ( fmtHdr.format == 0x0001 ) 160 { 161 mFormat.set( fmtHdr.channels, fmtHdr.bitsPerSample * fmtHdr.channels, fmtHdr.samplesPerSec ); 162 chunkRemaining -= sizeof( WAVFmtHdr ); 163 } 164 else 165 { 166 mStream->read(sizeof(WAVFmtExHdr), &fmtExHdr); 167 chunkRemaining -= sizeof(WAVFmtExHdr); 168 } 169 } 170 171 // WAV data chunk 172 else if (!dStrncmp((const char*)chunkHdr.id,"data",4)) 173 { 174 // TODO: Handle these other formats in a more graceful manner! 175 176 if (fmtHdr.format==0x0001) 177 { 178 mDataStart = mStream->getPosition(); 179 mStream->setPosition( mDataStart + chunkHdr.size ); 180 chunkRemaining -= chunkHdr.size; 181 mSamples = chunkHdr.size / mFormat.getBytesPerSample(); 182 } 183 else if (fmtHdr.format==0x0011) 184 { 185 //IMA ADPCM 186 } 187 else if (fmtHdr.format==0x0055) 188 { 189 //MP3 WAVE 190 } 191 } 192 193 // WAV sample header 194 else if (!dStrncmp((const char*)chunkHdr.id,"smpl",4)) 195 { 196 // this struct read is NOT endian safe but it is ok because 197 // we are only testing the loops field against ZERO 198 mStream->read(sizeof(WAVSmplHdr), &smplHdr); 199 200 // This has never been hooked up and its usefulness is 201 // dubious. Do we really want the audio file overriding 202 // the SFXDescription setting? 203 //mLooping = ( smplHdr.loops ? true : false ); 204 205 chunkRemaining -= sizeof(WAVSmplHdr); 206 } 207 208 // either we have unread chunk data or we found an unknown chunk type 209 // loop and read up to 1K bytes at a time until we have 210 // read to the end of this chunk 211 AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero."); 212 if ( chunkRemaining > 0 ) 213 { 214 U32 pos = mStream->getPosition(); 215 mStream->setPosition( pos + chunkRemaining ); 216 chunkRemaining = 0; 217 } 218 219 fileHdr.size-=(((chunkHdr.size+1)&~1)+8); 220 221 // read next chunk header... 222 mStream->read(4, &chunkHdr.id[0]); 223 mStream->read(&chunkHdr.size); 224 // unread chunk data rounded up to nearest WORD 225 chunkRemaining = chunkHdr.size + (chunkHdr.size&1); 226 } 227 228 return ( mDataStart != -1 ); 229} 230 231void SFXWavStream::reset() 232{ 233 AssertFatal( mStream, "SFXWavStream::reset() - Stream is null!" ); 234 AssertFatal( mDataStart != -1, "SFXWavStream::seek() - Data start offset is invalid!" ); 235 mStream->setPosition( mDataStart ); 236} 237 238U32 SFXWavStream::getPosition() const 239{ 240 AssertFatal( mStream, "SFXWavStream::getPosition() - Stream is null!" ); 241 return ( mStream->getPosition() - mDataStart ); 242} 243 244void SFXWavStream::setPosition( U32 offset ) 245{ 246 AssertFatal( mStream, "SFXWavStream::setPosition() - Stream is null!" ); 247 248 offset -= offset % mFormat.getBytesPerSample(); 249 const U32 dataLength = mSamples * mFormat.getBytesPerSample(); 250 if( offset > dataLength ) 251 offset = dataLength; 252 253 AssertFatal( mDataStart != -1, "SFXWavStream::getPosition() - Data start offset is invalid!" ); 254 255 U32 byte = mDataStart + offset; 256 257 mStream->setPosition( byte ); 258} 259 260U32 SFXWavStream::read( U8 *buffer, U32 bytes ) 261{ 262 AssertFatal( mStream, "SFXWavStream::seek() - Stream is null!" ); 263 264 // Read in even sample chunks. 265 266 bytes -= bytes % mFormat.getBytesPerSample(); 267 268 // Read the data and determine how much we've read. 269 // FileStreams apparently report positions past 270 // the actual stream length, so manually cap the 271 // numbers here. 272 273 const U32 oldPosition = mStream->getPosition(); 274 mStream->read( bytes, buffer ); 275 U32 newPosition = mStream->getPosition(); 276 const U32 maxPosition = getDataLength() + mDataStart; 277 if( newPosition > maxPosition ) 278 newPosition = maxPosition; 279 280 const U32 numBytesRead = newPosition - oldPosition; 281 282 // TODO: Is it *just* 16 bit samples that needs to 283 // be flipped? What about 32 bit samples? 284 #ifdef TORQUE_BIG_ENDIAN 285 286 // We need to endian-flip 16-bit data. 287 if ( getFormat().getBytesPerChannel() == 2 ) 288 { 289 U16 *ds = (U16*)buffer; 290 U16 *de = (U16*)(buffer+bytes); 291 while (ds<de) 292 { 293 *ds = convertLEndianToHost(*ds); 294 ds++; 295 } 296 } 297 298 #endif 299 300 return numBytesRead; 301} 302