// // Copyright (c) 1995-2000 Microsoft Corporation // // Waves.cpp // #include "stdafx.h" BOOL DSParseWaveResource(void *pvRes, WAVEFORMATEX **ppWaveHeader, BYTE ** ppbWaveData, DWORD *pdwWaveSize); static const char c_szWAV[] = "WAVE"; /////////////////////////////////////////////////////////////////////////////// // // DSGetWaveResource // /////////////////////////////////////////////////////////////////////////////// BOOL DSGetWaveResource(HMODULE hModule, LPCTSTR lpName, WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData, DWORD *pcbWaveSize) { HRSRC hResInfo; HGLOBAL hResData; void *pvRes; if (((hResInfo = FindResource(hModule, lpName, c_szWAV)) != NULL) && ((hResData = LoadResource(hModule, hResInfo)) != NULL) && ((pvRes = LockResource(hResData)) != NULL) && DSParseWaveResource(pvRes, ppWaveHeader, ppbWaveData, pcbWaveSize)) { return TRUE; } return FALSE; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// BOOL DSParseWaveResource(void *pvRes, WAVEFORMATEX **ppWaveHeader, BYTE ** ppbWaveData,DWORD *pcbWaveSize) { DWORD *pdw; DWORD *pdwEnd; DWORD dwRiff; DWORD dwType; DWORD dwLength; if (ppWaveHeader) { *ppWaveHeader = NULL; } if (ppbWaveData) { *ppbWaveData = NULL; } if (pcbWaveSize) { *pcbWaveSize = 0; } pdw = (DWORD *)pvRes; dwRiff = *pdw++; dwLength = *pdw++; dwType = *pdw++; if (dwRiff != mmioFOURCC('R', 'I', 'F', 'F')) goto exit; // not even RIFF if (dwType != mmioFOURCC('W', 'A', 'V', 'E')) goto exit; // not a WAV pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4); while (pdw < pdwEnd) { dwType = *pdw++; dwLength = *pdw++; switch (dwType) { case mmioFOURCC('f', 'm', 't', ' '): if (ppWaveHeader && !*ppWaveHeader) { if (dwLength < sizeof(WAVEFORMAT)) { goto exit; // not a WAV } *ppWaveHeader = (WAVEFORMATEX *)pdw; if ((!ppbWaveData || *ppbWaveData) && (!pcbWaveSize || *pcbWaveSize)) { return TRUE; } } break; case mmioFOURCC('d', 'a', 't', 'a'): if ((ppbWaveData && !*ppbWaveData) || (pcbWaveSize && !*pcbWaveSize)) { if (ppbWaveData) { *ppbWaveData = (LPBYTE)pdw; } if (pcbWaveSize) { *pcbWaveSize = dwLength; } if (!ppWaveHeader || *ppWaveHeader) { return TRUE; } } break; } pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1)); } exit: return FALSE; } ////////////////////////////////////////////////////////////////////////////// // CWavePlayer::CWavePlayer() { m_hWaveOutTone = NULL; m_hWaveOutRing = NULL; m_hMixer = NULL; m_fInitialized = FALSE; memset(m_fPlaying, 0, NUM_TONES * sizeof(BOOL)); m_lCurrentTone = -1; } ////////////////////////////////////////////////////////////////////////////// // CWavePlayer::~CWavePlayer() { // // We should have closed the wave device by now. // if ( m_fInitialized == TRUE ) { _ASSERTE( m_hWaveOutTone == NULL ); _ASSERTE( m_hWaveOutRing == NULL ); _ASSERTE( m_hMixer == NULL ); } } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::Initialize(void) { int i; // // It's wasteful to initialize twice, but it won't break anything. // _ASSERTE( m_fInitialized == FALSE ); // // Read all wave data resources. // We ignore the size and the wave header -- since these are our own // resources, we do not expect any surprises. // BOOL fResult; // // For each wave // for ( i = 0; i < NUM_WAVES; i ++ ) { // // Read the wave resource for this tone. // fResult = DSGetWaveResource( _Module.GetModuleInstance(), // HMODULE hModule, (LPCTSTR)IDR_WAV_DTMF0 + i, // LPCTSTR lpName, NULL, // WAVEFORMATEX **ppWaveHeader, &m_lpWaveform[i], // BYTE **ppbWaveData, &m_dwWaveformSize[i] // DWORD *pcbWaveSize ); if ( fResult == FALSE ) { return E_FAIL; } } // // We can now go ahead with the other methods. // m_fInitialized = TRUE; return S_OK; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::StartTone( long lTone ) { MMRESULT mmresult; HRESULT hr; if ( lTone < 0 ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( lTone > NUM_TONES ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hWaveOutTone == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } m_fPlaying[lTone] = TRUE; hr = ChangeTone(); if ( FAILED( hr ) ) { m_fPlaying[lTone] = FALSE; } return hr; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::StopTone( long lTone ) { if ( lTone < 0 ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( lTone > NUM_TONES ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hWaveOutTone == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } m_fPlaying[lTone] = FALSE; return ChangeTone(); } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::ChangeTone() { MMRESULT mmresult; for (int i=0; i < NUM_TONES; i++) { if (m_fPlaying[i]) { // // If already playing it, just return // if (m_lCurrentTone == i) { return S_OK; } // // Reset the wave device to flush out any pending buffers. // mmresult = waveOutReset( m_hWaveOutTone ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } // // Construct a wave header structure that will indicate what to play // in waveOutWrite. // ZeroMemory( & m_WaveHeaderTone, sizeof( m_WaveHeaderTone ) ); m_WaveHeaderTone.lpData = (LPSTR)m_lpWaveform[i]; m_WaveHeaderTone.dwBufferLength = m_dwWaveformSize[i]; m_WaveHeaderTone.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; m_WaveHeaderTone.dwLoops = (DWORD) -1; // // Submit the data to the wave device. The wave header indicated that // we want to loop. Need to prepare the header first, but it can // only be prepared after the device has been opened. // mmresult = waveOutPrepareHeader(m_hWaveOutTone, & m_WaveHeaderTone, sizeof(WAVEHDR) ); if ( mmresult != MMSYSERR_NOERROR ) { m_lCurrentTone = -1; return E_FAIL; } mmresult = waveOutWrite(m_hWaveOutTone, & m_WaveHeaderTone, sizeof(WAVEHDR) ); if ( mmresult != MMSYSERR_NOERROR ) { m_lCurrentTone = -1; return E_FAIL; } m_lCurrentTone = i; return S_OK; } } // // Stop the tone // if ( m_lCurrentTone != -1 ) { mmresult = waveOutReset( m_hWaveOutTone ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } m_lCurrentTone = -1; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // BOOL CWavePlayer::PlayingTone( long lTone ) { if ( lTone < 0 ) { _ASSERTE( FALSE ); return FALSE; } if ( lTone > NUM_TONES ) { _ASSERTE( FALSE ); return FALSE; } return m_fPlaying[lTone]; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::StartRing() { MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hWaveOutRing == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } // // Reset the wave device to flush out any pending buffers. // mmresult = waveOutReset( m_hWaveOutRing ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } // // Construct a wave header structure that will indicate what to play // in waveOutWrite. // ZeroMemory( & m_WaveHeaderRing, sizeof( m_WaveHeaderRing ) ); m_WaveHeaderRing.lpData = (LPSTR)m_lpWaveform[NUM_WAVES-1]; m_WaveHeaderRing.dwBufferLength = m_dwWaveformSize[NUM_WAVES-1]; m_WaveHeaderRing.dwFlags = 0; m_WaveHeaderRing.dwLoops = (DWORD) 0; // // Submit the data to the wave device. The wave header indicated that // we want to loop. Need to prepare the header first, but it can // only be prepared after the device has been opened. // mmresult = waveOutPrepareHeader(m_hWaveOutRing, & m_WaveHeaderRing, sizeof(WAVEHDR) ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } mmresult = waveOutWrite(m_hWaveOutRing, & m_WaveHeaderRing, sizeof(WAVEHDR) ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::StopRing( void ) { MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hWaveOutRing == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } mmresult = waveOutReset( m_hWaveOutRing ); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::OpenWaveDeviceForTone( long lWaveID ) { MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } // // We expect that the wave device will not be opened twice. This is // dependent on the calling code. // _ASSERTE( m_hWaveOutTone == NULL ); // // Open the wave device. Here we specify a hard-coded audio format. // WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; // linear PCM waveFormat.nChannels = 1; // mono waveFormat.nSamplesPerSec = 8000; // 8 KHz waveFormat.wBitsPerSample = 16; // 16-bit samples waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = 0; // no extra format info mmresult = waveOutOpen(& m_hWaveOutTone, // returned handle lWaveID, // which device to use &waveFormat, // wave format to use 0, // callback function pointer 0, // callback instance data WAVE_FORMAT_DIRECT // we don't want ACM ); if ( mmresult != MMSYSERR_NOERROR ) { m_hWaveOutTone = NULL; return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::OpenWaveDeviceForRing( long lWaveID ) { MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } // // We expect that the wave device will not be opened twice. This is // dependent on the calling code. // _ASSERTE( m_hWaveOutRing == NULL ); // // Open the wave device. Here we specify a hard-coded audio format. // WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; // linear PCM waveFormat.nChannels = 1; // mono waveFormat.nSamplesPerSec = 8000; // 8 KHz waveFormat.wBitsPerSample = 16; // 16-bit samples waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = 0; // no extra format info mmresult = waveOutOpen(& m_hWaveOutRing, // returned handle lWaveID, // which device to use &waveFormat, // wave format to use 0, // callback function pointer 0, // callback instance data WAVE_FORMAT_DIRECT // we don't want ACM ); if ( mmresult != MMSYSERR_NOERROR ) { m_hWaveOutRing = NULL; return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // void CWavePlayer::CloseWaveDeviceForTone(void) { if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); } if ( m_hWaveOutTone != NULL ) { waveOutReset( m_hWaveOutTone ); memset(m_fPlaying, 0, NUM_TONES * sizeof(BOOL)); m_lCurrentTone = -1; waveOutClose( m_hWaveOutTone ); m_hWaveOutTone = NULL; } } ////////////////////////////////////////////////////////////////////////////// // void CWavePlayer::CloseWaveDeviceForRing(void) { if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); } if ( m_hWaveOutRing != NULL ) { waveOutReset( m_hWaveOutRing ); waveOutClose( m_hWaveOutRing ); m_hWaveOutRing = NULL; } } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::OpenMixerDevice( long lWaveID ) { MMRESULT mmresult; MIXERLINECONTROLS mxlc; mmresult = mixerOpen( &m_hMixer, lWaveID, 0, 0, MIXER_OBJECTF_WAVEOUT); if ( mmresult != MMSYSERR_NOERROR ) { m_hMixer = NULL; return E_FAIL; } mxlc.cbStruct = sizeof(MIXERLINECONTROLS); mxlc.dwLineID = 0; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mxlc.pamxctrl = &m_mxctrl; mxlc.cbmxctrl = sizeof(m_mxctrl); mmresult = mixerGetLineControls( (HMIXEROBJ)m_hMixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ); if ( mmresult != MMSYSERR_NOERROR ) { // // Close the mixer // mixerClose( m_hMixer ); m_hMixer = NULL; return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // void CWavePlayer::CloseMixerDevice(void) { if ( m_hMixer != NULL ) { mixerClose( m_hMixer ); m_hMixer = NULL; } } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::SetVolume( DWORD dwVolume ) { MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_UNSIGNED mxcd_u; MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hMixer == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } mxcd_u.dwValue = dwVolume; mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = m_mxctrl.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(mxcd_u); mxcd.paDetails = &mxcd_u; mmresult = mixerSetControlDetails( (HMIXEROBJ)m_hMixer, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // HRESULT CWavePlayer::GetVolume( DWORD * pdwVolume ) { MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_UNSIGNED mxcd_u; MMRESULT mmresult; if ( m_fInitialized == FALSE ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } if ( m_hMixer == NULL ) { _ASSERTE( FALSE ); return E_UNEXPECTED; } mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = m_mxctrl.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(mxcd_u); mxcd.paDetails = &mxcd_u; mmresult = mixerGetControlDetails( (HMIXEROBJ)m_hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE); if ( mmresult != MMSYSERR_NOERROR ) { return E_FAIL; } *pdwVolume = mxcd_u.dwValue; return S_OK; }