参考文档:
- 主要的代码参考了官方文档的 Capturing a Stream 和 Loopback Recording 两篇
- WAVHead 代码参考了 C++生成简单WAV文件(三)——根据简谱生成菊花台
直接上代码
主体代码:
#include "pch.h"
#include "WAVHead.h"
using namespace std;
//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
vector<BYTE> body;
unsigned int recordCount = 20;// 2147483648;
HRESULT RecordAudioStream()
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
IID_IMMDeviceEnumerator, (void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)
// 因为接下来要手动指定音频格式,所以就不需要了
// hr = pAudioClient->GetMixFormat(&pwfx);
// EXIT_ON_ERROR(hr)
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = 2;
waveFormat.nSamplesPerSec = 48000;
waveFormat.nAvgBytesPerSec = 192000;
waveFormat.wBitsPerSample = 16;
waveFormat.nBlockAlign = 4;
waveFormat.cbSize = 0;
pwfx = &waveFormat;
hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_LOOPBACK,
hnsRequestedDuration,
0,
pwfx,
NULL);
EXIT_ON_ERROR(hr)
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
EXIT_ON_ERROR(hr)
// Notify the audio sink which format to use.
// 如上一行注释,以下的代码是将获取到的音频格式传给另外的类(自己定义的),同样的,因为
// 手动制定了音频格式,所以就不需要通知了
// hr = pMySink->SetFormat(pwfx);
// EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)
// Each loop fills about half of the shared buffer.
while (bDone == FALSE && recordCount > 0)
{
// Sleep for half the buffer duration.
// 如需降低延迟,可删除以下代码
Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
while (packetLength != 0)
{
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
EXIT_ON_ERROR(hr)
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
// 如上一行注释,以下代码是将捕获到的音频数据传给另外的类处理,因为接下
// 来就要在这里处理了,所以也不需要了
// hr = pMySink->CopyData(pData, numFramesAvailable, &bDone);
// EXIT_ON_ERROR(hr)
int dataSize = numFramesAvailable * 4;
for (int i = 0; i < dataSize; i++) {
BYTE tem = pData[i];
body.push_back(pData[i]);
}
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
recordCount--;
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr)
Exit:
// CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pCaptureClient)
return hr;
}
int main() {
CoInitialize(NULL);
RecordAudioStream();
CoUninitialize();
WAVHead head;
head.setSize(body.size());
ofstream ocout;
ocout.open("1214.wav", ios::out | ios::binary);
ocout.write((char*)&head, sizeof head);
for (BYTE n : body) {
ocout.write((char*)&n, 1);
}
ocout.close();
return 0;
}
pch.h
#ifndef PCH_H
#define PCH_H
// TODO: 添加要在此处预编译的标头
#include <iostream>
#include <fstream>
#include <vector>
#include <dshow.h>
#include <Windows.h>
#include <winerror.h>
#include <mmdeviceapi.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <Audioclient.h>
#include <audiopolicy.h>
#endif //PCH_H
接下来修改 RealTek HD 音频管理器 里的设置为 16位 48000Hz
关于最后一行注释掉的代码:
CoTaskMemFree(pwfx);
官方文档 CoTaskMemFree function 中说道:
Frees a block of task memory previously allocated through a call to the CoTaskMemAlloc
or CoTaskMemRealloc function.
因为 pwfx 并不是由 CoTaskMemAlloc or CoTaskMemRealloc 分配的,所以也就不用调用 CoTaskMemFree 释放。