使用WASAPI捕获声卡音频

时间:2024-03-22 22:24:47

参考文档:

直接上代码

主体代码:

#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

使用WASAPI捕获声卡音频

关于最后一行注释掉的代码:

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 释放。