|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "plWavFile.h"
|
|
|
|
|
|
|
|
#include <dxerr.h>
|
|
|
|
#include <dsound.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#pragma comment(lib, "winmm.lib")
|
|
|
|
#ifdef PATCHER
|
|
|
|
#define DXTRACE_ERR(str,hr) hr // I'm not linking in directx stuff to the just for this
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// if it looks like I lifted this class directly from Microsoft it's because that
|
|
|
|
// is exactly what I did. It's okay, though. Microsoft tells you to go ahead
|
|
|
|
// and do it in the DX8 documentation. They are SO nice.
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::CWaveFile()
|
|
|
|
// Desc: Constructs the class. Call Open() to open a wave file for reading.
|
|
|
|
// Then call Read() as needed. Calling the destructor or Close()
|
|
|
|
// will close the file.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CWaveFile::CWaveFile()
|
|
|
|
{
|
|
|
|
m_pwfx = NULL;
|
|
|
|
m_hmmio = NULL;
|
|
|
|
m_dwSize = 0;
|
|
|
|
m_bIsReadingFromMemory = FALSE;
|
|
|
|
fSecsPerSample = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::~CWaveFile()
|
|
|
|
// Desc: Destructs the class
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CWaveFile::~CWaveFile()
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
|
|
|
|
if( !m_bIsReadingFromMemory )
|
|
|
|
{
|
|
|
|
delete[] m_pwfx;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0 ; i < fMarkers.size() ; i++)
|
|
|
|
{
|
|
|
|
delete [] fMarkers[i]->fName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::Open()
|
|
|
|
// Desc: Opens a wave file for reading
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::Open(const char *strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
m_dwFlags = dwFlags;
|
|
|
|
m_bIsReadingFromMemory = FALSE;
|
|
|
|
|
|
|
|
char fileName[MAX_PATH];
|
|
|
|
sprintf(fileName, strFileName);
|
|
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
wchar_t * temp = hsStringToWString(fileName);
|
|
|
|
std::wstring wFileName = temp;
|
|
|
|
delete [] temp;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( m_dwFlags == WAVEFILE_READ )
|
|
|
|
{
|
|
|
|
if( strFileName == NULL )
|
|
|
|
return E_INVALIDARG;
|
|
|
|
delete[] m_pwfx;
|
|
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
m_hmmio = mmioOpen( (wchar_t*)wFileName.c_str(), NULL, MMIO_ALLOCBUF | MMIO_READ );
|
|
|
|
#else
|
|
|
|
m_hmmio = mmioOpen( fileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( NULL == m_hmmio )
|
|
|
|
{
|
|
|
|
HRSRC hResInfo;
|
|
|
|
HGLOBAL hResData;
|
|
|
|
DWORD dwSize;
|
|
|
|
VOID* pvRes;
|
|
|
|
|
|
|
|
// Loading it as a file failed, so try it as a resource
|
|
|
|
#ifdef UNICODE
|
|
|
|
if( NULL == ( hResInfo = FindResource( NULL, wFileName.c_str(), TEXT("WAVE") ) ) )
|
|
|
|
{
|
|
|
|
if( NULL == ( hResInfo = FindResource( NULL, wFileName.c_str(), TEXT("WAV") ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("FindResource"), E_FAIL );
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
|
|
|
|
{
|
|
|
|
if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("FindResource"), E_FAIL );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("LoadResource"), E_FAIL );
|
|
|
|
|
|
|
|
if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("SizeofResource"), E_FAIL );
|
|
|
|
|
|
|
|
if( NULL == ( pvRes = LockResource( hResData ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("LockResource"), E_FAIL );
|
|
|
|
|
|
|
|
CHAR* pData = TRACKED_NEW CHAR[ dwSize ];
|
|
|
|
memcpy( pData, pvRes, dwSize );
|
|
|
|
|
|
|
|
MMIOINFO mmioInfo;
|
|
|
|
ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
|
|
|
|
mmioInfo.fccIOProc = FOURCC_MEM;
|
|
|
|
mmioInfo.cchBuffer = dwSize;
|
|
|
|
mmioInfo.pchBuffer = (CHAR*) pData;
|
|
|
|
|
|
|
|
m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( hr = ReadMMIO() ) )
|
|
|
|
{
|
|
|
|
// ReadMMIO will fail if its an not a wave file
|
|
|
|
mmioClose( m_hmmio, 0 );
|
|
|
|
return DXTRACE_ERR( TEXT("ReadMMIO"), hr );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( hr = ResetFile() ) )
|
|
|
|
return DXTRACE_ERR( TEXT("ResetFile"), hr );
|
|
|
|
|
|
|
|
// After the reset, the size of the wav file is m_ck.cksize so store it now
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_dwSize = m_ck.cksize;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef UNICODE
|
|
|
|
m_hmmio = mmioOpen( (wchar_t*)wFileName.c_str(), NULL, MMIO_ALLOCBUF |
|
|
|
|
MMIO_READWRITE |
|
|
|
|
MMIO_CREATE );
|
|
|
|
#else
|
|
|
|
m_hmmio = mmioOpen( fileName, NULL, MMIO_ALLOCBUF |
|
|
|
|
MMIO_READWRITE |
|
|
|
|
MMIO_CREATE );
|
|
|
|
#endif
|
|
|
|
if( NULL == m_hmmio )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioOpen"), E_FAIL );
|
|
|
|
|
|
|
|
if( FAILED( hr = WriteMMIO( pwfx ) ) )
|
|
|
|
{
|
|
|
|
mmioClose( m_hmmio, 0 );
|
|
|
|
return DXTRACE_ERR( TEXT("WriteMMIO"), hr );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( FAILED( hr = ResetFile() ) )
|
|
|
|
return DXTRACE_ERR( TEXT("ResetFile"), hr );
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::OpenFromMemory()
|
|
|
|
// Desc: copy data to CWaveFile member variable from memory
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize,
|
|
|
|
WAVEFORMATEX* pwfx, DWORD dwFlags )
|
|
|
|
{
|
|
|
|
m_pwfx = pwfx;
|
|
|
|
m_ulDataSize = ulDataSize;
|
|
|
|
m_pbData = pbData;
|
|
|
|
m_pbDataCur = m_pbData;
|
|
|
|
m_bIsReadingFromMemory = TRUE;
|
|
|
|
|
|
|
|
if( dwFlags != WAVEFILE_READ )
|
|
|
|
return E_NOTIMPL;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This defintion for a CuePoint was ripped from the internet somewhere. There are more defs at the end of this file which attempt to document
|
|
|
|
these wave file format extensions for storing markers.
|
|
|
|
|
|
|
|
Cue Point--
|
|
|
|
|
|
|
|
The dwIdentifier field contains a unique number (ie, different than the ID number of any other CuePoint structure). This is used to associate
|
|
|
|
a CuePoint structure with other structures used in other chunks which will be described later.
|
|
|
|
|
|
|
|
The dwPosition field specifies the position of the cue point within the "play order" (as determined by the Playlist chunk. See that chunk for
|
|
|
|
a discussion of the play order).
|
|
|
|
|
|
|
|
The fccChunk field specifies the chunk ID of the Data or Wave List chunk which actually contains the waveform data to which this CuePoint
|
|
|
|
refers. If there is only one Data chunk in the file, then this field is set to the ID 'data'. On the other hand, if the file contains a Wave
|
|
|
|
List (which can contain both 'data' and 'slnt' chunks), then fccChunk will specify 'data' or 'slnt' depending upon in which type of chunk the
|
|
|
|
referenced waveform data is found.
|
|
|
|
|
|
|
|
The dwChunkStart and dwBlockStart fields are set to 0 for an uncompressed WAVE file that contains one 'data' chunk. These fields are used
|
|
|
|
only for WAVE files that contain a Wave List (with multiple 'data' and 'slnt' chunks), or for a compressed file containing a 'data' chunk.
|
|
|
|
(Actually, in the latter case, dwChunkStart is also set to 0, and only dwBlockStart is used). Again, I want to emphasize that you can avoid
|
|
|
|
all of this unnecessary crap if you avoid hassling with compressed files, or Wave Lists, and instead stick to the sensible basics.
|
|
|
|
|
|
|
|
The dwChunkStart field specifies the byte offset of the start of the 'data' or 'slnt' chunk which actually contains the waveform data to
|
|
|
|
which this CuePoint refers. This offset is relative to the start of the first chunk within the Wave List. (ie, It's the byte offset, within
|
|
|
|
the Wave List, of where the 'data' or 'slnt' chunk of interest appears. The first chunk within the List would be at an offset of 0).
|
|
|
|
|
|
|
|
The dwBlockStart field specifies the byte offset of the start of the block containing the position. This offset is relative to the start of
|
|
|
|
the waveform data within the 'data' or 'slnt' chunk.
|
|
|
|
|
|
|
|
The dwSampleOffset field specifies the sample offset of the cue point relative to the start of the block. In an uncompressed file, this
|
|
|
|
equates to simply being the offset within the waveformData array. Unfortunately, the WAVE documentation is much too ambiguous, and doesn't
|
|
|
|
define what it means by the term "sample offset". This could mean a byte offset, or it could mean counting the sample points (for example,
|
|
|
|
in a 16-bit wave, every 2 bytes would be 1 sample point), or it could even mean sample frames (as the loop offsets in AIFF are specified).
|
|
|
|
Who knows? The guy who conjured up the Cue chunk certainly isn't saying. I'm assuming that it's a byte offset, like the above 2 fields.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class CuePoint
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DWORD dwIdentifier;
|
|
|
|
DWORD dwPosition;
|
|
|
|
FOURCC fccChunk;
|
|
|
|
DWORD dwChunkStart;
|
|
|
|
DWORD dwBlockStart;
|
|
|
|
DWORD dwSampleOffset;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CuePoint(DWORD id, DWORD pos, FOURCC chk, DWORD ckSt, DWORD BkSt, DWORD SO) :
|
|
|
|
dwIdentifier(id), dwPosition(pos), fccChunk(chk), dwChunkStart(ckSt), dwBlockStart(BkSt), dwSampleOffset(SO)
|
|
|
|
{}
|
|
|
|
CuePoint(){}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// this struct is used to hold cue pts temporarily while we wait for the labels that match them
|
|
|
|
//
|
|
|
|
struct myCuePoint
|
|
|
|
{
|
|
|
|
DWORD fId;
|
|
|
|
DWORD fOffset;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::ReadMMIO()
|
|
|
|
// Desc: Support function for reading from a multimedia I/O stream.
|
|
|
|
// m_hmmio must be valid before calling. This function uses it to
|
|
|
|
// update m_ckRiff, and m_pwfx.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::ReadMMIO()
|
|
|
|
{
|
|
|
|
MMCKINFO ckIn; // chunk info. for general use.
|
|
|
|
PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in.
|
|
|
|
|
|
|
|
m_pwfx = NULL;
|
|
|
|
|
|
|
|
if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
|
|
|
|
|
|
|
|
// Check to make sure this is a valid wave file
|
|
|
|
if( (m_ckRiff.ckid != FOURCC_RIFF) ||
|
|
|
|
(m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioFOURCC"), E_FAIL );
|
|
|
|
|
|
|
|
// Search the input file for for the 'fmt ' chunk.
|
|
|
|
ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
|
|
|
|
if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
|
|
|
|
|
|
|
|
// Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
|
|
|
|
// if there are extra parameters at the end, we'll ignore them
|
|
|
|
if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
|
|
|
|
return DXTRACE_ERR( TEXT("sizeof(PCMWAVEFORMAT)"), E_FAIL );
|
|
|
|
|
|
|
|
// Read the 'fmt ' chunk into <pcmWaveFormat>.
|
|
|
|
if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat,
|
|
|
|
sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
|
|
|
|
|
|
|
|
// Allocate the waveformatex, but if its not pcm format, read the next
|
|
|
|
// word, and thats how many extra bytes to allocate.
|
|
|
|
if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
|
|
|
|
{
|
|
|
|
m_pwfx = (WAVEFORMATEX*)( TRACKED_NEW CHAR[ sizeof( WAVEFORMATEX ) ] );
|
|
|
|
if( NULL == m_pwfx )
|
|
|
|
return DXTRACE_ERR( TEXT("m_pwfx"), E_FAIL );
|
|
|
|
|
|
|
|
// Copy the bytes from the pcm structure to the waveformatex structure
|
|
|
|
memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
|
|
|
|
m_pwfx->cbSize = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Read in length of extra bytes.
|
|
|
|
WORD cbExtraBytes = 0L;
|
|
|
|
if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
|
|
|
|
|
|
|
|
m_pwfx = (WAVEFORMATEX*)( TRACKED_NEW CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ] );
|
|
|
|
if( NULL == m_pwfx )
|
|
|
|
return DXTRACE_ERR( TEXT("new"), E_FAIL );
|
|
|
|
|
|
|
|
// Copy the bytes from the pcm structure to the waveformatex structure
|
|
|
|
memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
|
|
|
|
m_pwfx->cbSize = cbExtraBytes;
|
|
|
|
|
|
|
|
// Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
|
|
|
|
if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)),
|
|
|
|
cbExtraBytes ) != cbExtraBytes )
|
|
|
|
{
|
|
|
|
delete m_pwfx;
|
|
|
|
return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fSecsPerSample = 1.0/ (double)(pcmWaveFormat.wf.nSamplesPerSec) ; // * (((double)pcmWaveFormat.wBitsPerSample)/8.0);
|
|
|
|
|
|
|
|
|
|
|
|
// Ascend the input file out of the 'fmt ' chunk.
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
|
|
|
|
{
|
|
|
|
delete m_pwfx;
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Here is where we attempt to parse sound indicies from the file for loop points.
|
|
|
|
// If there is no cue chunk then we just return OK
|
|
|
|
//
|
|
|
|
|
|
|
|
ckIn.ckid = mmioFOURCC('c', 'u', 'e', ' ');
|
|
|
|
if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
|
|
|
|
return S_OK; // No Cue Chunck so no point in reading the rest
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Expect the 'cue ' chunk to be at least as large as <PCMWAVEFORMAT>;
|
|
|
|
// if there are extra parameters at the end, we'll ignore them
|
|
|
|
if( ckIn.cksize < (long) ( sizeof(FOURCC) + 2*(sizeof(DWORD)) + sizeof(CuePoint) ) )
|
|
|
|
return DXTRACE_ERR( TEXT("sizeof(CueChunk)"), E_FAIL );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
DWORD* CueBuff = TRACKED_NEW DWORD[ckIn.cksize];
|
|
|
|
DWORD Results;
|
|
|
|
Read((BYTE*)CueBuff, ckIn.cksize, &Results);
|
|
|
|
|
|
|
|
std::vector<myCuePoint> myCueList; // Place to hold the cue points
|
|
|
|
|
|
|
|
int numCuePoints = (ckIn.cksize - sizeof(DWORD))/sizeof(CuePoint); // this is how many there should be.
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
for (i = 1, j = 0; i <= ckIn.cksize && j < numCuePoints ; i += sizeof(CuePoint)/(sizeof(DWORD)), j++)
|
|
|
|
|
|
|
|
{
|
|
|
|
myCuePoint p;
|
|
|
|
p.fId = CueBuff[i]; // dwIdentifier
|
|
|
|
p.fOffset = CueBuff[i+1]; // dwPosition
|
|
|
|
myCueList.push_back(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] CueBuff;
|
|
|
|
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
|
|
|
|
{
|
|
|
|
delete m_pwfx;
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Goal --> Grab the label information below
|
|
|
|
ckIn.ckid = mmioFOURCC('a', 'd', 't', 'l');
|
|
|
|
if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDLIST ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioDescend, with list"), E_FAIL );
|
|
|
|
|
|
|
|
|
|
|
|
plSoundMarker *newMarker;
|
|
|
|
|
|
|
|
BYTE *labelBuf = TRACKED_NEW BYTE [ckIn.cksize];
|
|
|
|
|
|
|
|
// Read the entire lable chunk and then lets parse out the individual lables.
|
|
|
|
Read(labelBuf, ckIn.cksize-4, &Results);
|
|
|
|
|
|
|
|
BYTE *bp = labelBuf;
|
|
|
|
// Keep looking for labl chunks till we run out.
|
|
|
|
while(!strncmp("labl",(char*)bp,4))
|
|
|
|
{
|
|
|
|
DWORD size = *(DWORD*)(bp + 4);
|
|
|
|
DWORD id = *(DWORD*)(bp + 8);
|
|
|
|
newMarker = TRACKED_NEW plSoundMarker; // Grab a new label
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
int numPts = myCueList.size();
|
|
|
|
//
|
|
|
|
// Do we have a matching cue point for this label?
|
|
|
|
//
|
|
|
|
for(i = 0 ; i < numPts; i++)
|
|
|
|
{
|
|
|
|
if(id == myCueList[i].fId)
|
|
|
|
{
|
|
|
|
newMarker->fOffset = myCueList[i].fOffset * fSecsPerSample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int stringSize = size - sizeof(DWORD); // text string is size of chunck - size of the size word
|
|
|
|
newMarker->fName = TRACKED_NEW char[ stringSize];
|
|
|
|
|
|
|
|
strcpy(newMarker->fName, (char*)(bp + 12));
|
|
|
|
|
|
|
|
fMarkers.push_back(newMarker);
|
|
|
|
bp += size + 8;
|
|
|
|
|
|
|
|
// crappy fixup hack for odd length label records
|
|
|
|
if(size & 1 && !strncmp("labl", (char*)(bp +1), 4))
|
|
|
|
bp++;
|
|
|
|
|
|
|
|
fprintf(stderr,"Label name=%s Time =%f\n",newMarker->fName, newMarker->fOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete [] labelBuf;
|
|
|
|
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
|
|
|
|
{
|
|
|
|
delete m_pwfx;
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::GetSize()
|
|
|
|
// Desc: Retuns the size of the read access wave file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DWORD CWaveFile::GetSize()
|
|
|
|
{
|
|
|
|
return m_dwSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::ResetFile()
|
|
|
|
// Desc: Resets the internal m_ck pointer so reading starts from the
|
|
|
|
// beginning of the file again
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::ResetFile()
|
|
|
|
{
|
|
|
|
if( m_bIsReadingFromMemory )
|
|
|
|
{
|
|
|
|
m_pbDataCur = m_pbData;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_hmmio == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
|
|
|
|
if( m_dwFlags == WAVEFILE_READ )
|
|
|
|
{
|
|
|
|
// Seek to the data
|
|
|
|
if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
|
|
|
|
SEEK_SET ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioSeek"), E_FAIL );
|
|
|
|
|
|
|
|
// Search the input file for the 'data' chunk.
|
|
|
|
m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
|
|
|
|
if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Create the 'data' chunk that holds the waveform samples.
|
|
|
|
m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
|
|
|
|
m_ck.cksize = 0;
|
|
|
|
|
|
|
|
if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
|
|
|
|
|
|
|
|
if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define MCN_USE_NEW_READ_METHOD 0
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::Read()
|
|
|
|
// Desc: Reads section of data from a wave file into pBuffer and returns
|
|
|
|
// how much read in pdwSizeRead, reading not more than dwSizeToRead.
|
|
|
|
// This uses m_ck to determine where to start reading from. So
|
|
|
|
// subsequent calls will be continue where the last left off unless
|
|
|
|
// Reset() is called.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
|
|
|
|
{
|
|
|
|
if( m_bIsReadingFromMemory )
|
|
|
|
{
|
|
|
|
if( m_pbDataCur == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = 0;
|
|
|
|
|
|
|
|
if( (BYTE*)(m_pbDataCur + dwSizeToRead) >
|
|
|
|
(BYTE*)(m_pbData + m_ulDataSize) )
|
|
|
|
{
|
|
|
|
dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
|
|
|
|
}
|
|
|
|
|
|
|
|
CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = dwSizeToRead;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MMIOINFO mmioinfoIn; // current status of m_hmmio
|
|
|
|
|
|
|
|
if( m_hmmio == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
if( pBuffer == NULL || pdwSizeRead == NULL )
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = 0;
|
|
|
|
|
|
|
|
if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
|
|
|
|
|
|
|
|
UINT cbDataIn = dwSizeToRead;
|
|
|
|
if( cbDataIn > m_ck.cksize )
|
|
|
|
cbDataIn = m_ck.cksize;
|
|
|
|
|
|
|
|
m_ck.cksize -= cbDataIn;
|
|
|
|
|
|
|
|
#if !(MCN_USE_NEW_READ_METHOD)
|
|
|
|
for( DWORD cT = 0; cT < cbDataIn; cT++ )
|
|
|
|
{
|
|
|
|
// Copy the bytes from the io to the buffer.
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
{
|
|
|
|
if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
|
|
|
|
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actual copy.
|
|
|
|
*((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
|
|
|
|
mmioinfoIn.pchNext++;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Attempt to do this a bit faster... 9.12.2001 mcn
|
|
|
|
for( DWORD cT = 0; cT < cbDataIn; )
|
|
|
|
{
|
|
|
|
// Copy the bytes from the io to the buffer.
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
{
|
|
|
|
if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
|
|
|
|
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actual copy
|
|
|
|
DWORD length = (DWORD)mmioinfoIn.pchEndRead - (DWORD)mmioinfoIn.pchNext;
|
|
|
|
if( cT + length > cbDataIn )
|
|
|
|
length = cbDataIn - cT;
|
|
|
|
|
|
|
|
memcpy( (BYTE*)pBuffer + cT, mmioinfoIn.pchNext, length );
|
|
|
|
mmioinfoIn.pchNext += length;
|
|
|
|
cT += length;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = cbDataIn;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::AdvanceWithoutRead()
|
|
|
|
// Desc: Identical to Read(), only doesn't actually read any data in.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::AdvanceWithoutRead( DWORD dwSizeToRead, DWORD* pdwSizeRead )
|
|
|
|
{
|
|
|
|
if( m_bIsReadingFromMemory )
|
|
|
|
{
|
|
|
|
if( m_pbDataCur == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = 0;
|
|
|
|
|
|
|
|
if( (BYTE*)(m_pbDataCur + dwSizeToRead) >
|
|
|
|
(BYTE*)(m_pbData + m_ulDataSize) )
|
|
|
|
{
|
|
|
|
dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = dwSizeToRead;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MMIOINFO mmioinfoIn; // current status of m_hmmio
|
|
|
|
|
|
|
|
if( m_hmmio == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
if( pdwSizeRead == NULL )
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = 0;
|
|
|
|
|
|
|
|
if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
|
|
|
|
|
|
|
|
UINT cbDataIn = dwSizeToRead;
|
|
|
|
if( cbDataIn > m_ck.cksize )
|
|
|
|
cbDataIn = m_ck.cksize;
|
|
|
|
|
|
|
|
m_ck.cksize -= cbDataIn;
|
|
|
|
|
|
|
|
#if !(MCN_USE_NEW_READ_METHOD)
|
|
|
|
for( DWORD cT = 0; cT < cbDataIn; cT++ )
|
|
|
|
{
|
|
|
|
// Copy the bytes from the io to the buffer.
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
{
|
|
|
|
if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
|
|
|
|
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
mmioinfoIn.pchNext++;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Attempt to do this a bit faster... 9.12.2001 mcn
|
|
|
|
for( DWORD cT = 0; cT < cbDataIn; )
|
|
|
|
{
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
{
|
|
|
|
if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
|
|
|
|
|
|
|
|
if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advance
|
|
|
|
DWORD length = (DWORD)mmioinfoIn.pchEndRead - (DWORD)mmioinfoIn.pchNext;
|
|
|
|
if( cT + length > cbDataIn )
|
|
|
|
length = cbDataIn - cT;
|
|
|
|
|
|
|
|
mmioinfoIn.pchNext += length;
|
|
|
|
cT += length;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
|
|
|
|
|
|
|
|
if( pdwSizeRead != NULL )
|
|
|
|
*pdwSizeRead = cbDataIn;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::IClose()
|
|
|
|
// Desc: Closes the wave file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::IClose()
|
|
|
|
{
|
|
|
|
if( m_dwFlags == WAVEFILE_READ )
|
|
|
|
{
|
|
|
|
mmioClose( m_hmmio, 0 );
|
|
|
|
m_hmmio = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
|
|
|
|
|
|
|
|
if( m_hmmio == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
|
|
|
|
if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
|
|
|
|
|
|
|
|
// Ascend the output file out of the 'data' chunk -- this will cause
|
|
|
|
// the chunk size of the 'data' chunk to be written.
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
|
|
|
|
// Do this here instead...
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
|
|
|
|
mmioSeek( m_hmmio, 0, SEEK_SET );
|
|
|
|
|
|
|
|
if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
|
|
|
|
|
|
|
|
m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
|
|
|
|
|
|
|
|
if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
|
|
|
|
{
|
|
|
|
DWORD dwSamples = 0;
|
|
|
|
mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) );
|
|
|
|
mmioAscend( m_hmmio, &m_ck, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ascend the output file out of the 'RIFF' chunk -- this will cause
|
|
|
|
// the chunk size of the 'RIFF' chunk to be written.
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
|
|
|
|
mmioClose( m_hmmio, 0 );
|
|
|
|
m_hmmio = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::WriteMMIO()
|
|
|
|
// Desc: Support function for reading from a multimedia I/O stream
|
|
|
|
// pwfxDest is the WAVEFORMATEX for this new wave file.
|
|
|
|
// m_hmmio must be valid before calling. This function uses it to
|
|
|
|
// update m_ckRiff, and m_ck.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest )
|
|
|
|
{
|
|
|
|
DWORD dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
|
|
|
|
MMCKINFO ckOut1;
|
|
|
|
|
|
|
|
dwFactChunk = (DWORD)-1;
|
|
|
|
|
|
|
|
// Create the output file RIFF chunk of form type 'WAVE'.
|
|
|
|
m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E');
|
|
|
|
m_ckRiff.cksize = 0;
|
|
|
|
|
|
|
|
if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
|
|
|
|
|
|
|
|
// We are now descended into the 'RIFF' chunk we just created.
|
|
|
|
// Now create the 'fmt ' chunk. Since we know the size of this chunk,
|
|
|
|
// specify it in the MMCKINFO structure so MMIO doesn't have to seek
|
|
|
|
// back and set the chunk size after ascending from the chunk.
|
|
|
|
m_ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
|
|
|
|
m_ck.cksize = sizeof(PCMWAVEFORMAT);
|
|
|
|
|
|
|
|
if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
|
|
|
|
|
|
|
|
// Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type.
|
|
|
|
if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM )
|
|
|
|
{
|
|
|
|
if( mmioWrite( m_hmmio, (HPSTR) pwfxDest,
|
|
|
|
sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
|
|
|
|
return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Write the variable length size.
|
|
|
|
if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest,
|
|
|
|
sizeof(*pwfxDest) + pwfxDest->cbSize ) !=
|
|
|
|
( sizeof(*pwfxDest) + pwfxDest->cbSize ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
|
|
|
|
// Now create the fact chunk, not required for PCM but nice to have. This is filled
|
|
|
|
// in when the close routine is called.
|
|
|
|
ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't');
|
|
|
|
ckOut1.cksize = 0;
|
|
|
|
|
|
|
|
if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
|
|
|
|
|
|
|
|
if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) !=
|
|
|
|
sizeof(dwFactChunk) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL );
|
|
|
|
|
|
|
|
// Now ascend out of the fact chunk...
|
|
|
|
if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Name: CWaveFile::Write()
|
|
|
|
// Desc: Writes data to the open wave file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote )
|
|
|
|
{
|
|
|
|
UINT cT;
|
|
|
|
|
|
|
|
if( m_bIsReadingFromMemory )
|
|
|
|
return E_NOTIMPL;
|
|
|
|
if( m_hmmio == NULL )
|
|
|
|
return CO_E_NOTINITIALIZED;
|
|
|
|
if( pnSizeWrote == NULL || pbSrcData == NULL )
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
*pnSizeWrote = 0;
|
|
|
|
|
|
|
|
for( cT = 0; cT < nSizeToWrite; cT++ )
|
|
|
|
{
|
|
|
|
if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite )
|
|
|
|
{
|
|
|
|
m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
|
|
|
|
if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) )
|
|
|
|
return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
|
|
|
|
}
|
|
|
|
|
|
|
|
*((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT);
|
|
|
|
(BYTE*)m_mmioinfoOut.pchNext++;
|
|
|
|
|
|
|
|
(*pnSizeWrote)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Overloads for plAudioFileReader (we only support writing for CWaveFile for now)
|
|
|
|
CWaveFile::CWaveFile( const char *path, plAudioCore::ChannelSelect whichChan )
|
|
|
|
{
|
|
|
|
m_pwfx = NULL;
|
|
|
|
m_hmmio = NULL;
|
|
|
|
m_dwSize = 0;
|
|
|
|
m_bIsReadingFromMemory = FALSE;
|
|
|
|
fSecsPerSample = 0;
|
|
|
|
|
|
|
|
// Just a stub--do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool CWaveFile::OpenForWriting( const char *path, plWAVHeader &header )
|
|
|
|
{
|
|
|
|
fHeader = header;
|
|
|
|
|
|
|
|
WAVEFORMATEX winFormat;
|
|
|
|
winFormat.cbSize = 0;
|
|
|
|
winFormat.nAvgBytesPerSec = header.fAvgBytesPerSec;
|
|
|
|
winFormat.nBlockAlign = header.fBlockAlign;
|
|
|
|
winFormat.nChannels = header.fNumChannels;
|
|
|
|
winFormat.nSamplesPerSec = header.fNumSamplesPerSec;
|
|
|
|
winFormat.wBitsPerSample = header.fBitsPerSample;
|
|
|
|
winFormat.wFormatTag = header.fFormatTag;
|
|
|
|
|
|
|
|
if( SUCCEEDED( Open( path, &winFormat, WAVEFILE_WRITE ) ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
plWAVHeader &CWaveFile::GetHeader( void )
|
|
|
|
{
|
|
|
|
return fHeader;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CWaveFile::Close( void )
|
|
|
|
{
|
|
|
|
IClose();
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 CWaveFile::GetDataSize( void )
|
|
|
|
{
|
|
|
|
hsAssert( false, "Unsupported" );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
float CWaveFile::GetLengthInSecs( void )
|
|
|
|
{
|
|
|
|
hsAssert( false, "Unsupported" );
|
|
|
|
return 0.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool CWaveFile::SetPosition( UInt32 numBytes )
|
|
|
|
{
|
|
|
|
hsAssert( false, "Unsupported" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool CWaveFile::Read( UInt32 numBytes, void *buffer )
|
|
|
|
{
|
|
|
|
hsAssert( false, "Unsupported" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 CWaveFile::NumBytesLeft( void )
|
|
|
|
{
|
|
|
|
hsAssert( false, "Unsupported" );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 CWaveFile::Write( UInt32 bytes, void *buffer )
|
|
|
|
{
|
|
|
|
UINT written;
|
|
|
|
Write( (DWORD)bytes, (BYTE *)buffer, &written );
|
|
|
|
return (UInt32)written;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool CWaveFile::IsValid( void )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
THIS IS MORE STUFF having to do with WAV FILE format. It is just sitting here for documentation purposes.
|
|
|
|
/*
|
|
|
|
Cue Chunk--
|
|
|
|
|
|
|
|
The ID is always 'cue '. chunkSize is the number of bytes in the chunk, not counting the 8 bytes used by ID and Size fields.
|
|
|
|
The dwCuePoints field is the number of CuePoint structures in the Cue Chunk. If dwCuePoints is not 0, it is followed by that many
|
|
|
|
CuePoint structures, one after the other. Because all fields in a CuePoint structure are an even number of bytes, the length of any
|
|
|
|
CuePoint will always be even. Thus, CuePoints are packed together with no unused bytes between them. The CuePoints need not be placed
|
|
|
|
in any particular order.
|
|
|
|
|
|
|
|
The Cue chunk is optional. No more than one Cue chunk can appear in a WAVE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class CueChunk
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
FOURCC chunkID;
|
|
|
|
DWORD chunkSize;
|
|
|
|
DWORD dwCuePoints;
|
|
|
|
std::vector<CuePoint*> points;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CueChunk(DWORD ChunkSize)
|
|
|
|
{
|
|
|
|
chunkID = mmioFOURCC('c','u','e',' ');
|
|
|
|
chunkSize = ChunkSize;
|
|
|
|
dwCuePoints = (ChunkSize - (sizeof(DWORD)*1))/(sizeof(CuePoint));
|
|
|
|
//points = NULL;
|
|
|
|
//points = TRACKED_NEW CuePoint[dwCuePoints];
|
|
|
|
|
|
|
|
}
|
|
|
|
//Cue
|
|
|
|
|
|
|
|
~CueChunk() {} //for(int i = 0; i < (int) dwCuePoints; i++) points.erase(i); }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
LabelChunk--
|
|
|
|
|
|
|
|
The ID is always 'labl'. chunkSize is the number of bytes in the chunk, not counting the 8 bytes used by ID and Size fields nor any possible
|
|
|
|
pad byte needed to make the chunk an even size (ie, chunkSize is the number of remaining bytes in the chunk after the chunkSize field, not
|
|
|
|
counting any trailing pad byte).
|
|
|
|
The dwIdentifier field contains a unique number (ie, different than the ID number of any other Label chunk). This field should correspond
|
|
|
|
with the dwIndentifier field of some CuePoint stored in the Cue chunk. In other words, this Label chunk contains the text label associated
|
|
|
|
with that CuePoint structure with the same ID number.
|
|
|
|
|
|
|
|
The dwText array contains the text label. It should be a null-terminated string. (The null byte is included in the chunkSize, therefore the
|
|
|
|
length of the string, including the null byte, is chunkSize - 4).
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
class LabelChunk
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
FOURCC chunkID;
|
|
|
|
DWORD chunkSize;
|
|
|
|
|
|
|
|
DWORD dwIdentifier;
|
|
|
|
char* dwText;
|
|
|
|
public:
|
|
|
|
LabelChunk(DWORD ChunkSize)
|
|
|
|
{
|
|
|
|
chunkID = mmioFOURCC('l','a','b','l');
|
|
|
|
chunkSize = ChunkSize;
|
|
|
|
dwIdentifier = 0;
|
|
|
|
dwText = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LabelChunk()
|
|
|
|
{
|
|
|
|
chunkID = mmioFOURCC('l','a','b','l');
|
|
|
|
chunkSize = 0;
|
|
|
|
dwIdentifier = 0;
|
|
|
|
dwText = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
~LabelChunk() { delete[] dwText; }
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|