windows-nt/Source/XPSP1/NT/net/tapi/explib/wave.cpp
2020-09-26 16:20:57 +08:00

2126 lines
54 KiB
C++

/*
*) Functions to simplify recording & playback of wave file/data to a line/phone
*) Put the code in TAPI32L.LIB? Then only apps that need it, get it
+) tapiMakeNoise(
DWORD Device Type: PHONE/LINE/WAVE, etc?
HANDLE Device Handle,
DWORD NoiseType: BUFFER/FILENAME/HFILE(readfile directly?)/MMIOHANDLE
HANDLE hArray - array of type NoiseTypes that are to be played serially
DWORD Flags:
fSYNC
fSTOP_EXISTING_PLAYING_IF_ANY
);
-) How to handle hardware assist? IE: Hey, hardware, play prompt #7 - how would an
app know how/when to request that?
-) What about proprietary wave formats? How to know what proprietary formats the hardware supports?
Just try it?
-) What about conversions? How to know what conversions the hardware can do
-) How about a notification method? Such that an app can know when the wave is done.
-)
*/
#define STRICT
#include "windows.h"
#include "windowsx.h"
#include "mmsystem.h"
#include "tapi.h"
#if DBG
VOID
DbgPrtWave(
IN DWORD dwDbgLevel,
IN PTCHAR lpszFormat,
IN ...
)
/*++
Routine Description:
Formats the incoming debug message & calls DbgPrint
Arguments:
DbgLevel - level of message verboseness
DbgMessage - printf-style format string, followed by appropriate
list of arguments
Return Value:
--*/
{
// if (dwDbgLevel <= gdwDebugLevel)
{
TCHAR buf[1280];
va_list ap;
va_start(ap, lpszFormat);
wsprintf(buf, TEXT("CallUpW (0x%08lx) - "), GetCurrentThreadId() );
wvsprintf (&buf[23],
lpszFormat,
ap
);
lstrcat (buf, TEXT("\n"));
OutputDebugString (buf);
va_end(ap);
}
}
#define WDBGOUT(_x_) DbgPrtWave _x_
#else
#define WDBGOUT(_x_)
#endif
//****************************************************************************
//****************************************************************************
//****************************************************************************
unsigned long WINAPI WaveThread( LPVOID junk );
void CALLBACK WaveOutCallback(
HWAVE hWave, // handle of waveform device
UINT uMsg, // sent message
DWORD dwInstance, // instance data
DWORD dwParam1, // application-defined parameter
DWORD dwParam2 // application-defined parameter
);
enum
{
DEVICE_WAVEID,
DEVICE_WAVEHANDLE,
DEVICE_HLINE,
DEVICE_HPHONE,
DEVICE_HCALL
};
enum
{
SOURCE_WAVEFILE,
SOURCE_MSDOSFILE,
SOURCE_MEM
};
class WaveDevice;
class WaveOperation;
#define OPERATIONSTATUS_DONTPLAYTHIS 0x00000001
#define MAX_NUM_BUFFERS (8)
#define BUFFER_SIZE (8192)
typedef struct {
ULONG uBufferLength;
WaveOperation * poWaveOperation;
PBYTE pBuffer;
} MISCINFO;
//****************************************************************************
//****************************************************************************
LONG gfInited = 0;
BOOLEAN gfShutdown = FALSE;
WaveDevice *gpoWaveDeviceList = NULL;
HANDLE ghFreeBufferEvent = 0;
HANDLE ghWaveThread = NULL;
MISCINFO *gDoneBuffersToBeProcessed[MAX_NUM_BUFFERS + 1];
CRITICAL_SECTION gCriticalSection;
//****************************************************************************
//****************************************************************************
//****************************************************************************
class WaveOperation
{
public:
DWORD dwSourceType;
union
{
PTSTR psz;
PBYTE pb;
HANDLE h;
LONG l;
} SourceThing;
class WaveOperation * pNextWaveOperationInList;
class WaveDevice * poWaveDevice;
HANDLE hSyncEvent;
DWORD dwStatus;
DWORD cFileSize;
DWORD cDataRemaining;
DWORD cDataDonePlaying;
BOOLEAN fInited;
LONG WaveOperation::InitOperation(
class WaveDevice * poWaveDevice,
DWORD dwSoundTypeIn,
LONG lSourceThing
);
virtual LONG InitSpecific( void ) = 0;
virtual ULONG GetData( PBYTE pBuffer, ULONG uBufferSize ) = 0;
virtual void FreeSpecific( void ) = 0;
inline WaveOperation * GetpNext();
inline void SetpNext( WaveOperation * );
inline HANDLE GetSyncEvent();
inline void SetSyncEvent( HANDLE );
inline void ProcessDoneBuffer( MISCINFO * pMiscInfo );
inline ULONG BytesNotDonePlaying( void );
};
//****************************************************************************
//****************************************************************************
//****************************************************************************
class WaveDevice
{
ULONG uDeviceId;
DWORD dwDeviceType;
HANDLE hDevice;
HWAVEOUT hWaveOut;
CRITICAL_SECTION CriticalSection;
ULONG uUsageCount;
class WaveDevice * pNextWaveDeviceInList;
class WaveOperation * CurrentWaveOperation;
class WaveOperation * LastWaveOperation;
ULONG Head;
ULONG Tail;
ULONG NumFreeBuffers;
ULONG cBufferSize;
PBYTE FreeQueue[MAX_NUM_BUFFERS];
WAVEHDR WaveHeader[MAX_NUM_BUFFERS];
MISCINFO MiscInfo[MAX_NUM_BUFFERS];
DWORD dwStatusBits;
public:
inline ULONG GetNumFreeBuffers( void );
DWORD GetStatus( void );
void TerminateAllOperations( void );
LONG KillWaveDevice( BOOLEAN fWaitForThreadTermination );
LONG CloseWaveDevice();
LONG InitWaveDevice( ULONG uDeviceId );
LONG OpenWaveDevice( WAVEFORMATEX * pWaveFormat );
inline WaveDevice * GetpNext();
inline void SetpNext( WaveDevice * );
LONG QueueOperation( class WaveOperation * );
class WaveOperation * NextOperation();
ULONG PlaySomeData( BOOL fPrimeOnly );
inline void ReturnToFreeBufferQueue( PBYTE pBuffer );
inline void IncrementBytesPlayed( ULONG cCount );
inline ULONG GetWaveDeviceId( void );
inline CRITICAL_SECTION * GetCriticalSection( void );
// static void CALLBACK WaveOutCallback(
// HWAVE hWave, // handle of waveform device
// UINT uMsg, // sent message
// DWORD dwInstance, // instance data
// DWORD dwParam1, // application-defined parameter
// DWORD dwParam2 // application-defined parameter
// );
void IncUsageCount( void );
void DecUsageCount( void );
UINT GetUsageCount( void );
};
//****************************************************************************
LONG WaveDevice::InitWaveDevice( ULONG uDevId )
{
LONG lResult = 0;
ULONG n;
WDBGOUT((4, "Entering InitWaveDevice"));
//
// Alloc some buffers
//
Head = 0;
Tail = 0;
NumFreeBuffers = 0;
uUsageCount = 0;
dwStatusBits = 0;
cBufferSize = BUFFER_SIZE;
uDeviceId = uDevId;
for ( n = 0; n < MAX_NUM_BUFFERS; n++ )
{
FreeQueue[n] = (PBYTE)LocalAlloc(LPTR, cBufferSize);
if ( NULL == FreeQueue[n] )
{
WDBGOUT((1, "Mem alloc failed. Size= 0x%08lx", cBufferSize));
while ( n )
{
LocalFree( FreeQueue[n-1] );
n--;
}
return( LINEERR_NOMEM );
}
NumFreeBuffers++;
}
InitializeCriticalSection( &CriticalSection );
CurrentWaveOperation = NULL;
LastWaveOperation = NULL;
return( lResult );
}
//****************************************************************************
inline ULONG WaveDevice::GetWaveDeviceId( void )
{
return uDeviceId;
}
//****************************************************************************
inline ULONG WaveDevice::GetNumFreeBuffers( void )
{
return NumFreeBuffers;
}
//****************************************************************************
LONG WaveDevice::OpenWaveDevice( WAVEFORMATEX * pWaveFormat )
{
ULONG u;
LONG lResult;
WDBGOUT((4, "Entering OpenWaveDevice"));
lResult = (LONG)waveOutOpen(
&hWaveOut,
uDeviceId,
pWaveFormat,
(DWORD)WaveOutCallback,
(DWORD)this,
CALLBACK_FUNCTION | WAVE_MAPPED
);
//{
// TCHAR buf[500];
// wsprintf( buf, "woo on %lx ret=0x%lx", uDeviceId, lResult);
// MessageBox(GetFocus(), buf, buf, MB_OK);
//}
if ( lResult )
{
WDBGOUT((1, "waveOutOpen returned 0x%08lx", lResult ));
return( LINEERR_NOMEM); //TODO LATER: Diff ret codes?
}
for ( u = 0; u < NumFreeBuffers; u++ )
{
WaveHeader[u].lpData = (LPSTR)FreeQueue[u];
WaveHeader[u].dwBufferLength = cBufferSize;
WaveHeader[u].dwFlags = 0;
lResult = waveOutPrepareHeader(
hWaveOut,
&(WaveHeader[u]),
sizeof(WAVEHDR)
);
if ( lResult )
{
WDBGOUT((1, TEXT("waveOutPrepareHeader returned 0x%08lx"), lResult ));
return( LINEERR_NOMEM); //TODO LATER: Diff ret codes?
}
}
WDBGOUT((4, TEXT("Leaving OpenWaveDevice result = 0x0")));
return( 0 );
}
////****************************************************************************
//LONG WaveDevice::RestartDevice( WAVEFORMATEX * pWaveFormat )
//{
// ULONG n;
//
//
// WDBGOUT((4, "Entering RestartDevice"));
//
//
// // Reset wave device
// WDBGOUT((4, TEXT("Resetting the wave device...")));
// waveOutReset( hWaveOut );
//
// //
// // Wait until all of the outstanding buffers are back.
// //
// WDBGOUT((4, TEXT("Waiting for all buffers to be returned...")));
// while ( NumFreeBuffers < MAX_NUM_BUFFERS )
// {
// Sleep(0);
// }
//
// WDBGOUT((4, TEXT("Closing the wave device...")));
// waveOutClose( hWaveOut );
//
//
//
// return( 0 );
//}
//
//****************************************************************************
LONG WaveDevice::CloseWaveDevice()
{
WDBGOUT((4, "Entering CloseWaveDevice"));
// Reset wave device
WDBGOUT((4, TEXT("Resetting the wave device...")));
waveOutReset( hWaveOut );
//
// Wait until all of the outstanding buffers are back.
//
WDBGOUT((4, TEXT("Waiting for all buffers to be returned...")));
while ( NumFreeBuffers < MAX_NUM_BUFFERS )
{
Sleep(0);
}
WDBGOUT((4, TEXT("Closing the wave device...")));
waveOutClose( hWaveOut );
return( 0 );
}
//****************************************************************************
LONG WaveDevice::KillWaveDevice( BOOLEAN fWaitForThreadTermination )
{
ULONG n;
WaveDevice * poTempDevice;
WDBGOUT((4, "Entering KillWaveDevice"));
// Reset wave device
WDBGOUT((4, TEXT("Resetting the wave device...")));
waveOutReset( hWaveOut );
//
// Wait until all of the outstanding buffers are back.
//
WDBGOUT((4, TEXT("Waiting for all buffers to be returned...")));
while ( NumFreeBuffers < MAX_NUM_BUFFERS )
{
Sleep(0);
}
WDBGOUT((4, TEXT("Closing the wave device...")));
waveOutClose( hWaveOut );
//
// Free the memory for all of the buffers
//
for ( n=0; n<MAX_NUM_BUFFERS; n++ )
{
LocalFree( FreeQueue[n] );
FreeQueue[n] = NULL;
}
//
// Remove the device from the global list
//
poTempDevice = gpoWaveDeviceList;
if ( poTempDevice == this )
{
gpoWaveDeviceList = GetpNext();
}
else
{
while ( poTempDevice
&&
( (*poTempDevice).GetpNext() != this )
)
{
poTempDevice =(*poTempDevice).GetpNext();
}
//
// The next one in the list is it. Remove the link.
//
if ( poTempDevice != NULL )
{
//
// Adjust the list pointers
//
(*poTempDevice).SetpNext( GetpNext() );
}
}
DeleteCriticalSection( &CriticalSection );
delete this;
//
// Are all of the devices dead and buried?
//
if ( NULL == gpoWaveDeviceList )
{
gfShutdown = TRUE;
//TODO NOW: fix this gfInited = 0;
//
// Signal the other thread to come down
//
SetEvent( ghFreeBufferEvent );
//
// Wait 'till the thread is dead?
//
if ( fWaitForThreadTermination )
{
WaitForSingleObject( ghWaveThread, INFINITE );
}
CloseHandle( ghWaveThread );
//
// Zero this so we start fresh next time.
//
// ghWaveThread = NULL;
}
return( 0 );
}
//****************************************************************************
inline DWORD WaveDevice::GetStatus( void )
{
return dwStatusBits;
}
//****************************************************************************
inline void WaveDevice::TerminateAllOperations( void )
{
WaveOperation *poWaveOperation;
WDBGOUT((3, TEXT("Entering TerminateAllOps")));
EnterCriticalSection( &CriticalSection );
poWaveOperation = CurrentWaveOperation;
while ( poWaveOperation )
{
WDBGOUT((4, TEXT("Tainting oper: 0x%08lx"), poWaveOperation ));
(*poWaveOperation).dwStatus |= OPERATIONSTATUS_DONTPLAYTHIS;
poWaveOperation = (*poWaveOperation).GetpNext();
}
//
// Reset wave device to force all the buffers in
//
WDBGOUT((4, TEXT("Resetting the wave device...")));
waveOutReset( hWaveOut );
LeaveCriticalSection( &CriticalSection );
WDBGOUT((3, TEXT("Leaving TerminateAllOps")));
}
//****************************************************************************
inline CRITICAL_SECTION * WaveDevice::GetCriticalSection( void )
{
return &CriticalSection;
}
//****************************************************************************
inline WaveDevice * WaveDevice::GetpNext()
{
return( pNextWaveDeviceInList );
}
//****************************************************************************
inline void WaveDevice::SetpNext(WaveDevice * pWaveDevice)
{
pNextWaveDeviceInList = pWaveDevice;
}
//****************************************************************************
inline void WaveDevice::IncUsageCount( void )
{
uUsageCount++;
};
//****************************************************************************
inline void WaveDevice::DecUsageCount( void )
{
uUsageCount--;
};
//****************************************************************************
inline UINT WaveDevice::GetUsageCount( void )
{
return uUsageCount;
};
//****************************************************************************
LONG WaveDevice::QueueOperation( class WaveOperation *poNewWaveOperation )
{
WDBGOUT((3, TEXT("Entering QueueOperation")));
EnterCriticalSection( &CriticalSection );
(*poNewWaveOperation).SetpNext( NULL );
//
// Add operation to list
//
if ( LastWaveOperation )
{
(*LastWaveOperation).SetpNext( poNewWaveOperation );
}
LastWaveOperation = poNewWaveOperation;
if ( NULL == CurrentWaveOperation )
{
CurrentWaveOperation = poNewWaveOperation;
}
LeaveCriticalSection( &CriticalSection );
WDBGOUT((4, TEXT("Created new oper: 0x%08lx"), poNewWaveOperation));
WDBGOUT((3, TEXT("Leaving QueueOperation")));
return( 0 );
}
//****************************************************************************
class WaveOperation * WaveDevice::NextOperation()
{
//
// This function will get rid of the operation at the top of this wave
// device's operation queue, and will update the queue to reflect the next
// as now the first.
//
WDBGOUT((3, TEXT("Entering NextOperation")));
EnterCriticalSection( &CriticalSection );
if ( CurrentWaveOperation )
{
WaveOperation * poWaveOperation;
WaveOperation * poTempOperation;
poWaveOperation = (*CurrentWaveOperation).GetpNext();
delete CurrentWaveOperation;
while ( poWaveOperation )
{
//
// If we can play this operation, break outta this loop
//
if ( !( (*poWaveOperation).dwStatus & OPERATIONSTATUS_DONTPLAYTHIS) )
{
WDBGOUT((55, TEXT("How much break?")));
break;
}
//
// We're not supposed to play this operation
//
if ( (*poWaveOperation).hSyncEvent )
{
WDBGOUT((5, TEXT("Caller was waiting. Signaling...")));
SetEvent( (*poWaveOperation).hSyncEvent );
}
poTempOperation = (*poWaveOperation).GetpNext();
delete poWaveOperation;
poWaveOperation = poTempOperation;
}
WDBGOUT((55, TEXT("Not too much")));
CurrentWaveOperation = poWaveOperation;
}
WDBGOUT((55, TEXT("was it Too much?")));
//
// The CurrentWaveOperation may have been "NULLED" out by the previous stuff
//
if ( NULL == CurrentWaveOperation )
{
LastWaveOperation = NULL;
}
LeaveCriticalSection( &CriticalSection );
WDBGOUT((4, TEXT("Leaving NextOperation - returning 0x%08lx"), CurrentWaveOperation));
return( CurrentWaveOperation );
}
//****************************************************************************
inline void WaveDevice::ReturnToFreeBufferQueue( PBYTE pBuffer )
{
FreeQueue[Tail] = pBuffer;
//
// If we're at the end of the list, wrap.
//
Tail = ( Tail + 1 ) % MAX_NUM_BUFFERS;
NumFreeBuffers++;
}
//****************************************************************************
inline void WaveDevice::IncrementBytesPlayed( ULONG cCount )
{
// //
// // If there is an operation on the dying queue, this must be from it
// //
// if ( DyingWaveOperation )
// {
// //
// // Is it dead yet?
// //
// if ( 0 == DyingWaveOperation->BytesNotDonePlaying() )
// {
// WaveOperation * poNextOperation;
//
// EnterCriticalSection( &CriticalSection );
//
// //
// // Yes, it's dead.
// //
// poNextOperation = DyingWaveOperation->GetpNext();
//
// //
// // Was the caller waiting (ie: was it sync) ?
// //
// if ( (*DyingWaveOperation).GetSyncEvent() )
// {
// SetEvent( (*DyingWaveOperation).GetSyncEvent() );
// }
//
// delete DyingWaveOperation;
//
// DyingWaveOperation = poNextOperation;
//
// LeaveCriticalSection( &CriticalSection );
// }
// }
//
//TODO LATER: Keep a total count of bytes played out this device?
}
//****************************************************************************
//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG WaveOperation::InitOperation(
class WaveDevice * poWaveDeviceIn,
DWORD dwSourceTypeIn,
LONG lSourceThing
)
{
WDBGOUT((4, TEXT("Entering InitOperation")));
dwSourceType = dwSourceTypeIn;
SourceThing.l = lSourceThing;
poWaveDevice = poWaveDeviceIn;
pNextWaveOperationInList = NULL;
(*poWaveDevice).IncUsageCount();
dwStatus = 0;
fInited = FALSE;
return(0);
}
//****************************************************************************
inline HANDLE WaveOperation::GetSyncEvent()
{
return( hSyncEvent );
}
//****************************************************************************
inline void WaveOperation::SetSyncEvent( HANDLE hEvent )
{
hSyncEvent = hEvent;
return;
}
//****************************************************************************
inline WaveOperation * WaveOperation::GetpNext()
{
return( pNextWaveOperationInList );
}
//****************************************************************************
inline void WaveOperation::SetpNext(WaveOperation * pWaveOperation)
{
pNextWaveOperationInList = pWaveOperation;
}
//****************************************************************************
inline void WaveOperation::ProcessDoneBuffer( MISCINFO * pMiscInfo )
{
ULONG nBytesQueued;
WDBGOUT((3, TEXT("Entering ProcessDoneBuffer")));
cDataDonePlaying += pMiscInfo->uBufferLength;
WDBGOUT((11, TEXT("Now - size=0x%08lx done=0x%08lx"),
cFileSize,
cDataDonePlaying));
(*poWaveDevice).IncrementBytesPlayed( pMiscInfo->uBufferLength );
(*poWaveDevice).ReturnToFreeBufferQueue( pMiscInfo->pBuffer );
//
// Has someone decided this wave should stop?
//
if ( dwStatus & OPERATIONSTATUS_DONTPLAYTHIS )
{
if ( (*poWaveDevice).GetNumFreeBuffers() != MAX_NUM_BUFFERS )
{
WDBGOUT((4, TEXT("Bailing from ProcessDoneBuffer - dontplay")));
return;
}
cDataDonePlaying = cFileSize;
}
//
// Is this thing already dead?
//
if ( cDataDonePlaying >= cFileSize )
{
WDBGOUT((4, TEXT("Done playing this:0x%08lx"), this ));
//
// Was the caller waiting (ie: was it sync) ?
//
if ( hSyncEvent )
{
WDBGOUT((5, TEXT("Caller was waiting. Signaling...")));
SetEvent( hSyncEvent );
}
//TODO LATER: PERFORMANCE: If the next format is the same as this one, don't close the device
(*poWaveDevice).CloseWaveDevice();
(*poWaveDevice).DecUsageCount();
EnterCriticalSection( &gCriticalSection );
//
// Was this the last oper?
//
if ( (*poWaveDevice).GetUsageCount() == 0 )
{
WDBGOUT((4, TEXT("Last oper out...")));
(*poWaveDevice).KillWaveDevice(FALSE);
}
else
{
WaveOperation * pNewOperation;
//
// Move up the next operation
//
while ( TRUE )
{
pNewOperation = (*poWaveDevice).NextOperation();
if ( NULL == pNewOperation )
{
if ( (*poWaveDevice).GetUsageCount() == 0 )
{
WDBGOUT((4, TEXT("No more ops to run...")));
(*poWaveDevice).KillWaveDevice(FALSE);
}
//
// All operations done. Go away.
//
WDBGOUT((3, TEXT("All operations seem to be done...")));
break;
}
WDBGOUT((3, TEXT("Playing data from new op...")));
nBytesQueued = (*poWaveDevice).PlaySomeData( FALSE );
if ( nBytesQueued )
{
//
// There were some bytes played. Break the loop...
//
break;
}
//
// Was the caller waiting (ie: was it sync) ?
//
if ( pNewOperation->hSyncEvent )
{
WDBGOUT((3, TEXT("No data in new op and caller is waiting...")));
SetEvent( pNewOperation->hSyncEvent );
}
//
// Update the counter. This op is, for all intents and purposes, done.
//
(*poWaveDevice).DecUsageCount();
WDBGOUT((3, TEXT("No data in new op. Looking for next...")));
}
}
FreeSpecific();
delete this;
LeaveCriticalSection( &gCriticalSection );
}
else
{
WDBGOUT((3, TEXT("Playing data from same op...")));
(*poWaveDevice).PlaySomeData( FALSE );
}
WDBGOUT((3, TEXT("Leaving ProcessDoneBuffer")));
}
//****************************************************************************
//****************************************************************************
inline ULONG WaveOperation::BytesNotDonePlaying( void )
{
return cFileSize - cDataDonePlaying;
}
//****************************************************************************
//****************************************************************************
class BufferWave: public WaveOperation
{
PBYTE pData; // Pointer to the data to play
PBYTE pCurrentPointer;
public:
LONG BufferWave::InitSpecific( void );
ULONG GetData( PBYTE pBuffer, ULONG uBufferSize );
void BufferWave::FreeSpecific( void );
};
//****************************************************************************
LONG BufferWave::InitSpecific( void )
{
pData = SourceThing.pb;
pCurrentPointer = pData;
return(0);
}
//****************************************************************************
ULONG BufferWave::GetData( PBYTE pBuffer, ULONG uBufferSize )
{
ULONG uBytesToPlay;
uBytesToPlay = (cDataRemaining > uBufferSize) ?
uBufferSize :
cDataRemaining;
cDataRemaining -= uBytesToPlay;
memcpy( pBuffer, pCurrentPointer, uBytesToPlay );
pCurrentPointer += uBytesToPlay;
return( uBytesToPlay );
}
//****************************************************************************
void BufferWave::FreeSpecific( void )
{
return;
}
//****************************************************************************
//****************************************************************************
class WaveFile: public WaveOperation
{
HMMIO hmmio;
public:
LONG WaveFile::InitSpecific( void );
ULONG GetData( PBYTE pBuffer, ULONG uBufferSize );
void WaveFile::FreeSpecific( void );
};
//****************************************************************************
LONG WaveFile::InitSpecific( void )
{
MMCKINFO mmckinfoParent; /* parent chunk information structure */
MMCKINFO mmckinfoSubchunk; /* subchunk information structure */
DWORD dwFmtSize; /* size of "fmt" chunk */
WAVEFORMATEX Format; /* pointer to memory for "fmt" chunk */
LONG lResult;
WDBGOUT((4, TEXT("Entering WaveFile::InitSpecific")));
hmmio = mmioOpen(
SourceThing.psz,
NULL,
MMIO_READ
);
//
// Did the open go ok?
//
if ( NULL == hmmio )
{
//
// Nope.
//
WDBGOUT((1, TEXT("Error during mmioOpen of [%s] - err=0x%08lx"),
(SourceThing.psz == NULL) ? "" : SourceThing.psz,
GetLastError() ));
return LINEERR_OPERATIONFAILED;
}
/*
* Locate a "RIFF" chunk with a "WAVE" form type
* to make sure the file is a WAVE file.
*/
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
WDBGOUT((11, TEXT("Descend WAVE")));
if ( mmioDescend(
hmmio,
(LPMMCKINFO) &mmckinfoParent,
NULL,
MMIO_FINDRIFF)
)
{
WDBGOUT((1, TEXT("This is not a WAVE file - [%s]"),
(SourceThing.psz == NULL) ? "" : SourceThing.psz));
mmioClose( hmmio, 0);
return LINEERR_INVALPARAM;
}
/*
* Find the "fmt " chunk (form type "fmt "); it must be
* a subchunk of the "RIFF" parent chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
WDBGOUT((11, TEXT("Descend FMT")));
if ( mmioDescend(
hmmio,
&mmckinfoSubchunk,
&mmckinfoParent,
MMIO_FINDCHUNK)
)
{
WDBGOUT((1, TEXT("WAVE file has no \"fmt\" chunk")));
mmioClose(hmmio, 0);
return LINEERR_INVALPARAM;
}
/*
* Get the size of the "fmt " chunk--allocate and lock memory for it.
*/
dwFmtSize = mmckinfoSubchunk.cksize;
WDBGOUT((11, TEXT("read fmt")));
/* Read the "fmt " chunk. */
mmioRead(
hmmio,
(HPSTR)&Format,
sizeof(Format) );
// {
// WDBGOUT((1, TEXT("Failed to read format chunk.")));
// mmioClose(pMyWaveFile->hmmio, 0);
// return 1;
// }
WDBGOUT((11, TEXT("Ascend fmt")));
/* Ascend out of the "fmt " subchunk. */
mmioAscend(hmmio, &mmckinfoSubchunk, 0);
/*
* Find the data subchunk. The current file position
* should be at the beginning of the data chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
WDBGOUT((11, TEXT("Descend DATA")));
if ( mmioDescend(
hmmio,
&mmckinfoSubchunk,
&mmckinfoParent,
MMIO_FINDCHUNK)
)
{
WDBGOUT((1, TEXT("WAVE file has no data chunk.")));
mmioClose(hmmio, 0);
return LINEERR_INVALPARAM;
}
/* Get the size of the data subchunk. */
cFileSize = mmckinfoSubchunk.cksize;
cDataRemaining = mmckinfoSubchunk.cksize;
cDataDonePlaying = 0;
WDBGOUT((11, TEXT("OpenWaveDev")));
lResult = poWaveDevice->OpenWaveDevice( &Format );
// if ( cDataRemaining == 0L)
// {
// WDBGOUT((1, TEXT("The data chunk contains no data.")));
// mmioClose(hmmio, 0);
// return 0; //TODO LATER: Right? It's not an error...
// It'll just get 0 bytes on the first read...
// }
return( lResult );
}
//****************************************************************************
ULONG WaveFile::GetData( PBYTE pBuffer, ULONG uBufferSize )
{
ULONG uBytesToPlay;
ULONG uBytesRead;
WDBGOUT((11, TEXT("Entering WaveFile::GetData")));
//
// Have we done anything yet?
//
if ( !fInited )
{
if ( InitSpecific() )
{
return( 0 );
}
fInited = TRUE;
}
uBytesToPlay = (cDataRemaining > uBufferSize) ?
uBufferSize :
cDataRemaining;
if ( 0 == uBytesToPlay )
{
return 0;
}
/* Read the waveform data subchunk. */
uBytesRead = mmioRead(
hmmio,
(LPSTR)pBuffer,
uBytesToPlay
);
if ( uBytesRead != uBytesToPlay )
{
WDBGOUT((1, TEXT("Failed to properly read data chunk.")));
mmioClose(hmmio, 0);
return 0;
}
cDataRemaining -= uBytesToPlay;
return( uBytesToPlay );
}
//****************************************************************************
void WaveFile::FreeSpecific( void )
{
mmioClose(hmmio, 0);
return;
}
//****************************************************************************
//****************************************************************************
class DosFile: public WaveOperation
{
HANDLE hFile;
public:
LONG DosFile::InitSpecific( void );
ULONG GetData( PBYTE pBuffer, ULONG uBufferSize );
void DosFile::FreeSpecific( void );
};
//****************************************************************************
LONG DosFile::InitSpecific( void )
{
BOOL fResult;
// WIN32_FILE_ATTRIBUTE_DATA FileInfo;
BY_HANDLE_FILE_INFORMATION FileInfo;
hFile = CreateFile(
SourceThing.psz,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( 0 == hFile )
{
WDBGOUT((1, TEXT("Error doing OpenFile( lpszName ) GetLastError=0x%)8lx"),
SourceThing.psz, GetLastError() ));
return( LINEERR_OPERATIONFAILED );
}
// fResult = GetFileAttributesEx( SourceThing.psz,
// GetFileExInfoStandard,
// (PVOID) &FileInfo
// );
fResult = GetFileInformationByHandle( hFile, &FileInfo );
if ( fResult )
{
//TODO LATER: Handle > 4 gig files
//
// Uh, we don't really handle gigabyte files...
//
if ( FileInfo.nFileSizeHigh )
{
cFileSize = (DWORD)-1;
cDataRemaining = (DWORD)-1;
}
else
{
cFileSize = FileInfo.nFileSizeLow;
cDataRemaining = FileInfo.nFileSizeLow;
}
}
else
{
cFileSize = 0;
cDataRemaining = 0;
}
cDataDonePlaying = 0;
return(0);
}
//****************************************************************************
ULONG DosFile::GetData( PBYTE pBuffer, ULONG uBufferSize )
{
BOOL fResult;
UINT uBytesRead = 0;
fResult = ReadFile( hFile,
pBuffer,
uBufferSize,
(LPDWORD)&uBytesRead,
NULL
);
if ( fResult )
{
if ( 0 == uBytesRead )
{
//
// We're at the end of the file
//
cDataRemaining = 0;
}
else
{
cDataRemaining -= uBytesRead;
}
}
return( uBytesRead );
}
//****************************************************************************
void DosFile::FreeSpecific( void )
{
CloseHandle( hFile );
return;
}
//****************************************************************************
//****************************************************************************
//****************************************************************************
ULONG WaveDevice::PlaySomeData( BOOL fPrimeOnly )
{
ULONG uBufferedBytes = 0;
ULONG uTotalQueuedSize = 0;
PBYTE pBuffer = NULL;
LONG lResult;
CRITICAL_SECTION *pCriticalSection;
WDBGOUT((3, TEXT("Entering PlaySomeData")));
pCriticalSection = &CriticalSection;
EnterCriticalSection( pCriticalSection );
if ( NULL != CurrentWaveOperation )
{
//
// Is it OK to play this thing?
//
if ( !((*CurrentWaveOperation).dwStatus & OPERATIONSTATUS_DONTPLAYTHIS) )
{
while ( NumFreeBuffers )
{
uBufferedBytes = (*CurrentWaveOperation).GetData( FreeQueue[Head], cBufferSize );
WDBGOUT((11, "GetData on 0x%08lx gave %ld bytes for buffer #%d",
CurrentWaveOperation,
uBufferedBytes,
Head));
if ( 0 == uBufferedBytes )
{
WDBGOUT((10, TEXT("breakin 'cause 0 bytes...")));
break;
}
WDBGOUT((10, TEXT("past if...")));
uTotalQueuedSize += uBufferedBytes;
MiscInfo[Head].uBufferLength = uBufferedBytes;
MiscInfo[Head].poWaveOperation = CurrentWaveOperation;
MiscInfo[Head].pBuffer = FreeQueue[Head];
WaveHeader[Head].dwUser = (DWORD) &MiscInfo[Head];
WaveHeader[Head].dwBufferLength = uBufferedBytes;
lResult = waveOutWrite( hWaveOut,
&WaveHeader[Head],
sizeof(WAVEHDR)
);
if ( lResult )
{
//
// Something's wrong. Quit this operation.
//
uTotalQueuedSize = 0;
uBufferedBytes = 0;
WDBGOUT((1, TEXT("waveOutWrite returned 0x%08lx"), lResult));
break;
}
Head = (Head + 1) % MAX_NUM_BUFFERS;
NumFreeBuffers--;
//
// Are we just "priming" the pump?
//
// if ( fPrimeOnly )
// {
// WDBGOUT((4, TEXT("Leaving PlaySomeData - primed (size=%08ld)"), uTotalQueuedSize ));
// LeaveCriticalSection( pCriticalSection );
// return uTotalQueuedSize;
// }
}
}
#if DBG
else
{
WDBGOUT((10, TEXT("I've been asked not to play this operation (0x%08lx)"), CurrentWaveOperation));
}
#endif
WDBGOUT((10, TEXT("past while numfreebuffers...")));
//
// We got here because we're out of buffers, or the operation is done
//
if ( 0 != uBufferedBytes )
{
//
// Must be here because we ran out of buffers...
//
LeaveCriticalSection( pCriticalSection );
return( uTotalQueuedSize );
}
//
// We get here when the current operation is all done
// (or, at least, all of its remaining data is queued in the
// wave driver)
//
}
//
// If we got here, it's because we're out of things to do
//
LeaveCriticalSection( pCriticalSection );
WDBGOUT((4, TEXT("Leaving PlaySomeData - no currop (size=%08ld)"), uTotalQueuedSize ));
return uTotalQueuedSize;
// return( 0 );
}
//****************************************************************************
//****************************************************************************
//****************************************************************************
void CALLBACK WaveOutCallback(
HWAVE hWave, // handle of waveform device
UINT uMsg, // sent message
DWORD dwInstance, // instance data
DWORD dwParam1, // application-defined parameter
DWORD dwParam2 // application-defined parameter
)
{
UINT n;
switch ( uMsg )
{
case WOM_DONE:
{
class WaveDevice * poWaveDevice =
(class WaveDevice *)dwInstance;
MISCINFO * pMiscInfo = (MISCINFO *)((LPWAVEHDR)dwParam1)->dwUser;
WDBGOUT((11, TEXT("Got DoneWithBuff msg for 0x%08lx in 0x%08lx"),
*(LPDWORD)dwParam1,
dwParam1));
// EnterCriticalSection( &gBufferCriticalSection );
n = 0;
//TODO NOW: If this buffer won't fit, it'll get lost. This can easily happen
// when there are >1 wave devices playing.
while (
( n < MAX_NUM_BUFFERS )
&&
( gDoneBuffersToBeProcessed[n] != NULL )
)
{
n++;
}
gDoneBuffersToBeProcessed[n] = pMiscInfo;
// LeaveCriticalSection( &gBufferCriticalSection );
SetEvent( ghFreeBufferEvent );
}
break;
case WOM_OPEN:
WDBGOUT((11, TEXT("Got Waveout Open")));
break;
case WOM_CLOSE:
WDBGOUT((11, TEXT("Got Waveout Close")));
break;
}
}
//****************************************************************************
//****************************************************************************
//****************************************************************************
//LONG tapiMakeNoise(
// DWORD Device Type: PHONE/LINE/WAVE, etc?
// HANDLE Device Handle,
// DWORD NoiseType: BUFFER/FILENAME/HFILE(readfile directly?)/MMIOHANDLE
// HANDLE hArray - array of type NoiseTypes that are to be played serially
// DWORD Flags:
// fSYNC
// fSTOP_EXISTING_PLAYING_IF_ANY
// );
// SOME FLAGS FOR THIS FUNC
#define PLAY_SYNC 0x00000001
#define KILL_ALL_NOISE 0x80000000
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
LONG WINAPI tapiPlaySound(
DWORD dwDeviceType,
HANDLE hDevice,
DWORD dwSoundType,
HANDLE hArray,
DWORD dwFlags
)
{
HANDLE hSyncEvent = NULL;
class WaveDevice * poWaveDevice;
class WaveOperation * poWaveOperation;
LONG fAreWeInited;
LONG lResult = 0;
ULONG uNormalizedWaveId = 0;
// BOOLEAN fNeedToPrimeDevice = FALSE;
WDBGOUT((3, "Entering tapiPlaySound"));
WDBGOUT((5, " dwDeviceType: %ld", dwDeviceType));
WDBGOUT((5, " hDevice: 0x%08lx", hDevice));
WDBGOUT((5, " dwSoundType: %ld", dwSoundType));
WDBGOUT((5, " hArray: 0x%08lx", hArray));
WDBGOUT((5, " dwFlags: 0x%08lx", dwFlags));
fAreWeInited = InterlockedExchange(
&gfInited,
TRUE
);
if ( 0 == fAreWeInited )
{
InitializeCriticalSection( &gCriticalSection );
}
if ( 0 == ghFreeBufferEvent )
{
ghFreeBufferEvent = CreateEvent(
NULL,
FALSE,
FALSE,
NULL
);
if ( NULL == ghFreeBufferEvent )
{
WDBGOUT((1, "CreateEvent2 failed: GetLastError = 0x%08lx", GetLastError()));
return LINEERR_NOMEM;
}
}
//
// Normalize to a wave device (and validate dwDeviceType at the same time)
//
switch ( dwDeviceType )
{
case DEVICE_WAVEID:
{
uNormalizedWaveId = (ULONG) hDevice;
}
break;
case DEVICE_WAVEHANDLE:
{
}
break;
case DEVICE_HLINE:
case DEVICE_HCALL:
{
DWORD VarString[ 8 ] =
{
sizeof(VarString),
0,
0,
STRINGFORMAT_BINARY,
0,
0,
0
};
if ( 0 == (lResult = lineGetID(
(HLINE)hDevice,
0,
(HCALL)hDevice,
(DEVICE_HCALL == dwDeviceType) ?
LINECALLSELECT_CALL :
LINECALLSELECT_LINE,
(LPVARSTRING)&VarString,
TEXT("wave/out")
) ) )
{
uNormalizedWaveId = (DWORD) ((LPBYTE)VarString)[ ((LPVARSTRING)&VarString)->dwStringOffset ];
}
else
{
WDBGOUT((1, "lineGetID failed - 0x%08lx", lResult));
return LINEERR_INVALPARAM;
}
}
break;
case DEVICE_HPHONE:
{
}
break;
default:
WDBGOUT((1, "Invalid dwDeviceType (0x%08lx) passed in.", dwDeviceType));
return LINEERR_BADDEVICEID;
}
EnterCriticalSection( &gCriticalSection );
poWaveDevice = gpoWaveDeviceList;
while ( poWaveDevice )
{
if ( (*poWaveDevice).GetWaveDeviceId() == uNormalizedWaveId )
{
//
// We found it!
//
break;
}
//
// ...and I still haven't found what I'm lookin' for.
//
poWaveDevice = (*poWaveDevice).GetpNext();
}
//
// So, was it not in our list already?
//
if ( NULL == poWaveDevice )
{
//
// No, add a new device object to the list
//
poWaveDevice = new WaveDevice;
lResult = (*poWaveDevice).InitWaveDevice( uNormalizedWaveId );
if ( lResult )
{
WDBGOUT((1, TEXT("InitWaveDevice returned 0x%08lx"), lResult));
//TODO: Diff error codes for diff causes...
LeaveCriticalSection( &gCriticalSection );
return LINEERR_RESOURCEUNAVAIL;
}
(*poWaveDevice).SetpNext( gpoWaveDeviceList );
gpoWaveDeviceList = poWaveDevice;
}
//
// If the caller wants to cancel all currently queued and playing
// sound on this device, do it now
//
if ( KILL_ALL_NOISE & dwFlags )
{
(*poWaveDevice).TerminateAllOperations();
WDBGOUT((4, "Caller was asking to terminate the wave device. Done."));
// LeaveCriticalSection( &gCriticalSection );
//
// return( 0 );
}
// t-mperh 6/30 was all commented before - not sure why
//
//
// If the user passed in a NULL for hArray, we'll (for now?) assume
// he wants a no-op (or 'twas a TERMINATE request).
//
if ( NULL == hArray )
{
WDBGOUT((3, "Leaving tapiPlaySound - NULL thing"));
LeaveCriticalSection( &gCriticalSection );
return 0;
}
//**************************************************************
//NOTE: The above code fixed a problem of passing in NULL names.
// This caused an OPEN to fail and this stuff would get stuck.
// There must still be a bug that will show up when someone calls with
// a bad filename or a file that plays 0 bytes.
//**************************************************************
switch ( dwSoundType )
{
case SOURCE_WAVEFILE:
{
poWaveOperation = new WaveFile;
}
break;
case SOURCE_MSDOSFILE:
{
poWaveOperation = new DosFile;
}
break;
case SOURCE_MEM:
{
poWaveOperation = new BufferWave;
}
break;
default:
{
WDBGOUT((1, "Invalid dwSourceType - 0x%08lx", dwSoundType));
LeaveCriticalSection( &gCriticalSection );
return LINEERR_INVALPARAM;
}
}
if ( NULL == ghWaveThread )
{
DWORD dwThreadID;
ghWaveThread = CreateThread(
NULL,
0,
WaveThread,
NULL,
0,
&dwThreadID
);
if ( 0 != lResult )
{
WDBGOUT((1, "Create thread failed! GetLastError()=0x%lx", GetLastError() ));
LeaveCriticalSection( &gCriticalSection );
return LINEERR_NOMEM;
}
}
//
// Init global operation
//
(*poWaveOperation).InitOperation(
poWaveDevice,
dwSoundType,
(LONG)hArray
);
(*poWaveDevice).QueueOperation( poWaveOperation );
if ( dwFlags & PLAY_SYNC )
{
hSyncEvent = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
if ( NULL == hSyncEvent )
{
WDBGOUT((1, TEXT("CreateEvent failed: GetLastError = 0x%08lx"), GetLastError()));
delete poWaveOperation;
LeaveCriticalSection( &gCriticalSection );
return( LINEERR_NOMEM );
}
(*poWaveOperation).SetSyncEvent( hSyncEvent );
}
//
// If all of the buffers are idle, we'll have to prime...
//
if ( MAX_NUM_BUFFERS == (*poWaveDevice).GetNumFreeBuffers() )
{
WDBGOUT((4, TEXT("Priming")));
if ( 0 == (*poWaveDevice).PlaySomeData( TRUE ) )
{
WaveOperation * poWaveOperation;
WDBGOUT((4, TEXT("No data played for this wave!")));
poWaveOperation = (*poWaveDevice).NextOperation();
while (poWaveOperation)
{
if ( (*poWaveDevice).PlaySomeData(TRUE) )
{
break;
}
poWaveOperation = (*poWaveDevice).NextOperation();
}
//
// If fNeedToPrimeDevice was true, this must be the first (and only,
// since we're still in the critical section) operation
// And, since there was no data (or we failed for any reason),
// we should shut down the wave device here.
// Now leave the critical section so we can wait for the WAVETHREAD
// to finish and so that thread can do work to clean up
LeaveCriticalSection( &gCriticalSection );
(*poWaveDevice).KillWaveDevice(TRUE);
EnterCriticalSection( &gCriticalSection );
//
// Fake out the event
//
if ( hSyncEvent )
{
WDBGOUT((5, TEXT("Faking hSyncEvent...")));
SetEvent( hSyncEvent );
}
}
}
#if DBG
else
{
WDBGOUT((4, TEXT("Not priming because %ln buffers are out"),
(*poWaveDevice).GetNumFreeBuffers() ));
}
#endif
LeaveCriticalSection( &gCriticalSection );
if ( hSyncEvent )
{
WDBGOUT((5, TEXT("Waiting for the wave to finish (event=0x%08lx)"),
hSyncEvent));
WaitForSingleObject( hSyncEvent, INFINITE );
//
// When it gets back, the thing is done playing
//
CloseHandle( hSyncEvent );
}
WDBGOUT((4, TEXT("Leaving tapiPlaySound - retcode = 0x0")));
return( 0 );
}
#ifdef __cplusplus
} /* End Assume C declarations for C++ */
#endif /* __cplusplus */
//****************************************************************************
//****************************************************************************
//****************************************************************************
unsigned long WINAPI WaveThread( LPVOID junk )
{
UINT n;
WDBGOUT((3, "WaveThread starting..."));
do
{
WDBGOUT((3, "WaveThread waiting..."));
WaitForSingleObject( ghFreeBufferEvent, INFINITE );
//
// First, deal with any finished buffers
//
n = 0;
// while ( gDoneBuffersToBeProcessed[n] != NULL )
EnterCriticalSection( &gCriticalSection );
while ( n < MAX_NUM_BUFFERS )
{
if ( gDoneBuffersToBeProcessed[n] != NULL )
{
MISCINFO *pMiscInfo = gDoneBuffersToBeProcessed[n];
pMiscInfo->poWaveOperation->ProcessDoneBuffer( pMiscInfo );
gDoneBuffersToBeProcessed[n] = NULL;
}
n++;
}
LeaveCriticalSection( &gCriticalSection );
// poWaveDevice = gpoWaveDeviceList;
//
// while ( poWaveDevice )
// {
// UINT nBytesQueued = 0;
//
// while ( nBytesQueued == 0 )
// {
// //
// // Now play some new data
// //
// nBytesQueued = (*poWaveDevice).PlaySomeData( FALSE );
//
// //
// // And is the entire wave done?
// //
// if ( 0 == nBytesQueued )
// {
// WaveOperation * poNewCurrent;
//
// poNewCurrent = (*poWaveDevice).NextOperation();
//
// if ( NULL == poNewCurrent )
// {
// if ( NULL == gpoWaveDeviceList )
// {
// gfShutdown = TRUE;
// gfInited = 0;
// }
// break;
// }
// }
// }
//
//
// poWaveDevice = (*poWaveDevice).GetpNext();
// }
} while ( !gfShutdown );
WDBGOUT((5, TEXT("Oh, I guess we're done now...")));
CloseHandle( ghFreeBufferEvent );
ghFreeBufferEvent = 0;
gfShutdown = FALSE;
WDBGOUT((3, TEXT("WaveThread ending...")));
ghWaveThread = NULL;
return 0;
}