用C++以3种不同的API播放“极霓太美“

发布于:2023-01-22 ⋅ 阅读:(175) ⋅ 点赞:(0)

在这里插入图片描述

手机上写的,纯娱乐😅😅😅

使用了Xaudio2,WASAPI和原生WinAPI
编译前,请先安装Windows7 SDK及以上版本,并将项目:“符合模式” 改否

TestSound.cpp


// TestSound.cpp

/*
    测试各Sound API的性能

    MSVC 编译DirectX套件中的Xaudio2 (VS,MSVC配置下的VScode)

    使用前,请先安装 Win7 SDK及以上,否则请删除Xaudio2部分

    然后创建项目,将符合模式改否(网上搜)

    即可成功编译
*/

#include <windows.h>
#include <mmsystem.h>
#include <xaudio2.h>
#include <string>
#include <iostream>
#include <mmreg.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <winerror.h>
#include <wrl.h>
using namespace std;
using namespace Microsoft;
using namespace Microsoft::WRL;

#pragma comment(lib,"xaudio2.lib") 
#pragma comment(lib,"winmm.lib")

#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#define REFTIMES_PER_SEC  5000000
#define REFTIMES_PER_MILLISEC  10000


// 定义抛错宏
#define Check(hr) if(FAILED(hr)) {throw SoundException(hr);}



struct WAVHEAD
{
    char riff[4];
    long riffSize;
    char wave[4];
    char fmt[4];
    long sizePCM;
    INT16 pcm;
    INT16 chanel;
    long one_second_hz;
    long one_second_data;
    INT16 chnale_num;
    INT16 per_sample_bit;
    char data[4];
    long dataSize;
};


// 抛错
class SoundException
{
private:
    HRESULT m_error;

public:
    explicit SoundException(HRESULT hr) :m_error(hr) {};

    // 抛错并弹出一个消息框
    // (内容|标题,默认:"提示"|风格,默认MB_OK|句柄,默认NULL)
    inline int MsgBox(wstring Text, wstring Caption = wstring(L"提示"), UINT style = MB_OK, HWND hwnd = NULL)const
    {
        return ::MessageBoxW(hwnd, &Text[0], &Caption[0], style);
    }


    // 抛错并终止进程
    inline void QuitMsgBox(wstring Text, wstring Caption = wstring(L"提示"), UINT style = MB_OK, HWND hwnd = NULL)const
    {
        ::MessageBoxW(hwnd, &Text[0], &Caption[0], style);
        ::PostQuitMessage(0);  // 对于窗口程序
        ::CoUninitialize();
        ::exit(0);
    }

    inline HRESULT GetError()const
    {
        return m_error;
    }
};



class SoundEngine
{
private:

    WCHAR* SoundFilename = nullptr;

    // Xaudio类
    class XaudioEngine
    {
    private:
        ComPtr<IXAudio2> XAudio2Engine;

        HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition);
        HRESULT ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset);

    public:
        bool playsound(WCHAR* Filename); //播放音乐

    };


    // WASAPI类
    class WASEngine
    {
    private:
        IMMDevice* pDevice = nullptr;
        IMMDeviceEnumerator* pEnumerator = nullptr;
        IAudioClient* pAudioClient = nullptr;
        IAudioRenderClient* pRenderClient = nullptr;
        IAudioClock* audioClock = nullptr;


        class MyAudio
        {
        public:
            WAVHEAD wav;
            MyAudio(){};
            ~MyAudio()
            {
                delete[]buffer;
                ::CloseHandle(hFile);
            };
            HRESULT SetFormat(WAVEFORMATEX*);
            HRESULT LoadData(UINT32, uint16_t*, DWORD*);
            HRESULT init(TCHAR* fName);
            BYTE* buffer = new BYTE[1764 * 3];
        private:
            BYTE* pcmAudio = NULL;
            WAVEFORMATEXTENSIBLE format;

            HANDLE hFile;
            UINT32 totalSamples;


            unsigned int pcmPos = 0;
            UINT32 bufferSize;
            UINT32 bufferPos = 0;
            static const unsigned int sampleCount = 96000 * 5;
            float frequency = 440;
        };

        bool PlayAudio(MyAudio* MySource, WCHAR* Filename);

        MyAudio Source;

    public:

        inline bool playsound(WCHAR* Filename)
        {
            return PlayAudio(&Source, Filename);
        }

    };


    XaudioEngine pXEngine;
    WASEngine pWEngine;

public:

    // 构造函数
    explicit SoundEngine(WCHAR* Filename) :SoundFilename(Filename)
    {
        // 初始化COM库
        if (FAILED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
        {
            ::MessageBoxW(NULL, L"COM组件初始化失败!", L"提示", MB_OK);
            ::PostQuitMessage(0);  // 对于窗口程序
            ::exit(0);
        }
    };

    ~SoundEngine()
    {
        ::CoUninitialize();
    }

    // 改变文件路径
    inline void ChangeFilename(WCHAR* Filename)
    {
        SoundFilename = Filename;
    }

    // 以Xaudio2方式播放音乐
    inline bool playXaudio()
    {
        return pXEngine.playsound(SoundFilename);
    }

    // 以PlaySound方式播放音乐
    inline bool playClassic()
    {
        bool value = PlaySound(SoundFilename, NULL, SND_ASYNC | SND_FILENAME);
        MessageBoxW(NULL, L"按\"确认\"退出程序", L"提示", MB_OK);
        return value;
    }

    // 以WASAPI方式播放音乐
    inline bool playWASAPI()
    {
        return pWEngine.playsound(SoundFilename);
    }

};



HRESULT SoundEngine::WASEngine::MyAudio::SetFormat(WAVEFORMATEX* wfex)
{
    wfex->wFormatTag = 1;
    wfex->nChannels = wav.chanel;
    wfex->cbSize = 0;
    wfex->nSamplesPerSec = wav.one_second_hz;
    wfex->nAvgBytesPerSec = wav.one_second_data;
    wfex->wBitsPerSample = wav.per_sample_bit;
    wfex->nBlockAlign = 4;
    return S_OK;
}

HRESULT SoundEngine::WASEngine::MyAudio::LoadData(UINT32 totalFrames, uint16_t* dataOut, DWORD* flags)
{
    DWORD num = 0;
    BOOL R = ReadFile(hFile, dataOut, totalFrames, &num, NULL);
    if (R == false)
    {
        return S_FALSE;
    }
    return S_OK;
}

HRESULT SoundEngine::WASEngine::MyAudio::init(WCHAR* fName)
{
    DWORD num = 0;
    hFile = CreateFile(fName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (!ReadFile(hFile, &wav, 44, &num, NULL))
    {
        return S_FALSE;
    }
    totalSamples = wav.dataSize;
    return S_OK;
}


HRESULT SoundEngine::XaudioEngine::FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition)
{
    if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
        return HRESULT_FROM_WIN32(GetLastError());

    DWORD dwChunkType = 0;
    DWORD dwChunkDataSize = 0;
    DWORD dwRIFFDataSize = 0;
    DWORD dwFileType = 0;
    DWORD bytesRead = 0;
    DWORD dwOffset = 0;

    while (true)
    {
        DWORD dwRead;
        if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL))
            return HRESULT_FROM_WIN32(GetLastError());
        if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL))
            return HRESULT_FROM_WIN32(GetLastError());
        switch (dwChunkType)
        {
        case fourccRIFF:
            dwRIFFDataSize = dwChunkDataSize;
            dwChunkDataSize = 4;
            if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL))
                return HRESULT_FROM_WIN32(GetLastError());
            break;
        default:
            if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT))
                return HRESULT_FROM_WIN32(GetLastError());
        }
        dwOffset += sizeof(DWORD) * 2;
        if (dwChunkType == fourcc)
        {
            dwChunkSize = dwChunkDataSize;
            dwChunkDataPosition = dwOffset;
            return S_OK;
        }
        dwOffset += dwChunkDataSize;
        if (bytesRead >= dwRIFFDataSize) return S_FALSE;
    }
    return S_OK;
}


HRESULT SoundEngine::XaudioEngine::ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset)
{
    HRESULT hr = S_OK;
    if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN))
        return HRESULT_FROM_WIN32(GetLastError());
    DWORD dwRead;
    if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL))
        hr = HRESULT_FROM_WIN32(GetLastError());
    return hr;
}



