607 lines
12 KiB
C++
607 lines
12 KiB
C++
// WAVReader.cpp: implementation of the WAVReader class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "URLMon.h"
|
|
|
|
#include <mmsystem.h>
|
|
#include <mmreg.h>
|
|
#include <msacm.h>
|
|
|
|
#include "..\Header\WavReader.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
WAVReader::WAVReader()
|
|
{
|
|
|
|
// Init data members
|
|
|
|
m_pwfmt = NULL;
|
|
m_hmmio = NULL;
|
|
m_nBlockAlign= 0;
|
|
m_nAvgDataRate = 0;
|
|
m_InDataSize = 0;
|
|
m_OutDataSize = 0;
|
|
m_nBytesPlayed = 0;
|
|
|
|
memset (&m_mmckiRiff, 0, sizeof (MMCKINFO));
|
|
memset (&m_mmckiFmt, 0, sizeof (MMCKINFO));
|
|
memset (&m_mmckiData, 0, sizeof (MMCKINFO));
|
|
|
|
m_Buffer = NULL;
|
|
m_BufferSize = 0;
|
|
m_BufferDataRead = 0;
|
|
|
|
m_InDataCursor = 0;
|
|
|
|
m_IsPcm = FALSE;
|
|
m_pHas = NULL;
|
|
|
|
ZeroMemory(&m_Ash,sizeof(m_Ash));
|
|
m_SubFileMem = NULL;
|
|
}
|
|
|
|
WAVReader::~WAVReader()
|
|
{
|
|
|
|
|
|
// Close file
|
|
if (m_hmmio)
|
|
mmioClose (m_hmmio, 0);
|
|
|
|
if (!m_IsPcm)
|
|
{
|
|
if(m_pHas)
|
|
acmStreamUnprepareHeader(m_pHas, &m_Ash, 0);
|
|
delete [] m_Ash.pbDst;
|
|
delete [] m_Ash.pbSrc;
|
|
if(m_pHas)
|
|
acmStreamClose( m_pHas, 0);
|
|
|
|
}
|
|
else
|
|
delete [] m_Buffer;
|
|
|
|
if (m_pwfmt)
|
|
GlobalFree (m_pwfmt);
|
|
|
|
}
|
|
|
|
CKERROR WAVReader::ReadMemory(void* memory, int size) {
|
|
if(!memory || !size)
|
|
return -1;
|
|
|
|
BOOL fRtn = CK_OK; // assume success
|
|
|
|
MMIOINFO mminfo;
|
|
memset(&mminfo,0,sizeof(MMIOINFO));
|
|
|
|
mminfo.pchBuffer = (char*) memory;
|
|
mminfo.cchBuffer = size;
|
|
mminfo.fccIOProc = FOURCC_MEM;
|
|
|
|
// Open the requested file
|
|
if ((m_hmmio = mmioOpen(NULL, &mminfo, MMIO_ALLOCBUF | MMIO_READ)) == NULL)
|
|
{
|
|
m_mmr = MMIOERR_CANNOTOPEN;
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
return DoMMio();
|
|
|
|
OPEN_ERROR:
|
|
// Handle all errors here
|
|
fRtn = -1;
|
|
if (m_hmmio)
|
|
{
|
|
// Close file
|
|
mmioClose (m_hmmio, 0);
|
|
m_hmmio = NULL;
|
|
}
|
|
|
|
if (m_pwfmt)
|
|
{
|
|
// UNDONE: Change here if using malloc
|
|
// Free memory
|
|
GlobalFree (m_pwfmt);
|
|
m_pwfmt = NULL;
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Opens a file
|
|
CKERROR WAVReader::OpenFile(char *file)
|
|
{
|
|
|
|
WORD cbExtra = 0;
|
|
|
|
BOOL fRtn = CK_OK; // assume success
|
|
|
|
char* CacheName= file;
|
|
/* [MAX_PATH];
|
|
|
|
|
|
if(VxURLDownloadToCacheFile(NULL,file,CacheName,_MAX_PATH,0,&wavbsc) != S_OK)
|
|
return -1;
|
|
*/
|
|
if (!CacheName) return -1;
|
|
if(strlen(CacheName) == 0)
|
|
return -1;
|
|
|
|
// Open the requested file
|
|
if ((m_hmmio = mmioOpen (CacheName, NULL, MMIO_ALLOCBUF | MMIO_READ)) == NULL)
|
|
{
|
|
m_mmr = MMIOERR_CANNOTOPEN;
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
return DoMMio();
|
|
|
|
OPEN_ERROR:
|
|
// Handle all errors here
|
|
fRtn = -1;
|
|
if (m_hmmio)
|
|
{
|
|
// Close file
|
|
mmioClose (m_hmmio, 0);
|
|
m_hmmio = NULL;
|
|
}
|
|
|
|
if (m_pwfmt)
|
|
{
|
|
// UNDONE: Change here if using malloc
|
|
// Free memory
|
|
GlobalFree (m_pwfmt);
|
|
m_pwfmt = NULL;
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
CKERROR WAVReader::DoMMio(){
|
|
WORD cbExtra = 0;
|
|
BOOL fRtn = CK_OK; // assume success
|
|
|
|
// Descend into initial chunk ('RIFF')
|
|
if (m_mmr = mmioDescend (m_hmmio, &m_mmckiRiff, NULL, 0))
|
|
{
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
// Validate that it's a WAVE file
|
|
if ((m_mmckiRiff.ckid != FOURCC_RIFF) || (m_mmckiRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
|
|
{
|
|
m_mmr = MMIOERR_INVALIDFILE;
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
// Find format chunk ('fmt '), allocate and fill WAVEFORMATEX structure
|
|
m_mmckiFmt.ckid = mmioFOURCC('f', 'm', 't', ' ');
|
|
if (m_mmr = mmioDescend (m_hmmio, &m_mmckiFmt, &m_mmckiRiff, MMIO_FINDCHUNK))
|
|
{
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
// Read the format chunk into temporary structure
|
|
PCMWAVEFORMAT pcmwf;
|
|
if (mmioRead (m_hmmio, (CHAR *) &pcmwf, sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
|
|
{
|
|
m_mmr = MMIOERR_CANNOTREAD;
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
// If format is not PCM, then there are extra bytes appended to WAVEFORMATEX
|
|
if (pcmwf.wf.wFormatTag != WAVE_FORMAT_PCM)
|
|
{
|
|
// Read WORD specifying number of extra bytes
|
|
if (mmioRead (m_hmmio, (LPSTR) &cbExtra, sizeof (cbExtra)) != sizeof(cbExtra))
|
|
{
|
|
m_mmr = MMIOERR_CANNOTREAD;
|
|
goto OPEN_ERROR;
|
|
}
|
|
}
|
|
|
|
// Allocate memory for WAVEFORMATEX structure + extra bytes
|
|
// UNDONE: GMEM_FIXED???? use malloc?
|
|
if (m_pwfmt = (WAVEFORMATEX *) GlobalAlloc (GMEM_FIXED, sizeof(WAVEFORMATEX)+cbExtra))
|
|
{
|
|
// Copy bytes from temporary format structure
|
|
memcpy (m_pwfmt, &pcmwf, sizeof(pcmwf));
|
|
m_pwfmt->cbSize = cbExtra;
|
|
|
|
// Read those extra bytes, append to WAVEFORMATEX structure
|
|
if (cbExtra != 0)
|
|
{
|
|
if ((m_mmr = mmioRead (m_hmmio, (LPSTR) ((BYTE *)(m_pwfmt) + sizeof (WAVEFORMATEX)), cbExtra)) != cbExtra)
|
|
{
|
|
// Error reading extra bytes
|
|
m_mmr = MMIOERR_CANNOTREAD;
|
|
goto OPEN_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Error allocating memory
|
|
m_mmr = MMIOERR_OUTOFMEMORY;
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
|
|
// Init some member data from format chunk
|
|
m_nBlockAlign = m_pwfmt->nBlockAlign;
|
|
m_nAvgDataRate = m_pwfmt->nAvgBytesPerSec;
|
|
|
|
// Ascend out of format chunk
|
|
if (m_mmr = mmioAscend (m_hmmio, &m_mmckiFmt, 0))
|
|
{
|
|
goto OPEN_ERROR;
|
|
}
|
|
|
|
// Cue for streaming
|
|
Cue();
|
|
|
|
// Init some member data from data chunk
|
|
m_InDataSize = m_mmckiData.cksize;
|
|
|
|
// Successful open!
|
|
goto OPEN_DONE;
|
|
|
|
OPEN_ERROR:
|
|
// Handle all errors here
|
|
fRtn = -1;
|
|
if (m_hmmio)
|
|
{
|
|
// Close file
|
|
mmioClose (m_hmmio, 0);
|
|
m_hmmio = NULL;
|
|
}
|
|
|
|
if (m_pwfmt)
|
|
{
|
|
// UNDONE: Change here if using malloc
|
|
// Free memory
|
|
GlobalFree (m_pwfmt);
|
|
m_pwfmt = NULL;
|
|
}
|
|
|
|
return -1;
|
|
|
|
OPEN_DONE:
|
|
|
|
// Create Decode Buffer
|
|
|
|
m_IsPcm = (m_pwfmt->wFormatTag == WAVE_FORMAT_PCM);
|
|
|
|
if(m_IsPcm)
|
|
{
|
|
m_BufferSize = min(m_pwfmt->nAvgBytesPerSec / 8, m_InDataSize);
|
|
m_Buffer = new BYTE[m_BufferSize ];
|
|
|
|
m_Wfe = *m_pwfmt;
|
|
|
|
m_OutDataSize = m_InDataSize; // Equal if PCM
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
m_Wfe.cbSize = 0;
|
|
m_Wfe.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
m_Wfe.nChannels = m_pwfmt->nChannels;
|
|
m_Wfe.nSamplesPerSec = m_pwfmt->nSamplesPerSec;
|
|
m_Wfe.wBitsPerSample = 16;
|
|
|
|
m_Wfe.nBlockAlign = m_Wfe.nChannels * m_Wfe.wBitsPerSample / 8;
|
|
m_Wfe.nAvgBytesPerSec = m_Wfe.nSamplesPerSec * m_Wfe.nBlockAlign;
|
|
|
|
HRESULT res = acmStreamOpen( &m_pHas, NULL, m_pwfmt, &m_Wfe, NULL, NULL, NULL,ACM_STREAMOPENF_NONREALTIME);
|
|
|
|
if (!res)
|
|
{
|
|
|
|
unsigned long outsize,insize = m_pwfmt->nAvgBytesPerSec;
|
|
acmStreamSize(m_pHas, insize, &outsize, ACM_STREAMSIZEF_SOURCE);
|
|
|
|
LPBYTE inbuf=new BYTE[insize];
|
|
LPBYTE outbuf=new BYTE[outsize];
|
|
|
|
m_Ash.cbStruct= sizeof(ACMSTREAMHEADER);
|
|
m_Ash.fdwStatus= 0;
|
|
m_Ash.dwUser= 0;
|
|
m_Ash.pbSrc= inbuf;
|
|
m_Ash.cbSrcLength= insize;
|
|
m_Ash.cbSrcLengthUsed= 0;
|
|
m_Ash.dwSrcUser= insize;
|
|
m_Ash.pbDst= outbuf;
|
|
m_Ash.cbDstLength= outsize;
|
|
m_Ash.cbDstLengthUsed= 0;
|
|
m_Ash.dwDstUser= outsize;
|
|
|
|
res=acmStreamPrepareHeader( m_pHas, &m_Ash, 0);
|
|
|
|
if (res==0)
|
|
acmStreamSize(m_pHas, m_InDataSize, &m_OutDataSize, ACM_STREAMSIZEF_SOURCE);
|
|
else return -1;
|
|
} else return -1;
|
|
}
|
|
return CK_OK;
|
|
}
|
|
|
|
// Decodes next chunk of data, use get data buffer to get decoded data
|
|
CKERROR WAVReader::Decode()
|
|
{
|
|
if(m_IsPcm)
|
|
{
|
|
m_BufferDataRead = ReadPCM(m_Buffer,m_BufferSize);
|
|
if(m_BufferDataRead > 0)
|
|
return CK_OK;
|
|
else
|
|
return CKSOUND_READER_EOF;
|
|
}
|
|
else
|
|
{
|
|
|
|
MMRESULT res = -1;
|
|
|
|
if(m_InDataCursor >= m_InDataSize)
|
|
return CKSOUND_READER_EOF;
|
|
|
|
if(m_InDataCursor == 0)
|
|
{
|
|
m_Ash.cbSrcLength = DataRead(0, m_Ash.dwSrcUser, (char*)m_Ash.pbSrc);
|
|
m_Ash.cbDstLengthUsed = 0;
|
|
|
|
res = acmStreamConvert( m_pHas, &m_Ash, ACM_STREAMCONVERTF_START|ACM_STREAMCONVERTF_BLOCKALIGN);
|
|
if(!m_Ash.cbSrcLengthUsed)
|
|
res = acmStreamConvert( m_pHas, &m_Ash, ACM_STREAMCONVERTF_START);
|
|
}
|
|
else
|
|
{
|
|
m_Ash.cbSrcLength = DataRead(m_InDataCursor, m_Ash.dwSrcUser, (char*)m_Ash.pbSrc);;
|
|
m_Ash.cbDstLengthUsed = 0;
|
|
res = acmStreamConvert( m_pHas, &m_Ash,ACM_STREAMCONVERTF_BLOCKALIGN );
|
|
if(!m_Ash.cbSrcLengthUsed)
|
|
res = acmStreamConvert( m_pHas, &m_Ash,ACM_STREAMCONVERTF_END);
|
|
}
|
|
|
|
|
|
switch(res)
|
|
{
|
|
case 0:
|
|
{
|
|
m_InDataCursor += m_Ash.cbSrcLengthUsed;// ? m_Ash.cbSrcLengthUsed : m_Ash.cbSrcLength;
|
|
return CK_OK;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return CKSOUND_READER_GENERICERR;
|
|
|
|
}
|
|
|
|
|
|
int WAVReader::DataRead(int count, char* buf)
|
|
{
|
|
int r=mmioRead(m_hmmio,(HPSTR) buf, count);
|
|
return r;
|
|
}
|
|
|
|
int WAVReader::DataRead(long pos, int count, char* buf) {
|
|
return (DataSeek(pos)!=-1) ? DataRead(count, buf) : -1;
|
|
}
|
|
|
|
int WAVReader::DataSeek(long pos) {
|
|
int p=-1;
|
|
|
|
MMRESULT error = mmioAscend(m_hmmio, &m_mmckiFmt,0);
|
|
if (error!=0)
|
|
return -1;
|
|
|
|
m_mmckiData.ckid = mmioFOURCC('d','a','t','a');
|
|
error=mmioDescend(m_hmmio, &m_mmckiData, &m_mmckiRiff, MMIO_FINDCHUNK);
|
|
if (error!=0)
|
|
return -1;
|
|
|
|
p=mmioSeek(m_hmmio,pos,SEEK_CUR);
|
|
if (p==-1)
|
|
return -1;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
|
|
// Gets the last decoded buffer
|
|
CKERROR WAVReader::GetDataBuffer(BYTE **buf, int *size)
|
|
{
|
|
if(m_IsPcm)
|
|
{
|
|
*buf = m_Buffer;
|
|
*size = (int) m_BufferDataRead;
|
|
}
|
|
else
|
|
{
|
|
*buf = m_Ash.pbDst;
|
|
*size = m_Ash.cbDstLengthUsed;
|
|
}
|
|
|
|
return CK_OK;
|
|
}
|
|
|
|
// Gets the wave format of decoded datas
|
|
CKERROR WAVReader::GetWaveFormat(CKWaveFormat *wfe)
|
|
{
|
|
memcpy(wfe,&m_Wfe,sizeof(CKWaveFormat));
|
|
return CK_OK;
|
|
}
|
|
|
|
// Gets whole decoded data size
|
|
int WAVReader::GetDataSize()
|
|
{
|
|
return (int) m_OutDataSize;
|
|
}
|
|
|
|
// Gets the play time length
|
|
int WAVReader::GetDuration()
|
|
{
|
|
return (int)(((float)m_OutDataSize * 1000.0f ) / (float)m_Wfe.nAvgBytesPerSec);
|
|
}
|
|
|
|
|
|
CKERROR WAVReader::Seek(int pos)
|
|
{
|
|
int p=-1;
|
|
|
|
MMRESULT error = mmioAscend(m_hmmio, &m_mmckiFmt,0);
|
|
if (error!=0)
|
|
return -1;
|
|
|
|
m_mmckiData.ckid = mmioFOURCC('d','a','t','a');
|
|
error=mmioDescend(m_hmmio, &m_mmckiData, &m_mmckiRiff, MMIO_FINDCHUNK);
|
|
if (error!=0)
|
|
return -1;
|
|
|
|
p=mmioSeek(m_hmmio,pos,SEEK_CUR);
|
|
if (p==-1)
|
|
return CKSOUND_READER_GENERICERR;
|
|
if(pos == 0)
|
|
m_InDataCursor = 0;
|
|
else
|
|
m_InDataCursor = p;
|
|
|
|
|
|
return CK_OK;
|
|
}
|
|
|
|
// Play
|
|
CKERROR WAVReader::Play()
|
|
{
|
|
return CK_OK;
|
|
}
|
|
|
|
// Stop
|
|
CKERROR WAVReader::Stop()
|
|
{
|
|
return CK_OK;
|
|
}
|
|
|
|
|
|
|
|
// Cue
|
|
//
|
|
|
|
BOOL WAVReader::Cue (void)
|
|
{
|
|
BOOL fRtn = CK_OK; // assume success
|
|
|
|
|
|
// Seek to 'data' chunk from beginning of file
|
|
if (mmioSeek (m_hmmio, m_mmckiRiff.dwDataOffset + sizeof(FOURCC), SEEK_SET) != -1)
|
|
{
|
|
// Descend into 'data' chunk
|
|
m_mmckiData.ckid = mmioFOURCC('d', 'a', 't', 'a');
|
|
if ((m_mmr = mmioDescend (m_hmmio, &m_mmckiData, &m_mmckiRiff, MMIO_FINDCHUNK)) == MMSYSERR_NOERROR)
|
|
{
|
|
// Reset byte counter
|
|
m_nBytesPlayed = 0;
|
|
}
|
|
else
|
|
{
|
|
// UNDONE: set m_mmr
|
|
fRtn = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// mmioSeek error
|
|
m_mmr = MMIOERR_CANNOTSEEK;
|
|
fRtn = -1;
|
|
}
|
|
|
|
return fRtn;
|
|
}
|
|
|
|
|
|
// Read
|
|
//
|
|
// Returns number of bytes actually read.
|
|
// On error, returns 0, MMIO error code in m_mmr.
|
|
//
|
|
UINT WAVReader::ReadPCM(BYTE * pbDest, UINT cbSize)
|
|
{
|
|
MMIOINFO mmioinfo;
|
|
UINT cb;
|
|
|
|
|
|
// Use direct buffer access for reads to maximize performance
|
|
if (m_mmr = mmioGetInfo (m_hmmio, &mmioinfo, 0))
|
|
{
|
|
goto READ_ERROR;
|
|
}
|
|
|
|
// Limit read size to chunk size
|
|
cbSize = (cbSize > m_mmckiData.cksize) ? m_mmckiData.cksize : cbSize;
|
|
|
|
// Adjust chunk size
|
|
m_mmckiData.cksize -= cbSize;
|
|
|
|
// Copy bytes from MMIO buffer
|
|
for (cb = 0; cb < cbSize; cb++)
|
|
{
|
|
// Advance buffer if necessary
|
|
if (mmioinfo.pchNext == mmioinfo.pchEndRead)
|
|
{
|
|
if (m_mmr = mmioAdvance (m_hmmio, &mmioinfo, MMIO_READ))
|
|
{
|
|
goto READ_ERROR;
|
|
}
|
|
|
|
if (mmioinfo.pchNext == mmioinfo.pchEndRead)
|
|
{
|
|
m_mmr = MMIOERR_CANNOTREAD;
|
|
goto READ_ERROR;
|
|
}
|
|
}
|
|
|
|
// Actual copy
|
|
// *((BYTE*)pbDest+cb) = *((BYTE*)mmioinfo.pchNext)++;
|
|
*(pbDest+cb) = *(mmioinfo.pchNext)++;
|
|
}
|
|
|
|
// End direct buffer access
|
|
if (m_mmr = mmioSetInfo (m_hmmio, &mmioinfo, 0))
|
|
{
|
|
goto READ_ERROR;
|
|
}
|
|
|
|
// Successful read, keep running total of number of data bytes read
|
|
m_nBytesPlayed += cbSize;
|
|
goto READ_DONE;
|
|
|
|
READ_ERROR:
|
|
cbSize = 0;
|
|
|
|
READ_DONE:
|
|
return (cbSize);
|
|
}
|
|
|
|
void WAVReader::Release()
|
|
{
|
|
delete this;
|
|
}
|