/* *) 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; nBytesNotDonePlaying() ) // { // 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; }