I'm making the pac-man with the winapi32, and using XAudio2
API for playing multiple sounds simultanueosly. I played my sound file pacman_beginning.wav
, pacman_death.wav
, pacman_chomp.wav
, pacman_eatfruit.wav
, pacman_eatghost.wav
, pacman_intermission.wav
, pacman_siren.wav
, and all of them worked fine, but pacman_siren.wav
(I download them from Pac-Man Sounds, and Pacman Sound Effects, and pacman_siren.wav
is renamed from 8d82b5_Pacman_Siren_Sound_Effect.wav
).
the following is part of my code(for XAudio2
, I referred to How to: Play a Sound with XAudio2
)
PacmanApp.hpp:
////////////////////
// PacmanApp //
////////////////////
# include<Windows.h>
# include<functional>
# include<xaudio2.h>
# include<vector>
# include"resource.h" // `IDB_BITMAP1` is defined.
class PacmanApp {
struct AudioData {
BYTE* pDataBuffer;
DWORD dwChunkSize;
IXAudio2SourceVoice* pSourceVoice;
WAVEFORMATEXTENSIBLE wfx;
};
using Delegate = std::function<void(HDC,HDC)>;
// the window procedure.
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// find chunk from `fFile`.
static HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD dwChunkDataPosition);
// read chunk data from `fFile`.
static HRESULT ReadChunkData(HANDLE hFile, void* buffer, DWORD bufferSize, DWORD bufferoffset);
private:
// for message loop.
HINSTANCE hInstance = NULL;
HWND hwnd = NULL;
WNDCLASS wc = { 0 };
MSG msg = { 0 };
// for rendering.
HBITMAP hBM = NULL; // for pac-man sprite.
HBITMAP hBM2 = NULL; // for back-buffer.
RECT rect;
BITMAP bm;
// for application
Delegate callbackFn = nullptr; // callback function.
bool keyState[255] = { 0 }; // for key-down events.
bool isTimeout[256] = { 0 }; // for timer events.
// for sound.
IXAudio2* pXAudio2 = nullptr;
IXAudioMasteringVoice* pMasterVoice = nullptr;
XAUDIO2_BUFFER buffer = { 0 };
std::vector<AudioData> audioList;
private:
PacmanApp() = default;
PacmanApp(const PacmanApp&) = delete;
PacmanApp(PacmanApp&&) = delete;
~PacmanApp() {
if(hBM) DeleteObject(hBM);
if(hBM2) DeleteObject(hBM2);
for(auto& i : audioList) {
delete[] i.pDataBuffer;
}
}
public:
static PacmanApp inst; // for single-ton.
// initialize the pacman application.
bool Init(HINSTANCE hInstance, int nWidth, int nHeight);
// load the desired sound file.
bool LoadSound(const wchar_t strFileName[]);
// start the sound identified by `sndID`.
bool StartSound(UINT sndID, float playLength);
// run the pacman application.
template<typename Functor>
void Run(Functor&& callbackFn, int nCmdShow, UINT fps){
if(this->callbackFn = std::forward<Functor>(callbackFn) ) {
ShowWindow(hwnd, nCmdShow);
SetTimer(hwnd, 256, (1000 / fps), NULL);
while(GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer(hwnd, 256);
}
}
/* omitted */
};
PacmanApp.cpp:
# include"PacmanApp.hpp"
#ifdef _XBOX //Big-Endian
# define fourccRIFF 'RIFF'
# define fourccDATA 'data'
# define fourccFMT 'fmt '
# define fourccWAVE 'WAVE'
# define fourccXWMA 'XWMA'
# define fourccDPDS 'dpds'
#endif
#ifndef _XBOX //Little-Endian
# define fourccRIFF 'FFIR'
# define fourccDATA 'atad'
# define fourccFMT ' tmf'
# define fourccWAVE 'EVAW'
# define fourccXWMA 'AMWX'
# define fourccDPDS 'sdpd'
#endif
// for single-ton.
PacmanApp PacmanApp::inst;
// initialize the pac-man application.
bool PacmanApp::Init(HINSTANCE hInstance, int nWidth, int nHeight) {
/* ommitted. RegisterClass, CreateWindowEx, etc is here. */
// init COM
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)) ) {
return false;
}
// create XAudio engine.
if (FAILED(XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR) ) ) {
return false;
}
// create mastering voice.
if (FAILED(pXAudio2->CreateMasteringVoice(&pMasterVoice) ) ) {
return false;
}
return true;
}
// find chunk from `fFile`.
HRESULT PacmanApp::FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition) {
HRESULT hr = S_OK;
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) {
return HRESULT_FROM_WIN32(GetLastError() );
}
DWORD dwChunkType;
DWORD dwChunkDataSize;
DWORD dwRIFFDataSize = 0;
DWORD dwFileType;
DWORD bytesRead = 0;
DWORD dwOffset = 0;
while (hr == S_OK) {
DWORD dwRead;
// read ChunkID.
if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL)) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
// read ChunkDataSize.
if(0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
if (dwChunkType == fourccRIFF) {
dwRIFFDataSize = dwChunkDataSize;
dwChunkDataSize = 4;
// read RIFFType.
if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
}
else {
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;
}
// read chunk data from `hFile`.
HRESULT PacmanApp::ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset) {
HRESULT hr = S_OK;
DWORD dwRead;
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN) ) {
return HRESULT_FROM_WIN32(GetLastError() );
}
if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL) ) {
hr = HRESULT_FROM_WIN32(GetLastError() );
}
return hr;
}
// load the desired sound file.
bool PacmanApp::LoadSound(const wchar_t strFileName[]) {
HANDLE hFile = CreateFile(
strFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (INVALID_HANDLE_VALUE == hFile) {
return false;
}
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN) ) {
CloseHandle(hFile);
return false;
}
DWORD dwChunkSize;
DWORD dwChunkPosition;
DWORD filetype;
WAVEFORMATEXTENSIBLE wfx = { 0 };
// check the file type, should be fourccWAVE or 'XWMA'
FindChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition);
ReadChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition);
if (filetype != fourccWAVE) {
CloseHandle(hFile);
return false;
}
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition);
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition);
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
ReadChunkData(hFile, audioList.back().pDataBuffer, dwChunkSize, dwChunkPosition);
CloseHandle(hFile);
return true;
}
// start the sound identified by `sndID`.
bool PacmanApp::StartSound(UINT sndID, float playLength) { // playLength is in [0, 1.0f]
if (sndID < audioList.size() ) {
auto& curAudio = audioList[sndID];
buffer.AudioBytes = curAudio.dwChunkSize; // size of the audio buffer in bytes.
buffer.pAudioData = curAudio.pDataBuffer; // buffer containing audio data.
buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after the buffer.
buffer.PlayBegin = 0;
buffer.PlayLength = curAudio.dwChunkSize * playLength;
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&curAudio.pSourceVoice, (WAVEFORMATEX*)&curAudio.wfx)) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->SubmitSourceBuffer(&buffer) ) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->Start(0) ) ) {
return false;
}
}
return true;
}
main.cpp:
# include"PacmanApp.hpp"
# include"PacmanObject.hpp"
/////////////////////
// wWinMain //
/////////////////////
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR nCmdLine,
_In_ int nCmdShow)
{
if(!PacmanApp::inst.Init(hInstance, 1400, 650) {
return 0;
}
/* omitted */
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_beginning.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_death.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_chomp.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_eatfruit.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_eatghost.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_intermission.wav");
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_siren.wav");
enum struct PacmanSound : UINT {
INTRO, DEATH, CHOMP, EAT_FRUIT, EAT_GHOST, INTERMISSION, SIREN
};
// main loop.
PacmanApp::inst.Run([&](HDC srcDC, HDC destDC) {
PacmanApp::inst.StartSound((UINT)PacmanSound::INTRO, 1.0f); // OK. rest of them is same ,but..
// PacmanApp::inst.StartSound((UINT)PacmanSound::SIREN, 1.0f); // only this statement returns false.
}, nCmdShow, 60);
}
In the beginning, I thought that LoadSound
have some problem. but,
pacman_beginning.wav:
- hex dump:
52 49 46 46 EA B5 00 00 57 41 56 45 66 6D 74 20 RIFF....WAVEfmt
10 00 00 00 01 00 01 00 11 2B 00 00 11 2B 00 00 ......... ... ..
01 00 08 00 64 61 74 61 9A B5 00 00 80 7F 80 7F ....data........
- WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_beginning.wav"
WAVE file C:\Users\user\Desktop\pacman_beginning.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 11025
avg bytes per second 11025
sample block size 1 bytes
bits per sample 8
- when
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_beginning.wav");
is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x14
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x0x0000b59a, dwChunkPosition => 0x0000002c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
- the
LoadSound
result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 46490
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 11025,
.nAvgBytesPerSec = 11025,
.nBlockAlign = 1,
.wBitsPerSample = 8,
.cbSize = 0
}
pacman_death.wav:
- hex dump:
52 49 46 46 68 42 00 00 57 41 56 45 4C 49 53 54 RIFFhB..WAVELIST
28 00 00 00 49 4E 46 4F 49 53 46 54 1C 00 00 00 (...INFOISFT....
57 48 41 4D 20 31 2E 33 31 20 62 79 20 41 6E 64 WHAM 1.31 by And
72 65 77 20 42 75 6C 68 61 6B 00 00 66 6D 74 20 rew Bulhak..fmt
10 00 00 00 01 00 01 00 11 2B 00 00 11 2B 00 00 ......... ... ..
01 00 08 00 64 61 74 61 14 42 00 00 80 80 82 7F ....data.B......
- WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_death.wav"
WAVE file C:\Users\user\Desktop\pacman_death.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 11025
avg bytes per second 11025
sample block size 1 bytes
bits per sample 8
- when
PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_death.wav");
is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x44
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x00004214, dwChunkPosition => 0x0000005c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
- the
LoadSound
result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 16916
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 11025,
.nAvgBytesPerSec = 11025,
.nBlockAlign = 1,
.wBitsPerSample = 8,
.cbSize = 0
}
pacman_siren.wav:
hex dump:
52 49 46 46 54 3B 01 00 57 41 56 45 66 6D 74 20 RIFFT;..WAVEfmt
10 00 00 00 01 00 01 00 22 56 00 00 44 AC 00 00 ........"V..D...
02 00 10 00 4C 49 53 54 28 00 00 00 49 4E 46 4F ....LIST(...INFO
49 47 4E 52 06 00 00 00 4F 74 68 65 72 00 49 53 IGNR....Other.IS
46 54 0E 00 00 00 4C 61 76 66 35 39 2E 32 37 2E FT....Lavf59.27.
31 30 30 00 64 61 74 61 00 3B 01 00 00 00 00 00 100.data.;......
WAV format reader output:
C:\Users\user>"C:\Users\user\Desktop\WAV format reader\x64\Debug\WAV format reader.exe" "C:\Users\user\Desktop\pacman_siren.wav"
WAVE file C:\Users\user\Desktop\pacman_siren.wav
riff 'WAVE', chunk 'fmt ', 16 bytes
format tag 0001 (PCM)
number of channels 1
samples per second 22050
avg bytes per second 44100
sample block size 2 bytes
bits per sample 16
when PacmanApp::inst.LoadSound(L"C:/Users/user/Desktop/pacman_siren.wav");
is excuting:
FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x10, dwChunkPosition => 0x14
ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
// fill out the audio data buffer with the contents of the fourccDATA
FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition); // result: dwChunkSize => 0x00013b00, dwChunkPosition => 0x0000005c
audioList.push_back({
new BYTE[dwChunkSize],
dwChunkSize,
nullptr,
wfx
});
- the
LoadSound
result:
auto& curAudio = audioList.back();
curAudio.dwChunkSize = 80640
curAudio.wfx.Format = {
.wFormatTag = 1,
.nChannels = 1,
.nSamplesPerSec = 22050,
.nAvgBytesPerSec = 44100,
.nBlockAlign = 2,
.wBitsPerSample = 16,
.cbSize = 0
}
every calls to LoadSound
work correctly. so, I think there is not any WAVEFORMATEX
structure corruption, or incorrect uninitialized data.SubmitSourceBuffer
is failed when onlysndID = PacmanSound::SIREN
:
bool PacmanApp::StartSound(UINT sndID, float playLength) { // sndID = 6 (=PacmanSound::SIREN)
/* omitted */
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&curAudio.pSourceVoice, (WAVEFORMATEX*)&curAudio.wfx)) ) {
return false;
}
if (FAILED(hr = curAudio.pSourceVoice->SubmitSourceBuffer(&buffer) ) ) {
return false; // hr is XAUDIO2_E_INVALID_CALL (=0x88960001)
}
if (FAILED(hr = curAudio.pSourceVoice->Start(0) ) ) {
return false;
}
/* omitted */
}
MSDN says
Returned by XAudio2 for certain API usage errors (invalid calls and so on) that are hard to avoid completely and should be handled by a title at runtime. (API usage errors that are completely avoidable, such as invalid parameters, cause an ASSERT in debug builds and undefined behavior in retail builds, so no error code is defined for them.)
But, I don't know what I'm missing.. Could anyone help me to solve this issue??.
23/01/29 EDIT:
- add wavdump.cpp outputs, and
LoadSound
results.
CodePudding user response:
I believe the problem you are having is that buffer.PlayLength
is not valid. You are currently using:
buffer.PlayLength = curAudio.dwChunkSize * playLength;
This means you are setting it in terms of BYTES. Both PlayLength
and LoopLength
must be in terms of SAMPLES.
First, try using buffer.PlayLength = 0;
to verify it works.
Computing sample counts is challenging in a general case, but I have code for it in the DirectX Tool Kit for Audio in SoundEffect.cpp
for PCM, ADPCM, xWMA, and XMA2.
For PCM data, you use:
// You'll want to do this with 64-bit integer math to avoid overflow.
samples = (curAudio.dwChunkSize * 8) / (wfx.wBitsPerSample * wfx.nChannels)