手机上写的,纯娱乐😅😅😅
使用了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 后查看