📄 audiomanager.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "AudioManager.hpp"
#include "Common.hpp"
#include "Debug.hpp"
// -----------------------------------------------------------------------------
// FileHeader
// -----------------------------------------------------------------------------
typedef struct
{
DWORD dwRiff; // Type of file header.
DWORD dwSize; // Size of file header.
DWORD dwWave; // Type of wave.
} RIFF_FILEHEADER, *PRIFF_FILEHEADER;
// -----------------------------------------------------------------------------
// ChunkHeader
// -----------------------------------------------------------------------------
typedef struct
{
DWORD dwCKID; // Type Identification for current chunk header.
DWORD dwSize; // Size of current chunk header.
} RIFF_CHUNKHEADER, *PRIFF_CHUNKHEADER;
/* Chunk Types
*/
#define RIFF_FILE mmioFOURCC('R','I','F','F')
#define RIFF_WAVE mmioFOURCC('W','A','V','E')
#define RIFF_FORMAT mmioFOURCC('f','m','t',' ')
#define RIFF_CHANNEL mmioFOURCC('d','a','t','a')
bool
ReadChunk(
HANDLE FileHandle,
DWORD ChunkType,
__deref_out_opt VOID** ppBuffer,
__out_opt DWORD* pSize,
__out_opt DWORD* pBytesLeft
)
{
DWORD BytesRead;
PVOID pBuffer;
RIFF_CHUNKHEADER Chunk;
if ((!pBytesLeft) || (*pBytesLeft <= 0) || (!pSize) || (!ppBuffer))
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Invalid parameter to ReadChunk()\r\n"));
return false;
}
// now scan for the format chunk
while (*pBytesLeft > 0)
{
// now read the wave header (or what we hope is the wave header)
if (! ReadFile(
FileHandle,
&Chunk,
sizeof(Chunk),
&BytesRead,
NULL) ||
BytesRead < sizeof(Chunk)
)
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Error reading chunk header\n"));
return false;
}
*pBytesLeft -= BytesRead;
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Chunk: \"%c%c%c%c\" size=0x%08x\r\n",
(Chunk.dwCKID >> 0) & 0xff,
(Chunk.dwCKID >> 8) & 0xff,
(Chunk.dwCKID >> 16) & 0xff,
(Chunk.dwCKID >> 24) & 0xff,
Chunk.dwSize));
if (Chunk.dwCKID == ChunkType) {
// found the desired chunk
break;
}
// skip the data we don't know or care about...
if (0xFFFFFFFF == SetFilePointer (
FileHandle,
Chunk.dwSize,
NULL,
FILE_CURRENT
))
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Error setting file pointer while scanning for chunk\n"));
return false;
}
*pBytesLeft -= Chunk.dwSize;
}
// found the desired chunk.
// allocate a buffer and read in the data
pBuffer = new BYTE[Chunk.dwSize];
if (pBuffer == NULL) {
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Unable to allocate chunk buffer\r\n"));
return false;
}
if (! ReadFile(
FileHandle,
pBuffer,
Chunk.dwSize,
&BytesRead,
NULL
) ||
BytesRead < Chunk.dwSize
)
{
delete [] pBuffer;
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Unable to read chunk data\r\n"));
return false;
}
*pBytesLeft -= BytesRead;
*ppBuffer = pBuffer;
*pSize = Chunk.dwSize;
return true;
}
MMRESULT
ReadWaveFile(
__in LPCTSTR pFileName,
__deref_out WAVEFORMATEX** ppWaveFormat,
__out DWORD* pBufferSize,
__deref_out BYTE** ppBufferBytes
)
{
RIFF_FILEHEADER FileHeader;
DWORD BytesRead;
DWORD BufferSize;
DWORD FormatSize;
PBYTE pBufferBytes = NULL;
PWAVEFORMATEX pWaveFormat = NULL;
DWORD BytesInChunk;
HANDLE FileHandle;
MMRESULT MMResult = MMSYSERR_ERROR;
FileHandle = CreateFile(
pFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if( FileHandle == INVALID_HANDLE_VALUE )
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Error opening %s. Error code = 0x%08x\n", pFileName, GetLastError()));
return MMResult;
}
// Read file and determine sound format
// Start with RIFF header:
if (! ReadFile(
FileHandle,
&FileHeader,
sizeof(FileHeader),
&BytesRead, NULL
) ||
BytesRead < sizeof(FileHeader)
)
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Error reading file header\n"));
goto error_exit;
}
if ( FileHeader.dwRiff != RIFF_FILE || FileHeader.dwWave != RIFF_WAVE)
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Invalid wave file header\n"));
goto error_exit;
}
BytesInChunk = FileHeader.dwSize;
// load the wave format
if (! ReadChunk(
FileHandle,
RIFF_FORMAT,
(PVOID*)&pWaveFormat,
&FormatSize,
&BytesInChunk
))
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Unable to read format chunk\r\n"));
goto error_exit;
}
if (FormatSize < sizeof(PCMWAVEFORMAT))
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Format record too small\r\n"));
goto error_exit;
}
// load the wave data
if (!ReadChunk(
FileHandle,
RIFF_CHANNEL,
(PVOID*) &pBufferBytes,
&BufferSize,
&BytesInChunk
))
{
PHONEAPP_DEBUGMSG(ZONE_PHONEAPP_ERROR, (L"Unable to read format chunk\r\n"));
goto error_exit;
}
*ppWaveFormat = pWaveFormat;
*pBufferSize = BufferSize;
*ppBufferBytes = pBufferBytes;
// Success
MMResult = MMSYSERR_NOERROR;
goto exit;
error_exit:
delete [] pBufferBytes;
delete [] pWaveFormat;
exit:
CloseHandle(FileHandle);
return MMResult;
}
//Paths for progress tones we handle
const WCHAR AudioManager_t::sc_CallWaitingTone[] = L"\\Windows\\waiting.wav";
const WCHAR AudioManager_t::sc_DialTone[] = L"\\Windows\\dialtone.wav";
const WCHAR AudioManager_t::sc_ClientBusyTone[] = L"\\Windows\\busy.wav";
const WCHAR AudioManager_t::sc_ReorderTone[] = L"\\Windows\\reorder.wav";
const WCHAR AudioManager_t::sc_RingbackTone[] = L"\\Windows\\ringback.wav";
AudioManager_t::AudioManager_t(
void
)
{
ZeroMemory(&m_RingWaveDataBlockHeader, sizeof(m_RingWaveDataBlockHeader));
ZeroMemory(&m_Lock, sizeof(m_Lock));
m_RingWaveOutHandle = NULL;
m_ProgressToneThreadId = NULL;
m_StopToneEvent = NULL;
m_pProgressTonePath = NULL;
m_RingThreadId = NULL;
m_StopRingEvent = NULL;
}
AudioManager_t::~AudioManager_t(
void
)
{
CloseHandle(m_RingThreadId);
m_RingThreadId = NULL;
CloseHandle(m_StopRingEvent);
m_StopRingEvent = NULL;
CloseHandle(m_RingWaveOutHandle);
m_RingWaveOutHandle = NULL;
CloseHandle(m_ProgressToneThreadId);
m_ProgressToneThreadId = NULL;
CloseHandle(m_StopToneEvent);
m_StopToneEvent = NULL;
}
HRESULT
AudioManager_t::Initialize(
void
)
{
m_StopToneEvent = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
InitializeCriticalSection(&m_Lock);
m_StopRingEvent = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
if (m_StopToneEvent == NULL || m_StopRingEvent == NULL)
{
return E_FAIL;
}
return S_OK;
}
void
AudioManager_t::Uninitialize(
void
)
{
SetEvent(m_StopToneEvent);
if (m_ProgressToneThreadId != NULL)
{
WaitForSingleObject(m_ProgressToneThreadId, INFINITE);
}
DeleteCriticalSection(&m_Lock);
SetEvent(m_StopRingEvent);
if (m_RingThreadId != NULL)
{
WaitForSingleObject(m_RingThreadId, INFINITE);
}
}
HRESULT
AudioManager_t::PlayProgressTone(
ProgressToneType_e ProgressToneType
)
{
HRESULT hr = S_OK;
EnterCriticalSection(&m_Lock);
//Ensure we are the only progress tone playing
StopProgressTone();
if (SUCCEEDED(hr))
{
//Set which progress tone to play for this progress tone thread
switch (ProgressToneType)
{
case ProgressToneInvalid:
hr = E_INVALIDARG;
break;
case ProgressToneCallWaiting:
m_pProgressTonePath = sc_CallWaitingTone;
break;
case ProgressToneClientBusy:
m_pProgressTonePath = sc_ClientBusyTone;
break;
case ProgressToneDial:
m_pProgressTonePath = sc_DialTone;
break;
case ProgressToneReorder:
m_pProgressTonePath = sc_ReorderTone;
break;
case ProgressToneRingback:
m_pProgressTonePath = sc_RingbackTone;
break;
default:
hr = E_NOTIMPL;
break;
}
}
if (SUCCEEDED(hr))
{
//Create a thread that will play the progress tone continuously
m_ProgressToneThreadId = CreateThread(
NULL,
0,
s_ProgressToneThreadProc,
static_cast<VOID*>(this), //pass in 'this' as the parameters
0,
NULL
);
//Make sure the thread was actually started
if (m_ProgressToneThreadId == NULL)
{
hr = CommonUtilities_t::GetErrorFromWin32();
}
}
LeaveCriticalSection(&m_Lock);
return hr;
}
/*------------------------------------------------------------------------------
AudioManager_t::s_ProgressToneThreadProc
Static thread proc to play progress tones continually using waveout
Parameters:
Parameter: This is the instance of the mediamanager packed into a void
------------------------------------------------------------------------------*/
/*static */DWORD WINAPI
AudioManager_t::s_ProgressToneThreadProc(
LPVOID Parameter
)
{
if (Parameter == NULL)
{
ASSERT(FALSE);
return E_POINTER;
}
//Unpack the media manager instance
AudioManager_t *pAudioManager = static_cast<AudioManager_t*>(Parameter);
//Use the instance to play the progress tone
return pAudioManager->ProgressToneThreadProc();
}
/*------------------------------------------------------------------------------
AudioManager_t::ProgressToneThreadProc
Thread Proc that plays a progress tone continuously through waveOutWrite.
The tone keeps playing until StopProgressTone is called
------------------------------------------------------------------------------*/
HRESULT AudioManager_t::ProgressToneThreadProc()
{
MMRESULT MMResult = MMSYSERR_NOERROR;
DWORD BufferSize = 0;
BYTE* ppBufferBytes = NULL;
WAVEFORMATEX* pWaveFormat = NULL;
HWAVEOUT WaveOutHandle = NULL;
WAVEHDR WaveBufferHeader = {0};
HANDLE SoundDoneEvent = NULL;
//Callback event
SoundDoneEvent = CreateEvent(
NULL,
TRUE,
FALSE,
NULL
);
//Read the wave file using the helper functions
MMResult = ReadWaveFile(
m_pProgressTonePath,
&pWaveFormat,
&BufferSize,
&ppBufferBytes
);
if (MMResult != MMSYSERR_NOERROR)
{
goto cleanup;
}
//Open the waveout device
MMResult = waveOutOpen(
&WaveOutHandle, //handle to the out wave
0, //unused
pWaveFormat, //the waveformatex struct that contains the wav info
reinterpret_cast<DWORD>(SoundDoneEvent),
0,
CALLBACK_EVENT //SoundDoneEvent will be called back when the sound is done being played
);
if (MMResult != MMSYSERR_NOERROR)
{
goto cleanup;
}
//Prepare the header
WaveBufferHeader.dwBufferLength = BufferSize;
//Yes it should be a 'char' even though we are using unicode!
WaveBufferHeader.lpData = (char *)ppBufferBytes;
//Inform the wave handle about the header
MMResult = waveOutPrepareHeader(
WaveOutHandle,
&WaveBufferHeader,
sizeof(WaveBufferHeader)
);
if (MMResult != MMSYSERR_NOERROR)
{
goto cleanup;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -