ZBLOG

WASAPI多个实例同时使用的封装例子,做到每个实例都同时播放pcm文件

以下是使用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助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=3466

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?