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{
 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