最近,接触了一个项目,这个项目捕获的音频,噪音很大,所以研究了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