Torque3D Documentation / _generateds / sfxWavStream.cpp

sfxWavStream.cpp

Engine/source/sfx/media/sfxWavStream.cpp

More...

Classes:

class

WAV Chunk-header.

class

WAV File-header.

class

WAV FmtEx-header.

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