以下是使用WASAPI封装的多个实例同时播放PCM文件的示例代码:
#include <iostream>
#include <vector>
#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
// 定义一个结构体来保存每个音频实例的相关信息
struct AudioInstance {
IMMDevice* pDevice;
IAudioClient* pAudioClient;
WAVEFORMATEX* pWaveFormat;
UINT32 bufferSize;
};
// 初始化音频实例
bool InitializeAudioInstance(AudioInstance& audioInstance, const wchar_t* filename) {
HRESULT hr;
// 获取默认音频渲染设备
IMMDeviceEnumerator* pEnumerator = NULL;
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&pEnumerator
);
if (FAILED(hr)) {
std::cout << "Failed to create device enumerator" << std::endl;
return false;
}
hr = pEnumerator->GetDefaultAudioEndpoint(
eRender, // 指定为音频渲染设备,如果需要录制则选择eCapture
eConsole, // 指定为通用音频终端
&audioInstance.pDevice
);
// 释放设备枚举器
pEnumerator->Release();
if (FAILED(hr)) {
std::cout << "Failed to get default audio endpoint" << std::endl;
return false;
}
// 创建IAudioClient接口实例并初始化参数
hr = audioInstance.pDevice->Activate(
__uuidof(IAudioClient),
CLSCTX_ALL,
NULL,
(void**)&audioInstance.pAudioClient
);
if (FAILED(hr)) {
std::cout << "Failed to activate audio client" << std::endl;
return false;
}
// 设置音频格式(此处以PCM为例)
WAVEFORMATEX* waveFormat = NULL;
hr = audioInstance.pAudioClient->GetMixFormat(&waveFormat);
if (FAILED(hr)) {
std::cout << "Failed to get mix format" << std::endl;
return false;
}
audioInstance.pWaveFormat = waveFormat;
// 创建音频渲染流
hr = audioInstance.pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED, // 共享模式
0, // 默认周期时间0表示使用默认值
0, // 音频缓冲区大小设为0表示使用默认值
0, // 每次读取音频数据的最小包数量,设置为0表示使用默认值
waveFormat, // 音频格式
NULL // 不指定会话GUID,表示通用会话
);
if (FAILED(hr)) {
std::cout << "Failed to initialize audio client" << std::endl;
return false;
}
// 获取缓冲区大小
hr = audioInstance.pAudioClient->GetBufferSize(&audioInstance.bufferSize);
if (FAILED(hr)) {
std::cout << "Failed to get buffer size" << std::endl;
return false;
}
return true;
}
// PCM文件播放函数
void PlayPCMFile(const wchar_t* filename) {
FILE* file = nullptr;
_wfopen_s(&file, filename, L"rb");
if (file == nullptr) {
std::cout << "Failed to open PCM file: " << filename << std::endl;
return;
}
// 读取文件内容
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = new char[fileSize];
fread(buffer, sizeof(char), fileSize, file);
// 初始化音频实例
AudioInstance audioInstance;
bool success = InitializeAudioInstance(audioInstance, filename);
if (!success) {
std::cout << "Failed to initialize audio instance" << std::endl;
fclose(file);
delete[] buffer;
return;
}
// 创建音频渲染客户端
IAudioRenderClient* pRenderClient = NULL;
HRESULT hr = audioInstance.pAudioClient->GetService(
__uuidof(IAudioRenderClient),
(void**)&pRenderClient
);
if (FAILED(hr)) {
std::cout << "Failed to get audio render client" << std::endl;
fclose(file);
delete[] buffer;
return;
}
UINT32 numFramesPadding; // 当前缓冲区内的帧数
BYTE* pData; // 指向音频数据缓冲区的指针
// 开始播放音频
hr = audioInstance.pAudioClient->Start();
if (FAILED(hr)) {
std::cout << "Failed to start audio client" << std::endl;
pRenderClient->Release();
fclose(file);
delete[] buffer;
return;
}
while (true) {
hr = audioInstance.pAudioClient->GetCurrentPadding(&numFramesPadding);
if (FAILED(hr)) {
std::cout << "Failed to get current padding" << std::endl;
break;
}
// 计算可写入的帧数
UINT32 numFramesAvailable = audioInstance.bufferSize - numFramesPadding;
if (numFramesAvailable > 0) {
// 获取音频数据缓冲区指针
hr = pRenderClient->GetBuffer(numFramesAvailable, &pData);
if (FAILED(hr)) {
std::cout << "Failed to get buffer" << std::endl;
break;
}
// 将音频数据写入缓冲区
memcpy(pData, buffer, numFramesAvailable * audioInstance.pWaveFormat->nBlockAlign);
// 释放缓冲区
hr = pRenderClient->ReleaseBuffer(numFramesAvailable, 0);
if (FAILED(hr)) {
std::cout << "Failed to release buffer" << std::endl;
break;
}
// 更新剩余的音频数据
fileSize -= numFramesAvailable * audioInstance.pWaveFormat->nBlockAlign;
buffer += numFramesAvailable * audioInstance.pWaveFormat->nBlockAlign;
if (fileSize <= 0) {
break; // 文件已经播放完毕,退出循环
}
}
Sleep(10); // 等待一小段时间后再继续读取和播放音频数据
}
delete[] buffer;
// 停止和清理资源
audioInstance.pAudioClient->Stop();
pRenderClient->Release();
fclose(file);
}
int main() {
// 需要同时播放的PCM文件路径
const wchar_t* filename1 = L"path/to/file1.pcm";
const wchar_t* filename2 = L"path/to/file2.pcm";
PlayPCMFile(filename1); // 播放第一个PCM文件
PlayPCMFile(filename2); // 播放第二个PCM文件
return 0;
}
这个示例代码演示了如何使用WASAPI封装来初始化和播放多个音频实例,每个实例都可以同时播放不同的PCM文件。你可以根据需要配置其他参数,例如缓冲区大小等。请注意,此示例中仅包含初始化和播放部分的代码,你可能需要根据自己的需求进行适当的修改和扩展。
在使用此代码时,请确保已经链接正确的库并设置编译选项。
内容由零声教学AI助手提供,问题来源于学员提问