481 lines
12 KiB
C++
481 lines
12 KiB
C++
|
|
#include <windows.h>
|
|
#include <algorithm>
|
|
|
|
#include "../../include/sound/XWave.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: RIFFHEADER
|
|
// Desc: For parsing WAV files
|
|
//-----------------------------------------------------------------------------
|
|
struct RIFFHEADER
|
|
{
|
|
FOURCC fccChunkId;
|
|
DWORD dwDataSize;
|
|
};
|
|
|
|
#define RIFFCHUNK_FLAGS_VALID 0x00000001
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: class CRiffChunk
|
|
// Desc: RIFF chunk utility class
|
|
//-----------------------------------------------------------------------------
|
|
class CRiffChunk
|
|
{
|
|
FOURCC m_fccChunkId; // Chunk identifier
|
|
const CRiffChunk* m_pParentChunk; // Parent chunk
|
|
XStreamReader* m_pStreamReader;
|
|
DWORD m_dwDataOffset; // Chunk data offset
|
|
DWORD m_dwDataSize; // Chunk data size
|
|
DWORD m_dwFlags; // Chunk flags
|
|
|
|
public:
|
|
CRiffChunk();
|
|
|
|
// Initialization
|
|
VOID Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk,
|
|
XStreamReader *pStreamReader );
|
|
HRESULT Open();
|
|
BOOL IsValid() { return !!(m_dwFlags & RIFFCHUNK_FLAGS_VALID); }
|
|
|
|
// Data
|
|
HRESULT ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize );
|
|
|
|
// Chunk information
|
|
FOURCC GetChunkId() { return m_fccChunkId; }
|
|
DWORD GetDataSize() { return m_dwDataSize; }
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: class CWaveFile
|
|
// Desc: Wave file utility class
|
|
//-----------------------------------------------------------------------------
|
|
class CWaveFile
|
|
{
|
|
XStreamReader *m_pStreamReader; // File handle
|
|
CRiffChunk m_RiffChunk; // RIFF chunk
|
|
CRiffChunk m_FormatChunk; // Format chunk
|
|
CRiffChunk m_DataChunk; // Data chunk
|
|
CRiffChunk m_WaveSampleChunk; // Wave Sample chunk
|
|
|
|
public:
|
|
CWaveFile();
|
|
~CWaveFile();
|
|
|
|
// Initialization
|
|
HRESULT Open( XStreamReader *pStreamReader );
|
|
VOID Close();
|
|
|
|
// File format
|
|
HRESULT GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize, DWORD *pdwRequiredSize = NULL );
|
|
|
|
// File data
|
|
HRESULT ReadSample( DWORD dwPosition, VOID* pBuffer, DWORD dwBufferSize,
|
|
DWORD* pdwRead );
|
|
|
|
// Loop region
|
|
HRESULT GetLoopRegion( DWORD* pdwStart, DWORD* pdwLength );
|
|
|
|
// File properties
|
|
VOID GetDuration( DWORD* pdwDuration ) { *pdwDuration = m_DataChunk.GetDataSize(); }
|
|
};
|
|
|
|
|
|
XWave::XWave( XStreamReader *pStreamReader )
|
|
{
|
|
m_pStream = pStreamReader;
|
|
m_bIsMyStream = false;
|
|
|
|
init();
|
|
}
|
|
|
|
XWave::XWave( const char* szFileName )
|
|
{
|
|
m_pStream = new XFileStreamReader( szFileName );
|
|
m_bIsMyStream = true;
|
|
|
|
init();
|
|
}
|
|
|
|
void XWave::init()
|
|
{
|
|
memset( &m_WaveFormat, 0, sizeof(m_WaveFormat) );
|
|
m_unSize = 0;
|
|
m_pWaveFile = new CWaveFile();
|
|
}
|
|
|
|
|
|
XWave::~XWave()
|
|
{
|
|
if( m_pWaveFile ) delete m_pWaveFile;
|
|
if( m_bIsMyStream ) delete m_pStream;
|
|
}
|
|
|
|
bool XWave::ReadWaveInfo()
|
|
{
|
|
if( FAILED( m_pWaveFile->Open( m_pStream ) ) ) return false;
|
|
|
|
if( FAILED( m_pWaveFile->GetFormat( &m_WaveFormat, sizeof(m_WaveFormat) ) ) ) return false;
|
|
|
|
DWORD duration = 0;
|
|
m_pWaveFile->GetDuration( &duration );
|
|
m_unSize = duration;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool XWave::ReadWaveData( void * p, unsigned len )
|
|
{
|
|
DWORD read = 0;
|
|
if( FAILED( m_pWaveFile->ReadSample( 0, p, len, &read ) ) ) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool XWave::IsStereo()
|
|
{
|
|
return ( m_WaveFormat.nChannels > 1 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DWORD FOURCC_WAVE = 'EVAW';
|
|
const DWORD FOURCC_FORMAT = ' tmf';
|
|
const DWORD FOURCC_DATA = 'atad';
|
|
const DWORD FOURCC_WSMP = 'pmsw';
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: struct WAVESAMPE
|
|
// Desc: RIFF chunk type that contains loop point information
|
|
//-----------------------------------------------------------------------------
|
|
struct WAVESAMPLE
|
|
{
|
|
ULONG cbSize;
|
|
USHORT usUnityNote;
|
|
SHORT sFineTune;
|
|
LONG lGain;
|
|
ULONG ulOptions;
|
|
ULONG cSampleLoops;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: struct WAVESAMPLE_LOOP
|
|
// Desc: Loop point (contained in WSMP chunk)
|
|
//-----------------------------------------------------------------------------
|
|
struct WAVESAMPLE_LOOP
|
|
{
|
|
ULONG cbSize;
|
|
ULONG ulLoopType;
|
|
ULONG ulLoopStart;
|
|
ULONG ulLoopLength;
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CRiffChunk()
|
|
// Desc: Object constructor.
|
|
//-----------------------------------------------------------------------------
|
|
CRiffChunk::CRiffChunk()
|
|
{
|
|
// Initialize defaults
|
|
m_fccChunkId = 0;
|
|
m_pParentChunk = NULL;
|
|
m_pStreamReader= NULL;
|
|
m_dwDataOffset = 0;
|
|
m_dwDataSize = 0;
|
|
m_dwFlags = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Initialize()
|
|
// Desc: Initializes the object
|
|
//-----------------------------------------------------------------------------
|
|
VOID CRiffChunk::Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk,
|
|
XStreamReader *pStreamReader )
|
|
{
|
|
m_fccChunkId = fccChunkId;
|
|
m_pParentChunk = pParentChunk;
|
|
m_pStreamReader= pStreamReader;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Open()
|
|
// Desc: Opens an existing chunk.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CRiffChunk::Open()
|
|
{
|
|
if( !m_pStreamReader ) return S_FALSE;
|
|
|
|
RIFFHEADER rhRiffHeader;
|
|
LONG lOffset = 0;
|
|
|
|
// Seek to the first byte of the parent chunk's data section
|
|
if( m_pParentChunk )
|
|
{
|
|
lOffset = m_pParentChunk->m_dwDataOffset;
|
|
|
|
// Special case the RIFF chunk
|
|
if( FOURCC_RIFF == m_pParentChunk->m_fccChunkId )
|
|
lOffset += sizeof(FOURCC);
|
|
}
|
|
|
|
// Read each child chunk header until we find the one we're looking for
|
|
for( ;; )
|
|
{
|
|
if( !m_pStreamReader->Seek( lOffset, SEEK_SET ) ) return E_FAIL;
|
|
|
|
size_t dwRead = m_pStreamReader->Read( &rhRiffHeader, sizeof(rhRiffHeader) );
|
|
if( dwRead != sizeof(rhRiffHeader) )
|
|
return E_FAIL;
|
|
|
|
// Hit EOF without finding it
|
|
if( 0 == dwRead )
|
|
return E_FAIL;
|
|
|
|
// Check if we found the one we're looking for
|
|
if( m_fccChunkId == rhRiffHeader.fccChunkId )
|
|
{
|
|
// Save the chunk size and data offset
|
|
m_dwDataOffset = lOffset + sizeof(rhRiffHeader);
|
|
m_dwDataSize = rhRiffHeader.dwDataSize;
|
|
|
|
// Success
|
|
m_dwFlags |= RIFFCHUNK_FLAGS_VALID;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
lOffset += sizeof(rhRiffHeader) + rhRiffHeader.dwDataSize;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Read()
|
|
// Desc: Reads from the file
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CRiffChunk::ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize )
|
|
{
|
|
if( !m_pStreamReader ) return S_FALSE;
|
|
|
|
// Seek to the offset
|
|
if( !m_pStreamReader->Seek( m_dwDataOffset+lOffset, SEEK_SET ) ) return E_FAIL;
|
|
|
|
// Read from the file
|
|
size_t dwRead = m_pStreamReader->Read( pData, dwDataSize );
|
|
if( dwRead != dwDataSize ) return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CWaveFile()
|
|
// Desc: Object constructor.
|
|
//-----------------------------------------------------------------------------
|
|
CWaveFile::CWaveFile()
|
|
{
|
|
m_pStreamReader = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~CWaveFile()
|
|
// Desc: Object destructor.
|
|
//-----------------------------------------------------------------------------
|
|
CWaveFile::~CWaveFile()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Open()
|
|
// Desc: Initializes the object.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CWaveFile::Open( XStreamReader *pStreamReader )
|
|
{
|
|
// If we're already open, close
|
|
Close();
|
|
|
|
// Open the file
|
|
m_pStreamReader = pStreamReader;
|
|
if( !m_pStreamReader ) return S_FALSE;
|
|
|
|
// Initialize the chunk objects
|
|
m_RiffChunk.Initialize( FOURCC_RIFF, NULL, m_pStreamReader );
|
|
m_FormatChunk.Initialize( FOURCC_FORMAT, &m_RiffChunk, m_pStreamReader );
|
|
m_DataChunk.Initialize( FOURCC_DATA, &m_RiffChunk, m_pStreamReader );
|
|
m_WaveSampleChunk.Initialize( FOURCC_WSMP, &m_RiffChunk, m_pStreamReader );
|
|
|
|
HRESULT hr = m_RiffChunk.Open();
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
hr = m_FormatChunk.Open();
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
hr = m_DataChunk.Open();
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
// Wave Sample chunk is not required
|
|
m_WaveSampleChunk.Open();
|
|
|
|
// Validate the file type
|
|
FOURCC fccType;
|
|
hr = m_RiffChunk.ReadData( 0, &fccType, sizeof(fccType) );
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
if( FOURCC_WAVE != fccType )
|
|
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: GetFormat()
|
|
// Desc: Gets the wave file format
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CWaveFile::GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize, DWORD * pdwRequiredSize )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwValidSize = m_FormatChunk.GetDataSize();
|
|
|
|
// We should be reading a wave format and/or
|
|
// telling the caller the size of the format
|
|
if( ( NULL == pwfxFormat ||
|
|
0 == dwFormatSize ) &&
|
|
NULL == pdwRequiredSize )
|
|
return E_INVALIDARG;
|
|
|
|
if( pwfxFormat && dwFormatSize )
|
|
{
|
|
// Read the format chunk into the buffer
|
|
hr = m_FormatChunk.ReadData( 0, pwfxFormat, (std::min)(dwFormatSize, dwValidSize) );
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
// Zero out remaining bytes, in case enough bytes were not read
|
|
if( dwFormatSize > dwValidSize )
|
|
ZeroMemory( (BYTE*)pwfxFormat + dwValidSize, dwFormatSize - dwValidSize );
|
|
}
|
|
|
|
// Tell caller how much space they need for the format
|
|
if( pdwRequiredSize )
|
|
{
|
|
*pdwRequiredSize = (std::max)( dwValidSize, (DWORD)sizeof( *pwfxFormat ) );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ReadSample()
|
|
// Desc: Reads data from the audio file.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CWaveFile::ReadSample( DWORD dwPosition, VOID* pBuffer,
|
|
DWORD dwBufferSize, DWORD* pdwRead )
|
|
{
|
|
// Don't read past the end of the data chunk
|
|
DWORD dwDuration;
|
|
GetDuration( &dwDuration );
|
|
|
|
if( dwPosition + dwBufferSize > dwDuration )
|
|
dwBufferSize = dwDuration - dwPosition;
|
|
|
|
HRESULT hr = S_OK;
|
|
if( dwBufferSize )
|
|
hr = m_DataChunk.ReadData( (LONG)dwPosition, pBuffer, dwBufferSize );
|
|
|
|
if( pdwRead )
|
|
*pdwRead = dwBufferSize;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: GetLoopRegion
|
|
// Desc: Gets the loop region, in terms of samples
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CWaveFile::GetLoopRegion( DWORD *pdwStart, DWORD *pdwLength )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WAVESAMPLE ws;
|
|
WAVESAMPLE_LOOP wsl;
|
|
|
|
// Check arguments
|
|
if( NULL == pdwStart || NULL == pdwLength )
|
|
return E_INVALIDARG;
|
|
|
|
// Check to see if there was a wave sample chunk
|
|
if( !m_WaveSampleChunk.IsValid() )
|
|
return E_FAIL;
|
|
|
|
// Read the WAVESAMPLE struct from the chunk
|
|
hr = m_WaveSampleChunk.ReadData( 0, &ws, sizeof( ws ) );
|
|
if( FAILED( hr ) )
|
|
return hr;
|
|
|
|
// Currently, only 1 loop region is supported
|
|
if( ws.cSampleLoops != 1 )
|
|
return E_FAIL;
|
|
|
|
// Read the loop region
|
|
hr = m_WaveSampleChunk.ReadData( ws.cbSize, &wsl, sizeof( wsl ) );
|
|
if( FAILED( hr ) )
|
|
return hr;
|
|
|
|
// Fill output vars with the loop region
|
|
*pdwStart = wsl.ulLoopStart;
|
|
*pdwLength = wsl.ulLoopLength;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Close()
|
|
// Desc: Closes the object
|
|
//-----------------------------------------------------------------------------
|
|
VOID CWaveFile::Close()
|
|
{
|
|
}
|