本篇博客使用core audio api录制声卡和麦克风,但是不能同时来,因为这里没有做混音,所以同时只能录制一种,录制的音频会保存成test.pcm,格式是PCM,参数是(48000 32 2),不多说了,直接上代码,希望对各位小伙伴有用。
AudioCapT.h
#pragma once
#include <windows.h>
#include <windowsx.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <process.h>
#include <avrt.h>
#include <list>
typedef struct
{
INT iDataLen;
LPBYTE pData;
}Audio_Data, * PAudio_Data;
typedef std::list<PAudio_Data> AudioList;
class CAudioCapT
{
public:
enum {SPEAKER = 1,MICPHONE};
public:
CAudioCapT();
~CAudioCapT();
//开始捕获
bool Start();
//停止捕获
bool Stop();
void SaveFormat(WAVEFORMATEX * wf);
void SetDeiveType(int nType);
int GetDeviceType();
Audio_Data * GetAudio();
bool Init();
IMMDevice * GetDevice();
HANDLE GetStartEventHandle();
HANDLE GetStopEventHandle();
WAVEFORMATEX * GetWaveFormat();
AudioList * GetAudioList();
void ClearAudioList();
DWORD GetDataSize();
bool m_bStop;
protected:
bool m_bInit;
HANDLE m_hThreadCapture;
static UINT __stdcall _CaptureThreadProc(LPVOID param);
void OnCaptureData(LPBYTE pData, INT iDataLen);
WAVEFORMATEX m_WaveFormat;
int m_nDeviceType = 0;
AudioList m_al;
CRITICAL_SECTION m_cs;
IMMDevice * m_pDevice = NULL;
HANDLE m_hEventStarted = NULL;
HANDLE m_hEventStop = NULL;
IMMDevice * GetDefaultDevice(int nType);
DWORD m_dwDataSize;
};
AudioCapT.cpp
#include "stdafx.h"
#include "AudioCapT.h"
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#pragma comment(lib, "Avrt.lib")
CAudioCapT::CAudioCapT()
{
m_dwDataSize = 0;
m_bInit = false;
m_hThreadCapture = NULL;
m_bStop = false;
::InitializeCriticalSection(&m_cs);
}
CAudioCapT::~CAudioCapT()
{
Audio_Data * pTmp = NULL;
for (std::list<Audio_Data *>::iterator it = m_al.begin(); it != m_al.end(); it++)
{
pTmp = *it;
if (pTmp)
{
if (pTmp->pData)
delete[] pTmp->pData;
delete pTmp;
}
}
m_al.clear();
::DeleteCriticalSection(&m_cs);
}
void CAudioCapT::SetDeiveType(int nType)
{
m_nDeviceType = nType;
}
int CAudioCapT::GetDeviceType()
{
return m_nDeviceType;
}
void CAudioCapT::OnCaptureData(LPBYTE pData, INT iDataLen)
{
Audio_Data * pItem = NULL;
if (!pData)
return;
pItem = new Audio_Data();
if (!pItem)
return;
pItem->iDataLen = iDataLen;
pItem->pData = new (std::nothrow) BYTE[iDataLen];
if (pItem->pData)
{
memcpy_s(pItem->pData, iDataLen, pData, iDataLen);
::EnterCriticalSection(&m_cs);
m_al.push_back(pItem);
m_dwDataSize += iDataLen;
printf(".");
::LeaveCriticalSection(&m_cs);
}
return;
}
Audio_Data * CAudioCapT::GetAudio()
{
Audio_Data * pAudio = NULL;
::EnterCriticalSection(&m_cs);
if (m_al.empty() == false)
{
pAudio = m_al.front();
m_al.pop_front();
}
::LeaveCriticalSection(&m_cs);
return pAudio;
}
IMMDevice * CAudioCapT::GetDefaultDevice(int nType)
{
IMMDevice *pDevice = nullptr;
IMMDeviceEnumerator *pMMDeviceEnumerator = nullptr;
HRESULT hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&pMMDeviceEnumerator);
if (FAILED(hr))
return nullptr;
if (nType == CAudioCapT::MICPHONE)
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint((EDataFlow)eCapture, eConsole, &pDevice);
else if (nType == CAudioCapT::SPEAKER)
hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint((EDataFlow)eRender, eConsole, &pDevice);
else
pDevice = nullptr;
if (pMMDeviceEnumerator)
{
pMMDeviceEnumerator->Release();
pMMDeviceEnumerator = nullptr;
}
return pDevice;
}
bool CAudioCapT::Init()
{
if (m_bInit)
return true;
ClearAudioList();
m_pDevice = GetDefaultDevice(m_nDeviceType);
if (!m_pDevice)
return false;
m_hEventStarted = CreateEvent(nullptr, true, false, nullptr);
if (m_hEventStarted == NULL)
{
return false;
}
m_bStop = false;
m_dwDataSize = 0;
m_bInit = true;
return true;
}
DWORD CAudioCapT::GetDataSize()
{
::EnterCriticalSection(&m_cs);
DWORD dwSize = m_dwDataSize;
::LeaveCriticalSection(&m_cs);
return dwSize;
}
IMMDevice * CAudioCapT::GetDevice()
{
return m_pDevice;
}
HANDLE CAudioCapT::GetStartEventHandle()
{
return m_hEventStarted;
}
HANDLE CAudioCapT::GetStopEventHandle()
{
return m_hEventStop;
}
AudioList * CAudioCapT::GetAudioList()
{
return &m_al;
}
void CAudioCapT::ClearAudioList()
{
::EnterCriticalSection(&m_cs);
for (AudioList::iterator it = m_al.begin(); it != m_al.end(); it++)
{
byte * pData = (*it)->pData;
if (pData)
{
delete[] pData;
}
}
m_al.clear();
::LeaveCriticalSection(&m_cs);
}
UINT __stdcall CAudioCapT::_CaptureThreadProc(LPVOID param)
{
CAudioCapT * pObject = (CAudioCapT *)param;
if (!pObject)
return 0;
HRESULT hr = 0;
IAudioClient *pAudioClient = nullptr;
WAVEFORMATEX *pWfx = nullptr;
IAudioCaptureClient *pAudioCaptureClient = nullptr;
DWORD nTaskIndex = 0;
HANDLE hTask = nullptr;
bool bStarted(false);
int nDeviceType = 0;
IMMDevice * pDevice = pObject->GetDevice();
HANDLE hEventStarted = pObject->GetStartEventHandle();
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT bufferFrameCount = 0;
if (!pDevice || !hEventStarted)
return 0;
CoInitialize(nullptr);
do
{
hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);
if (FAILED(hr))
break;
hr = pAudioClient->GetMixFormat(&pWfx);
if (FAILED(hr))
break;
SetEvent(hEventStarted);
pObject->SaveFormat(pWfx);
nDeviceType = pObject->GetDeviceType();
if (nDeviceType == CAudioCapT::MICPHONE)
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, pWfx, 0);
else if (nDeviceType == CAudioCapT::SPEAKER)
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, pWfx, 0);
else
break;
if (FAILED(hr))
break;
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
if (FAILED(hr))
break;
hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient);
if (FAILED(hr))
break;
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pWfx->nSamplesPerSec;
if (nDeviceType == CAudioCapT::MICPHONE)
hTask = AvSetMmThreadCharacteristics(_T("Audio"), &nTaskIndex);
else
hTask = AvSetMmThreadCharacteristics(_T("Capture"), &nTaskIndex);
if (!hTask)
break;
hr = pAudioClient->Start();
if (FAILED(hr))
break;
bStarted = true;
DWORD dwWaitResult;
UINT32 uiNextPacketSize(0);
BYTE *pData = nullptr;
UINT32 uiNumFramesToRead;
DWORD dwFlags;
while (pObject->m_bStop == false)
{
Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
hr = pAudioCaptureClient->GetNextPacketSize(&uiNextPacketSize);
if (FAILED(hr))
break;
while (uiNextPacketSize != 0)
{
hr = pAudioCaptureClient->GetBuffer(
&pData,
&uiNumFramesToRead,
&dwFlags,
nullptr,
nullptr);
if (FAILED(hr))
break;
if (dwFlags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL;
}
pObject->OnCaptureData(pData, uiNumFramesToRead * pWfx->nBlockAlign);
pAudioCaptureClient->ReleaseBuffer(uiNumFramesToRead);
hr = pAudioCaptureClient->GetNextPacketSize(&uiNextPacketSize);
if (FAILED(hr))
break;
}
}
} while (0);
if (hTask)
{
AvRevertMmThreadCharacteristics(hTask);
hTask = nullptr;
}
if (pAudioCaptureClient)
{
pAudioCaptureClient->Release();
pAudioCaptureClient = nullptr;
}
if (pWfx)
{
CoTaskMemFree(pWfx);
pWfx = nullptr;
}
if (pAudioClient)
{
if (bStarted)
{
pAudioClient->Stop();
}
pAudioClient->Release();
pAudioClient = nullptr;
}
CoUninitialize();
return 0;
}
void CAudioCapT::SaveFormat(WAVEFORMATEX * wf)
{
if (!wf)
return;
/*
if (wf->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
wf->wFormatTag = WAVE_FORMAT_PCM;
}
else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
{
PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(wf);
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat))
{
pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
pEx->Samples.wValidBitsPerSample = 16;
}
}
else
return;
wf->wBitsPerSample = 16;
wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample / 8;
wf->nAvgBytesPerSec = wf->nBlockAlign * wf->nSamplesPerSec;
*/
memcpy(&m_WaveFormat, wf, sizeof(WAVEFORMATEX));
return;
}
WAVEFORMATEX * CAudioCapT::GetWaveFormat()
{
return &m_WaveFormat;
}
bool CAudioCapT::Start()
{
if (!m_bInit)
Init();
if (m_hThreadCapture)
return true;
m_hThreadCapture = (HANDLE)_beginthreadex(nullptr, 0, _CaptureThreadProc, this, 0, nullptr);
if (!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)
{
if (m_hThreadCapture)
{
CloseHandle(m_hThreadCapture);
m_hThreadCapture = NULL;
}
return false;
}
return true;
}
bool CAudioCapT::Stop()
{
if (m_bInit == false)
return false;
m_bStop = true;
WaitForSingleObject(m_hThreadCapture, INFINITE);
if (m_pDevice)
{
m_pDevice->Release();
m_pDevice = NULL;
}
if (m_hEventStarted)
{
CloseHandle(m_hEventStarted);
m_hEventStarted = NULL;
}
if (m_hThreadCapture)
{
CloseHandle(m_hThreadCapture);
m_hThreadCapture = NULL;
}
m_bInit = false;
return true;
}
运行效果如下:
敲击键盘,停止录音,然后保存到test.pcm,之后可以使用ffplay来播放,播放命令如下:
ffplay -ar 48000 -ac 2 -f f32le -i test.pcm
需要下载的小伙伴可以从下面下载:
https://download.csdn.net/download/u011711997/10539565