2126 lines
54 KiB
C++
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;
|
|
}
|
|
|