最近,接触了一个项目,这个项目捕获的音频,噪音很大,所以研究了core audio ,重写了音频捕获类。
Common.h
/*
* author: zyb
* \brief Common Macro
*/
#pragma once
#ifndef IS_NULLPTR
#define IS_NULLPTR(ptr) (nullptr == (ptr))
#endif
#ifndef NOT_NULLPTR
#define NOT_NULLPTR(ptr) (nullptr != (ptr))
#endif
#ifndef IS_TRUE
#define IS_TRUE(isTrue) (true == (isTrue))
#endif
#ifndef IS_FALSE
#define IS_FALSE(isFalse) (false == (isFalse))
#endif
#ifndef SafDelete_A
#define SafDelete_A(ptr) if(NOT_NULLPTR(ptr)){ delete[] (ptr); (ptr) = nullptr; }
#endif
#ifndef SafRelease
#define SafRelease(ptr) if(NOT_NULLPTR(ptr)){ (ptr)->Release(); (ptr) = nullptr; }
#endif
#ifndef SafCloseHandle
#define SafCloseHandle(handle) if(NOT_NULLPTR(handle)){ CloseHandle(handle); (handle) = nullptr; }
#endif
#include <wtypes.h>
CaptureEvent.h
/*
* author: zyb
* \brief CaptureEvent Interface
*/
#include "Common.h"
#include <mmreg.h>
class ICaptureEvent
{
public:
virtual ~ICaptureEvent(){};
virtual void OnCaptureStart(DWORD dwInterval) = 0;
virtual void OnCaptureStop() = 0;
virtual void OnAdjustCaptureFormat(WAVEFORMATEX *pFormat) = 0;
virtual void OnCaptureData(LPBYTE pData, int iDataLen) = 0;
};
AudioCapture.hpp
/*
* author: zyb
* \brief:
* CAudioCaptureT<eRender> : Capture Audio Output Stream
* CAudioCaptureT<eCapture> : Capture Audio Input Stream
*/
#pragma once
#include "CaptureEvent.h"
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <process.h>
#include <avrt.h>
#pragma comment(lib,"Avrt.lib")
namespace
{
typedef struct
{
HANDLE hEventStarted;
HANDLE hEventStop;
ICaptureEvent *pEventHandle;
IMMDevice *pDevice;
}capture_thread_data;
template<int _iDataFlow>
struct TaskNameT{};
template<>
struct TaskNameT<eRender>
{
static wchar_t * Get()
{
return L"Capture";
}
};
template<>
struct TaskNameT<eCapture>
{
static wchar_t * Get()
{
return L"Audio";
}
};
template<int _iDataFlow>
struct StreamFlagT{};
template<>
struct StreamFlagT<eRender>
{
static int Get()
{
return AUDCLNT_STREAMFLAGS_LOOPBACK;
}
};
template<>
struct StreamFlagT<eCapture>
{
static int Get()
{
return 0;
}
};
}
template<int _iDataFlow>
class CAudioCaptureT
{
public:
CAudioCaptureT();
~CAudioCaptureT();
bool Init(ICaptureEvent *pHandler);
void UnInit();
bool Start();
void Stop();
bool IsCapturing() const;
bool IsInit() const;
private:
static IMMDevice * _GetDefaultDevice();
static UINT _CaptureAudio(IMMDevice *pDevice, HANDLE hEventStarted, HANDLE hEventStop, ICaptureEvent *pEventHandle);
static UINT __stdcall _CaptureThreadProc(LPVOID param);
private:
bool m_bInited;
HANDLE m_hEventStarted;
HANDLE m_hEventStop;
HANDLE m_hThreadCapture;
IMMDevice *m_pDevice;
ICaptureEvent *m_pEventHandler;
};
template<int _iDataFlow>
CAudioCaptureT<_iDataFlow>::CAudioCaptureT() :
m_bInited(false),
m_hEventStarted(nullptr),
m_hEventStop(nullptr),
m_hThreadCapture(nullptr),
m_pDevice(nullptr),
m_pEventHandler(nullptr)
{
}
template<int _iDataFlow>
CAudioCaptureT<_iDataFlow>::~CAudioCaptureT()
{
UnInit();
}
template<int _iDataFlow>
bool CAudioCaptureT<_iDataFlow>::Init(ICaptureEvent *pHandler)
{
if (m_bInited)
return true;
#define checkRet(ret) if(IS_NULLPTR(ret)) break;
if (IS_NULLPTR(pHandler))
return false;
m_pEventHandler = pHandler;
do
{
m_pDevice = _GetDefaultDevice();
checkRet(m_pDevice);
m_hEventStarted = CreateEvent(nullptr, true, false, nullptr);
checkRet(m_hEventStarted);
m_hEventStop = CreateEvent(nullptr, true, false, nullptr);
checkRet(m_hEventStop);
m_bInited = true;
} while (0);
if (IS_FALSE(m_bInited))
UnInit();
return m_bInited;
}
template<int _iDataFlow>
void CAudioCaptureT<_iDataFlow>::UnInit()
{
m_pEventHandler = nullptr;
SafRelease(m_pDevice);
SafCloseHandle(m_hEventStarted);
SafCloseHandle(m_hEventStop);
m_bInited = false;
}
template<int _iDataFlow>
bool CAudioCaptureT<_iDataFlow>::IsCapturing() const
{
return NOT_NULLPTR(m_hThreadCapture);
}
template<int _iDataFlow>
bool CAudioCaptureT<_iDataFlow>::IsInit() const
{
return m_bInited;
}
template<int _iDataFlow>
bool CAudioCaptureT<_iDataFlow>::Start()
{
if (IS_FALSE(m_bInited))
return false;
if (NOT_NULLPTR(m_hThreadCapture))
return true;
capture_thread_data data;
data.hEventStarted = m_hEventStarted;
data.hEventStop = m_hEventStop;
data.pEventHandle = m_pEventHandler;
data.pDevice = m_pDevice;
m_hThreadCapture = (HANDLE)_beginthreadex(nullptr, 0, &_CaptureThreadProc, &data, 0, nullptr);
if (IS_NULLPTR(m_hThreadCapture))
return false;
HANDLE ahWaits[2] = { m_hEventStarted, m_hThreadCapture };
DWORD dwWaitResult = WaitForMultipleObjects(sizeof(ahWaits) / sizeof(ahWaits[0]), ahWaits, false, INFINITE);
if (WAIT_OBJECT_0 != dwWaitResult)
{
Stop();
return false;
}
return true;
}
template<int _iDataFlow>
void CAudioCaptureT<_iDataFlow>::Stop()
{
if (IS_FALSE(m_bInited))
return;
if (NOT_NULLPTR(m_hEventStop) &&
NOT_NULLPTR(m_hThreadCapture))
{
SetEvent(m_hEventStop);
SafCloseHandle(m_hThreadCapture);
}
}
template<int _iDataFlow>
IMMDevice * CAudioCaptureT<_iDataFlow>::_GetDefaultDevice()
{
IMMDevice *pDevice = nullptr;
IMMDeviceEnumerator *pMMDeviceEnumerator = nullptr;
HRESULT hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&pMMDeviceEnumerator);
if (FAILED(hr))
return nullptr;
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint((EDataFlow)_iDataFlow, eConsole, &pDevice);
SafRelease(pMMDeviceEnumerator);
return pDevice;
}
template<int _iDataFlow>
UINT CAudioCaptureT<_iDataFlow>::_CaptureAudio(IMMDevice *pDevice, HANDLE hEventStarted, HANDLE hEventStop, ICaptureEvent *pEventHandle)
{
HRESULT hr;
IAudioClient *pAudioClient = nullptr;
WAVEFORMATEX *pWfx = nullptr;
REFERENCE_TIME hnsDefaultDevicePeriod(0);
HANDLE hTimerWakeUp = nullptr;
IAudioCaptureClient *pAudioCaptureClient = nullptr;
DWORD nTaskIndex = 0;
HANDLE hTask = nullptr;
bool bStarted(false);
do
{
hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);
if (FAILED(hr))
break;
hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, nullptr);
if (FAILED(hr))
break;
hr = pAudioClient->GetMixFormat(&pWfx);
if (FAILED(hr))
break;
hTimerWakeUp = CreateWaitableTimer(nullptr, false, nullptr);
if (IS_NULLPTR(hTimerWakeUp))
break;
SetEvent(hEventStarted);
pEventHandle->OnAdjustCaptureFormat(pWfx);
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, StreamFlagT<_iDataFlow>::Get(), 0, 0, pWfx, 0);
if (FAILED(hr))
break;
hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient);
if (FAILED(hr))
break;
hTask = AvSetMmThreadCharacteristics(TaskNameT<_iDataFlow>::Get(), &nTaskIndex);
if (IS_NULLPTR(hTask))
break;
LARGE_INTEGER liFirstFire;
liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2;
LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000);
#pragma warning(disable:4800)
bool bOk = (bool)SetWaitableTimer(hTimerWakeUp, &liFirstFire, lTimeBetweenFires, nullptr, nullptr, false);
if (IS_FALSE(bOk))
break;
hr = pAudioClient->Start();
if (FAILED(hr))
break;
pEventHandle->OnCaptureStart(lTimeBetweenFires);
bStarted = true;
HANDLE ahWait[2] = { hEventStop, hTimerWakeUp };
DWORD dwWaitResult;
UINT32 uiNextPacketSize(0);
BYTE *pData = nullptr;
UINT32 uiNumFramesToRead;
DWORD dwFlags;
while (true)
{
dwWaitResult = WaitForMultipleObjects(sizeof(ahWait) / sizeof(ahWait[0]), ahWait, false, INFINITE);
if (WAIT_OBJECT_0 + 1 != dwWaitResult)
break;
hr = pAudioCaptureClient->GetNextPacketSize(&uiNextPacketSize);
if (FAILED(hr))
break;
if (0 == uiNextPacketSize)
continue;
hr = pAudioCaptureClient->GetBuffer(
&pData,
&uiNumFramesToRead,
&dwFlags,
nullptr,
nullptr);
if (FAILED(hr))
break;
if (0 != uiNumFramesToRead)
{
pEventHandle->OnCaptureData(pData, uiNumFramesToRead * pWfx->nBlockAlign);
}
pAudioCaptureClient->ReleaseBuffer(uiNumFramesToRead);
}
} while (0);
if (NOT_NULLPTR(hTask))
{
AvRevertMmThreadCharacteristics(hTask);
hTask = nullptr;
}
SafRelease(pAudioCaptureClient);
if (NOT_NULLPTR(pWfx))
{
CoTaskMemFree(pWfx);
pWfx = nullptr;
}
if (NOT_NULLPTR(hTimerWakeUp))
{
CancelWaitableTimer(hTimerWakeUp);
CloseHandle(hTimerWakeUp);
hTimerWakeUp = nullptr;
}
if (NOT_NULLPTR(pAudioClient))
{
if (IS_TRUE(bStarted))
{
pAudioClient->Stop();
pEventHandle->OnCaptureStop();
}
pAudioClient->Release();
pAudioClient = nullptr;
}
return 0;
}
template<int _iDataFlow>
UINT __stdcall CAudioCaptureT<_iDataFlow>::_CaptureThreadProc(LPVOID param)
{
capture_thread_data *pData = (capture_thread_data*)param;
CoInitialize(nullptr);
UINT uiRet = _CaptureAudio(
pData->pDevice,
pData->hEventStarted,
pData->hEventStop,
pData->pEventHandle);
CoUninitialize();
return uiRet;
}
AudioRecord.hpp
/*
* author: zyb
* \brief
* AudioRecordT<eRender>
* AudioRecordT<eCapture>
*/
#pragma once
#include "AudioCapture.hpp"
#include <list>
namespace
{
typedef struct
{
INT iDataLen;
LPBYTE pData;
}Audio_Data, *PAudio_Data;
template<int _iDataFlow>
struct FileNameT{};
template<>
struct FileNameT<eRender>
{
static wchar_t* Get()
{
return L"./output.wav";
}
};
template<>
struct FileNameT<eCapture>
{
static wchar_t* Get()
{
return L"./input.wav";
}
};
}
template<int _iDataFlow>
class CAudioRecordT : public ICaptureEvent
{
public:
typedef CAudioCaptureT<_iDataFlow> CaptureType;
typedef std::list<Audio_Data> ListType;
typedef typename ListType::iterator IteratorType;
typedef std::wstring StrType;
public:
CAudioRecordT();
~CAudioRecordT();
bool StartCapture();
void StopCapture();
bool IsCapturing() const;
void SetFilePath(wchar_t* pFilePath);
bool SaveFile();
public:
virtual void OnCaptureStart(DWORD dwInterval) override;
virtual void OnCaptureStop() override;
virtual void OnAdjustCaptureFormat(WAVEFORMATEX *pFormat) override;
virtual void OnCaptureData(LPBYTE pData, INT iDataLen) override;
private:
void _ClearData();
private:
CaptureType m_Capture;
WAVEFORMATEX* m_pFormat;
ListType m_DataList;
INT m_iDataLen;
StrType m_strFilePath;
};
template<int _iDataFlow>
CAudioRecordT<_iDataFlow>::CAudioRecordT() :
m_pFormat(nullptr),
m_iDataLen(0)
{
m_strFilePath = FileNameT<_iDataFlow>::Get();
}
template<int _iDataFlow>
CAudioRecordT<_iDataFlow>::~CAudioRecordT()
{
_ClearData();
}
template<int _iDataFlow>
bool CAudioRecordT<_iDataFlow>::StartCapture()
{
bool bRet = m_Capture.IsCapturing();
if (IS_FALSE(bRet))
{
bRet = m_Capture.Init(this);
if (IS_TRUE(bRet))
bRet = m_Capture.Start();
}
return bRet;
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::StopCapture()
{
bool bResult = m_Capture.IsCapturing();
if (IS_TRUE(bResult))
{
m_Capture.Stop();
m_Capture.UnInit();
}
}
template<int _iDataFlow>
bool CAudioRecordT<_iDataFlow>::IsCapturing() const
{
return m_Capture.IsCapturing();
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::_ClearData()
{
LPBYTE p = (LPBYTE)m_pFormat;
SafDelete_A(p);
IteratorType itr = m_DataList.begin();
while (itr != m_DataList.end())
{
delete[] itr->pData;
itr++;
}
m_DataList.clear();
m_iDataLen = 0;
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::OnCaptureStart(DWORD dwInterval)
{
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::OnCaptureStop()
{
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::OnAdjustCaptureFormat(WAVEFORMATEX *pFormat)
{
_ClearData();
int iDataLen = sizeof(WAVEFORMATEX) + pFormat->cbSize;
LPBYTE pData = new (std::nothrow) BYTE[iDataLen];
if (NOT_NULLPTR(pData))
{
memcpy_s(pData, iDataLen, pFormat, iDataLen);
m_pFormat = (WAVEFORMATEX*)pData;
}
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::OnCaptureData(LPBYTE pData, INT iDataLen)
{
Audio_Data item;
item.iDataLen = iDataLen;
item.pData = new (std::nothrow) BYTE[iDataLen];
if (NOT_NULLPTR(item.pData))
{
memcpy_s(item.pData, iDataLen, pData, iDataLen);
m_DataList.push_back(item);
m_iDataLen += iDataLen;
}
}
template<int _iDataFlow>
void CAudioRecordT<_iDataFlow>::SetFilePath(wchar_t* pFilePath)
{
if (IS_NULLPTR(pFilePath))
return;
m_strFilePath = pFilePath;
}
///from msdn///
namespace{
//
// WAV file writer.
//
// This is a VERY simple .WAV file writer.
//
//
// A wave file consists of:
//
// RIFF header: 8 bytes consisting of the signature "RIFF" followed by a 4 byte file length.
// WAVE header: 4 bytes consisting of the signature "WAVE".
// fmt header: 4 bytes consisting of the signature "fmt " followed by a WAVEFORMATEX
// WAVEFORMAT: <n> bytes containing a waveformat structure.
// DATA header: 8 bytes consisting of the signature "data" followed by a 4 byte file length.
// wave data: <m> bytes containing wave data.
//
//
// Header for a WAV file - we define a structure describing the first few fields in the header for convenience.
//
struct WAVEHEADER
{
DWORD dwRiff; // "RIFF"
DWORD dwSize; // Size
DWORD dwWave; // "WAVE"
DWORD dwFmt; // "fmt "
DWORD dwFmtSize; // Wave Format Size
};
// Static RIFF header, we'll append the format to it.
const BYTE WaveHeader[] =
{
'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', 0x00, 0x00, 0x00, 0x00
};
// Static wave DATA tag.
const BYTE WaveData[] = { 'd', 'a', 't', 'a' };
}
///
template<int _iDataFlow>
bool CAudioRecordT<_iDataFlow>::SaveFile()
{
DWORD dwWaveFileSize = sizeof(WAVEHEADER) + sizeof(WAVEFORMATEX) + m_pFormat->cbSize + sizeof(WaveData) + sizeof(DWORD) + m_iDataLen;
DWORD dwWaveFileNoDataSize = dwWaveFileSize - m_iDataLen;
BYTE *pWaveFileNoData = new (std::nothrow) BYTE[dwWaveFileSize];
BYTE *pWaveFileNoDataCopy = pWaveFileNoData;
if (IS_NULLPTR(pWaveFileNoDataCopy))
{
return false;
}
WAVEHEADER *pWaveHeader = reinterpret_cast<WAVEHEADER *>(pWaveFileNoDataCopy);
CopyMemory(pWaveFileNoDataCopy, WaveHeader, sizeof(WaveHeader));
pWaveFileNoDataCopy += sizeof(WaveHeader);
pWaveHeader->dwSize = dwWaveFileSize - (2 * sizeof(DWORD));
pWaveHeader->dwFmtSize = sizeof(WAVEFORMATEX) + m_pFormat->cbSize;
CopyMemory(pWaveFileNoDataCopy, m_pFormat, sizeof(WAVEFORMATEX) + m_pFormat->cbSize);
pWaveFileNoDataCopy += sizeof(WAVEFORMATEX) + m_pFormat->cbSize;
CopyMemory(pWaveFileNoDataCopy, WaveData, sizeof(WaveData));
pWaveFileNoDataCopy += sizeof(WaveData);
*(reinterpret_cast<DWORD*>(pWaveFileNoDataCopy)) = static_cast<DWORD>(m_iDataLen);
HANDLE FileHandle = CreateFile(m_strFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (FileHandle == INVALID_HANDLE_VALUE)
{
SafDelete_A(pWaveFileNoData)
return false;
}
DWORD dwWritten = 0;
WriteFile(FileHandle, pWaveFileNoData, dwWaveFileNoDataSize, &dwWritten, nullptr);
for (IteratorType it = m_DataList.begin(); it != m_DataList.end(); it++)
{
WriteFile(FileHandle, it->pData, it->iDataLen, &dwWritten, nullptr);
}
CloseHandle(FileHandle);
SafDelete_A(pWaveFileNoData);
return true;
}
typedef CAudioRecordT<eCapture> CInputRecord;
typedef CAudioRecordT<eRender> COutputRecord;
使用的测试工程,下载地址:
http://download.csdn.net/download/huanongying131/10136777