1 //-----------------------------------------------------------------------------
\r
2 // File: SDKWaveFile.cpp
\r
4 // Desc: Classes for reading and writing wav files. Feel free to use this class
\r
5 // as a starting point for adding extra functionality.
\r
7 // XNA Developer Connection
\r
9 // Copyright (c) Microsoft Corp. All rights reserved.
\r
10 //-----------------------------------------------------------------------------
\r
13 #include "SDKwavefile.h"
\r
14 #undef min // use __min instead
\r
15 #undef max // use __max instead
\r
17 //-----------------------------------------------------------------------------
\r
18 // Name: CWaveFile::CWaveFile()
\r
19 // Desc: Constructs the class. Call Open() to open a wave file for reading.
\r
20 // Then call Read() as needed. Calling the destructor or Close()
\r
21 // will close the file.
\r
22 //-----------------------------------------------------------------------------
\r
23 CWaveFile::CWaveFile()
\r
27 m_pResourceBuffer = NULL;
\r
29 m_bIsReadingFromMemory = FALSE;
\r
33 //-----------------------------------------------------------------------------
\r
34 // Name: CWaveFile::~CWaveFile()
\r
35 // Desc: Destructs the class
\r
36 //-----------------------------------------------------------------------------
\r
37 CWaveFile::~CWaveFile()
\r
41 if( !m_bIsReadingFromMemory )
\r
42 SAFE_DELETE_ARRAY( m_pwfx );
\r
46 //-----------------------------------------------------------------------------
\r
47 // Name: CWaveFile::Open()
\r
48 // Desc: Opens a wave file for reading
\r
49 //-----------------------------------------------------------------------------
\r
50 HRESULT CWaveFile::Open( LPWSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
\r
54 m_dwFlags = dwFlags;
\r
55 m_bIsReadingFromMemory = FALSE;
\r
57 if( m_dwFlags == WAVEFILE_READ )
\r
59 if( strFileName == NULL )
\r
60 return E_INVALIDARG;
\r
61 SAFE_DELETE_ARRAY( m_pwfx );
\r
63 m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
\r
65 if( NULL == m_hmmio )
\r
72 // Loading it as a file failed, so try it as a resource
\r
73 if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAVE" ) ) )
\r
75 if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAV" ) ) )
\r
76 return DXTRACE_ERR( L"FindResource", E_FAIL );
\r
79 if( NULL == ( hResData = LoadResource( GetModuleHandle( NULL ), hResInfo ) ) )
\r
80 return DXTRACE_ERR( L"LoadResource", E_FAIL );
\r
82 if( 0 == ( dwSize = SizeofResource( GetModuleHandle( NULL ), hResInfo ) ) )
\r
83 return DXTRACE_ERR( L"SizeofResource", E_FAIL );
\r
85 if( NULL == ( pvRes = LockResource( hResData ) ) )
\r
86 return DXTRACE_ERR( L"LockResource", E_FAIL );
\r
88 m_pResourceBuffer = new CHAR[ dwSize ];
\r
89 if( m_pResourceBuffer == NULL )
\r
90 return DXTRACE_ERR( L"new", E_OUTOFMEMORY );
\r
91 memcpy( m_pResourceBuffer, pvRes, dwSize );
\r
94 ZeroMemory( &mmioInfo, sizeof( mmioInfo ) );
\r
95 mmioInfo.fccIOProc = FOURCC_MEM;
\r
96 mmioInfo.cchBuffer = dwSize;
\r
97 mmioInfo.pchBuffer = ( CHAR* )m_pResourceBuffer;
\r
99 m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
\r
102 if( FAILED( hr = ReadMMIO() ) )
\r
104 // ReadMMIO will fail if its an not a wave file
\r
105 mmioClose( m_hmmio, 0 );
\r
106 return DXTRACE_ERR( L"ReadMMIO", hr );
\r
109 if( FAILED( hr = ResetFile() ) )
\r
110 return DXTRACE_ERR( L"ResetFile", hr );
\r
112 // After the reset, the size of the wav file is m_ck.cksize so store it now
\r
113 m_dwSize = m_ck.cksize;
\r
117 m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF |
\r
120 if( NULL == m_hmmio )
\r
121 return DXTRACE_ERR( L"mmioOpen", E_FAIL );
\r
123 if( FAILED( hr = WriteMMIO( pwfx ) ) )
\r
125 mmioClose( m_hmmio, 0 );
\r
126 return DXTRACE_ERR( L"WriteMMIO", hr );
\r
129 if( FAILED( hr = ResetFile() ) )
\r
130 return DXTRACE_ERR( L"ResetFile", hr );
\r
137 //-----------------------------------------------------------------------------
\r
138 // Name: CWaveFile::OpenFromMemory()
\r
139 // Desc: copy data to CWaveFile member variable from memory
\r
140 //-----------------------------------------------------------------------------
\r
141 HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize,
\r
142 WAVEFORMATEX* pwfx, DWORD dwFlags )
\r
145 m_ulDataSize = ulDataSize;
\r
147 m_pbDataCur = m_pbData;
\r
148 m_bIsReadingFromMemory = TRUE;
\r
150 if( dwFlags != WAVEFILE_READ )
\r
157 //-----------------------------------------------------------------------------
\r
158 // Name: CWaveFile::ReadMMIO()
\r
159 // Desc: Support function for reading from a multimedia I/O stream.
\r
160 // m_hmmio must be valid before calling. This function uses it to
\r
161 // update m_ckRiff, and m_pwfx.
\r
162 //-----------------------------------------------------------------------------
\r
163 HRESULT CWaveFile::ReadMMIO()
\r
165 MMCKINFO ckIn; // chunk info. for general use.
\r
166 PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in.
\r
170 if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
\r
171 return DXTRACE_ERR( L"mmioDescend", E_FAIL );
\r
173 // Check to make sure this is a valid wave file
\r
174 if( ( m_ckRiff.ckid != FOURCC_RIFF ) ||
\r
175 ( m_ckRiff.fccType != mmioFOURCC( 'W', 'A', 'V', 'E' ) ) )
\r
176 return DXTRACE_ERR( L"mmioFOURCC", E_FAIL );
\r
178 // Search the input file for for the 'fmt ' chunk.
\r
179 ckIn.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
\r
180 if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
\r
181 return DXTRACE_ERR( L"mmioDescend", E_FAIL );
\r
183 // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
\r
184 // if there are extra parameters at the end, we'll ignore them
\r
185 if( ckIn.cksize < ( LONG )sizeof( PCMWAVEFORMAT ) )
\r
186 return DXTRACE_ERR( L"sizeof(PCMWAVEFORMAT)", E_FAIL );
\r
188 // Read the 'fmt ' chunk into <pcmWaveFormat>.
\r
189 if( mmioRead( m_hmmio, ( HPSTR )&pcmWaveFormat,
\r
190 sizeof( pcmWaveFormat ) ) != sizeof( pcmWaveFormat ) )
\r
191 return DXTRACE_ERR( L"mmioRead", E_FAIL );
\r
193 // Allocate the waveformatex, but if its not pcm format, read the next
\r
194 // word, and thats how many extra bytes to allocate.
\r
195 if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
\r
197 m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) ];
\r
198 if( NULL == m_pwfx )
\r
199 return DXTRACE_ERR( L"m_pwfx", E_FAIL );
\r
201 // Copy the bytes from the pcm structure to the waveformatex structure
\r
202 memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
\r
203 m_pwfx->cbSize = 0;
\r
207 // Read in length of extra bytes.
\r
208 WORD cbExtraBytes = 0L;
\r
209 if( mmioRead( m_hmmio, ( CHAR* )&cbExtraBytes, sizeof( WORD ) ) != sizeof( WORD ) )
\r
210 return DXTRACE_ERR( L"mmioRead", E_FAIL );
\r
212 m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) + cbExtraBytes ];
\r
213 if( NULL == m_pwfx )
\r
214 return DXTRACE_ERR( L"new", E_FAIL );
\r
216 // Copy the bytes from the pcm structure to the waveformatex structure
\r
217 memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
\r
218 m_pwfx->cbSize = cbExtraBytes;
\r
220 // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
\r
221 if( mmioRead( m_hmmio, ( CHAR* )( ( ( BYTE* )&( m_pwfx->cbSize ) ) + sizeof( WORD ) ),
\r
222 cbExtraBytes ) != cbExtraBytes )
\r
224 SAFE_DELETE( m_pwfx );
\r
225 return DXTRACE_ERR( L"mmioRead", E_FAIL );
\r
229 // Ascend the input file out of the 'fmt ' chunk.
\r
230 if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
\r
232 SAFE_DELETE( m_pwfx );
\r
233 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
240 //-----------------------------------------------------------------------------
\r
241 // Name: CWaveFile::GetSize()
\r
242 // Desc: Retuns the size of the read access wave file
\r
243 //-----------------------------------------------------------------------------
\r
244 DWORD CWaveFile::GetSize()
\r
250 //-----------------------------------------------------------------------------
\r
251 // Name: CWaveFile::ResetFile()
\r
252 // Desc: Resets the internal m_ck pointer so reading starts from the
\r
253 // beginning of the file again
\r
254 //-----------------------------------------------------------------------------
\r
255 HRESULT CWaveFile::ResetFile()
\r
257 if( m_bIsReadingFromMemory )
\r
259 m_pbDataCur = m_pbData;
\r
263 if( m_hmmio == NULL )
\r
264 return CO_E_NOTINITIALIZED;
\r
266 if( m_dwFlags == WAVEFILE_READ )
\r
268 // Seek to the data
\r
269 if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof( FOURCC ),
\r
271 return DXTRACE_ERR( L"mmioSeek", E_FAIL );
\r
273 // Search the input file for the 'data' chunk.
\r
274 m_ck.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
\r
275 if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
\r
276 return DXTRACE_ERR( L"mmioDescend", E_FAIL );
\r
280 // Create the 'data' chunk that holds the waveform samples.
\r
281 m_ck.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
\r
284 if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
\r
285 return DXTRACE_ERR( L"mmioCreateChunk", E_FAIL );
\r
287 if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
\r
288 return DXTRACE_ERR( L"mmioGetInfo", E_FAIL );
\r
296 //-----------------------------------------------------------------------------
\r
297 // Name: CWaveFile::Read()
\r
298 // Desc: Reads section of data from a wave file into pBuffer and returns
\r
299 // how much read in pdwSizeRead, reading not more than dwSizeToRead.
\r
300 // This uses m_ck to determine where to start reading from. So
\r
301 // subsequent calls will be continue where the last left off unless
\r
302 // Reset() is called.
\r
303 //-----------------------------------------------------------------------------
\r
304 HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
\r
306 if( m_bIsReadingFromMemory )
\r
308 if( m_pbDataCur == NULL )
\r
309 return CO_E_NOTINITIALIZED;
\r
310 if( pdwSizeRead != NULL )
\r
313 if( ( BYTE* )( m_pbDataCur + dwSizeToRead ) >
\r
314 ( BYTE* )( m_pbData + m_ulDataSize ) )
\r
316 dwSizeToRead = m_ulDataSize - ( DWORD )( m_pbDataCur - m_pbData );
\r
319 #pragma warning( disable: 4616 ) // disable warning about warning number '22104' being out of range
\r
320 #pragma warning( disable: 22104 ) // disable PREfast warning during static code analysis
\r
321 CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
\r
322 #pragma warning( default: 22104 )
\r
323 #pragma warning( default: 4616 )
\r
325 if( pdwSizeRead != NULL )
\r
326 *pdwSizeRead = dwSizeToRead;
\r
332 MMIOINFO mmioinfoIn; // current status of m_hmmio
\r
334 if( m_hmmio == NULL )
\r
335 return CO_E_NOTINITIALIZED;
\r
336 if( pBuffer == NULL || pdwSizeRead == NULL )
\r
337 return E_INVALIDARG;
\r
341 if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
\r
342 return DXTRACE_ERR( L"mmioGetInfo", E_FAIL );
\r
344 UINT cbDataIn = dwSizeToRead;
\r
345 if( cbDataIn > m_ck.cksize )
\r
346 cbDataIn = m_ck.cksize;
\r
348 m_ck.cksize -= cbDataIn;
\r
350 for( DWORD cT = 0; cT < cbDataIn; cT++ )
\r
352 // Copy the bytes from the io to the buffer.
\r
353 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
\r
355 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
\r
356 return DXTRACE_ERR( L"mmioAdvance", E_FAIL );
\r
358 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
\r
359 return DXTRACE_ERR( L"mmioinfoIn.pchNext", E_FAIL );
\r
363 *( ( BYTE* )pBuffer + cT ) = *( ( BYTE* )mmioinfoIn.pchNext );
\r
364 mmioinfoIn.pchNext++;
\r
367 if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
\r
368 return DXTRACE_ERR( L"mmioSetInfo", E_FAIL );
\r
370 *pdwSizeRead = cbDataIn;
\r
377 //-----------------------------------------------------------------------------
\r
378 // Name: CWaveFile::Close()
\r
379 // Desc: Closes the wave file
\r
380 //-----------------------------------------------------------------------------
\r
381 HRESULT CWaveFile::Close()
\r
383 if( m_dwFlags == WAVEFILE_READ )
\r
385 mmioClose( m_hmmio, 0 );
\r
387 SAFE_DELETE_ARRAY( m_pResourceBuffer );
\r
391 m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
\r
393 if( m_hmmio == NULL )
\r
394 return CO_E_NOTINITIALIZED;
\r
396 if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
\r
397 return DXTRACE_ERR( L"mmioSetInfo", E_FAIL );
\r
399 // Ascend the output file out of the 'data' chunk -- this will cause
\r
400 // the chunk size of the 'data' chunk to be written.
\r
401 if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
\r
402 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
404 // Do this here instead...
\r
405 if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
\r
406 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
408 mmioSeek( m_hmmio, 0, SEEK_SET );
\r
410 if( 0 != ( INT )mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) )
\r
411 return DXTRACE_ERR( L"mmioDescend", E_FAIL );
\r
413 m_ck.ckid = mmioFOURCC( 'f', 'a', 'c', 't' );
\r
415 if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
\r
417 DWORD dwSamples = 0;
\r
418 mmioWrite( m_hmmio, ( HPSTR )&dwSamples, sizeof( DWORD ) );
\r
419 mmioAscend( m_hmmio, &m_ck, 0 );
\r
422 // Ascend the output file out of the 'RIFF' chunk -- this will cause
\r
423 // the chunk size of the 'RIFF' chunk to be written.
\r
424 if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
\r
425 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
427 mmioClose( m_hmmio, 0 );
\r
435 //-----------------------------------------------------------------------------
\r
436 // Name: CWaveFile::WriteMMIO()
\r
437 // Desc: Support function for reading from a multimedia I/O stream
\r
438 // pwfxDest is the WAVEFORMATEX for this new wave file.
\r
439 // m_hmmio must be valid before calling. This function uses it to
\r
440 // update m_ckRiff, and m_ck.
\r
441 //-----------------------------------------------------------------------------
\r
442 HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX* pwfxDest )
\r
444 DWORD dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
\r
447 dwFactChunk = ( DWORD )-1;
\r
449 // Create the output file RIFF chunk of form type 'WAVE'.
\r
450 m_ckRiff.fccType = mmioFOURCC( 'W', 'A', 'V', 'E' );
\r
451 m_ckRiff.cksize = 0;
\r
453 if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) )
\r
454 return DXTRACE_ERR( L"mmioCreateChunk", E_FAIL );
\r
456 // We are now descended into the 'RIFF' chunk we just created.
\r
457 // Now create the 'fmt ' chunk. Since we know the size of this chunk,
\r
458 // specify it in the MMCKINFO structure so MMIO doesn't have to seek
\r
459 // back and set the chunk size after ascending from the chunk.
\r
460 m_ck.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
\r
461 m_ck.cksize = sizeof( PCMWAVEFORMAT );
\r
463 if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
\r
464 return DXTRACE_ERR( L"mmioCreateChunk", E_FAIL );
\r
466 // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type.
\r
467 if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM )
\r
469 if( mmioWrite( m_hmmio, ( HPSTR )pwfxDest,
\r
470 sizeof( PCMWAVEFORMAT ) ) != sizeof( PCMWAVEFORMAT ) )
\r
471 return DXTRACE_ERR( L"mmioWrite", E_FAIL );
\r
475 // Write the variable length size.
\r
476 if( ( UINT )mmioWrite( m_hmmio, ( HPSTR )pwfxDest,
\r
477 sizeof( *pwfxDest ) + pwfxDest->cbSize ) !=
\r
478 ( sizeof( *pwfxDest ) + pwfxDest->cbSize ) )
\r
479 return DXTRACE_ERR( L"mmioWrite", E_FAIL );
\r
482 // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
\r
483 if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
\r
484 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
486 // Now create the fact chunk, not required for PCM but nice to have. This is filled
\r
487 // in when the close routine is called.
\r
488 ckOut1.ckid = mmioFOURCC( 'f', 'a', 'c', 't' );
\r
491 if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) )
\r
492 return DXTRACE_ERR( L"mmioCreateChunk", E_FAIL );
\r
494 if( mmioWrite( m_hmmio, ( HPSTR )&dwFactChunk, sizeof( dwFactChunk ) ) !=
\r
495 sizeof( dwFactChunk ) )
\r
496 return DXTRACE_ERR( L"mmioWrite", E_FAIL );
\r
498 // Now ascend out of the fact chunk...
\r
499 if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) )
\r
500 return DXTRACE_ERR( L"mmioAscend", E_FAIL );
\r
506 //-----------------------------------------------------------------------------
\r
507 // Name: CWaveFile::Write()
\r
508 // Desc: Writes data to the open wave file
\r
509 //-----------------------------------------------------------------------------
\r
510 HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote )
\r
514 if( m_bIsReadingFromMemory )
\r
516 if( m_hmmio == NULL )
\r
517 return CO_E_NOTINITIALIZED;
\r
518 if( pnSizeWrote == NULL || pbSrcData == NULL )
\r
519 return E_INVALIDARG;
\r
523 for( cT = 0; cT < nSizeToWrite; cT++ )
\r
525 if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite )
\r
527 m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
\r
528 if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) )
\r
529 return DXTRACE_ERR( L"mmioAdvance", E_FAIL );
\r
532 *( ( BYTE* )m_mmioinfoOut.pchNext ) = *( ( BYTE* )pbSrcData + cT );
\r
533 ( BYTE* )m_mmioinfoOut.pchNext++;
\r
535 ( *pnSizeWrote )++;
\r