bool SoundEngine::XaudioEngine::playsound(WCHAR* Filename)
{
    try
    {
        // 1.创建一个XAudio2对象(IXAudio2接口)
        Check(XAudio2Create(&XAudio2Engine, 0, XAUDIO2_DEFAULT_PROCESSOR));


        // 2.使用第1步建立的引擎建立MasteringVoice
        IXAudio2MasteringVoice* MasterVoice = nullptr;
        Check(XAudio2Engine->CreateMasteringVoice(&MasterVoice))


            // 3.打开文件
        HANDLE hFile = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
        if (INVALID_HANDLE_VALUE == hFile)
            throw SoundException(HRESULT_FROM_WIN32(GetLastError()));
        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
            throw SoundException(HRESULT_FROM_WIN32(GetLastError()));


        // 4.使用第一步建立的引擎建立SourceVoice
        IXAudio2SourceVoice* SourceVoice;
        XAUDIO2_BUFFER buffer = { 0 };
        WAVEFORMATEXTENSIBLE wfx = { 0 };

        DWORD dwChunkSize;
        DWORD dwChunkPosition;
        Check(FindChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition));
        DWORD filetype;
        Check(ReadChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition));
        if (filetype != fourccWAVE) throw SoundException(S_FALSE);
        Check(FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition));
        Check(ReadChunkData(hFile, &(wfx), dwChunkSize, dwChunkPosition));
        Check(FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition));
        BYTE* pDataBuffer = new BYTE[dwChunkSize];
        Check(ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition));
        buffer.AudioBytes = dwChunkSize;
        buffer.pAudioData = pDataBuffer;
        buffer.Flags = XAUDIO2_END_OF_STREAM;
        Check(XAudio2Engine->CreateSourceVoice(&SourceVoice, (WAVEFORMATEX*)&(wfx)));


        // 5.提交音频
        if (SourceVoice->SubmitSourceBuffer(&buffer) < 0)
            throw SoundException(S_FALSE);


        // 6.播放
        if (SourceVoice->Start(0, XAUDIO2_COMMIT_NOW) < 0)
            throw SoundException(S_FALSE);


        // 7.关闭打开的音频文件
        ::CloseHandle(hFile);

        // 休眠5s,不然还没听见声音,程序就结束了
        Sleep(5000);

    }
    catch (SoundException& e)
    {
        if (e.GetError() == S_FALSE)
        {
            e.MsgBox(wstring(L"发生了COM错误"));
        }
        else
        {
            e.MsgBox(wstring(L"发生了未知错误"));
        }
        return false;
    }
    return true;
}


bool SoundEngine::WASEngine::PlayAudio(MyAudio* MySource, WCHAR* Filename)
{
    try
    {
        int sk = 0;
        REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;

        UINT32 bufferSizeInFrames;
        DWORD initStreamFlags = (AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
        WAVEFORMATEX pwfx;
        int16_t* pData = NULL;

        UINT64 audioPlaybackFreq;
        UINT64 audioPlaybackPos;
        UINT64 audioPlaybackPosInSeconds;
        UINT64 audioPlaybackPosInSamples;


        uint32_t wavPlaybackSample = 0;
        UINT32 bufferPadding;
        UINT32 soundBufferLatency;
        UINT32 numFramesToWrite;
        int read = 0, n = 0;


        MySource->init(Filename);
        uint32_t numWavSamples = MySource->wav.dataSize / (MySource->wav.chanel * sizeof(uint16_t));
        DWORD num = 0;

        uint16_t* dataCache = new uint16_t[MySource->wav.dataSize / 2];
        MySource->LoadData(MySource->wav.dataSize, dataCache, NULL);


        Check(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator));
        Check(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice));
        Check(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient));
        Check(MySource->SetFormat(&pwfx));
        Check(pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, initStreamFlags, hnsRequestedDuration * 40, 0, &pwfx, NULL));
        Check(pAudioClient->GetService(__uuidof(IAudioRenderClient), (LPVOID*)(&pRenderClient)));
        Check(pAudioClient->GetBufferSize(&bufferSizeInFrames));
        Check(pAudioClient->Start());


        while (read < MySource->wav.dataSize)
        {
            Check(pAudioClient->GetCurrentPadding(&bufferPadding));
            soundBufferLatency = bufferSizeInFrames / 50;
            numFramesToWrite = soundBufferLatency - bufferPadding;
            Check(pRenderClient->GetBuffer(numFramesToWrite, (BYTE**)(&pData)));
            for (int i = 0; i < numFramesToWrite; i++)
            {
                pData[2 * i] = dataCache[sk++];
                pData[2 * i + 1] = dataCache[sk++];
                if (sk > MySource->wav.dataSize / 2)
                {
                    sk = 0;
                    break;
                }
            }
            Check(pRenderClient->ReleaseBuffer(numFramesToWrite, 0));
            Check(pAudioClient->GetService(__uuidof(IAudioClock), (LPVOID*)(&audioClock)));
            audioClock->GetFrequency(&audioPlaybackFreq);
            audioClock->GetPosition(&audioPlaybackPos, 0);
            audioClock->Release();
            audioPlaybackPosInSeconds = audioPlaybackPos / audioPlaybackFreq;
            audioPlaybackPosInSamples = audioPlaybackPosInSeconds * MySource->wav.one_second_hz;
        }

    }
    catch (SoundException& e)
    {
        if (e.GetError() == S_FALSE)
        {
            e.MsgBox(wstring(L"发生了COM错误"));
        }
        else
        {
            e.MsgBox(wstring(L"发生了未知错误"));
        }
        return false;
    }
    return true;
}




int main()
{
    
    SoundEngine SE(L"寒冬.wav");
    
    //SE.ChangeFilename(L"你干嘛.wav");
    
    SE.playXaudio();
    //SE.playClassic();
    //SE.playWASAPI(); 这个函数不能正常播放

}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到