8 #pragma comment(lib, "winmm.lib") 13 using error::MmioError;
14 using error::OggVorbisError;
16 using error::XAudioError;
20 void loadWaveFile(SoundEffect *out,
const wchar_t *path)
24 MMIOINFO mmioInfo = { 0 };
25 mmioInfo.pchBuffer =
reinterpret_cast<HPSTR
>(bin.data());
26 mmioInfo.fccIOProc = FOURCC_MEM;
27 mmioInfo.cchBuffer =
static_cast<LONG
>(bin.size());
29 HMMIO tmpHMmio = mmioOpen(0, &mmioInfo, MMIO_READ);
30 if (tmpHMmio ==
nullptr) {
31 throw MmioError(
"mmioOpen() failed", mmioInfo.wErrorRet);
33 std::unique_ptr<HMMIO, hmmioDeleter> hMmio(tmpHMmio);
36 MMRESULT mmRes = MMSYSERR_NOERROR;
37 MMCKINFO riffChunk = { 0 };
38 riffChunk.fccType = mmioFOURCC(
'W',
'A',
'V',
'E');
39 mmRes = mmioDescend(hMmio.get(), &riffChunk,
nullptr, MMIO_FINDRIFF);
40 if (mmRes != MMSYSERR_NOERROR) {
41 throw MmioError(
"mmioDescend() failed", mmRes);
44 MMCKINFO formatChunk = { 0 };
45 formatChunk.ckid = mmioFOURCC(
'f',
'm',
't',
' ');
46 mmRes = mmioDescend(hMmio.get(), &formatChunk, &riffChunk, MMIO_FINDCHUNK);
47 if (mmRes != MMSYSERR_NOERROR) {
48 throw MmioError(
"mmioDescend() failed", mmRes);
51 ::ZeroMemory(&out->format,
sizeof(out->format));
52 DWORD fmtSize = std::min(formatChunk.cksize, static_cast<DWORD>(
sizeof(out->format)));
53 DWORD size = mmioRead(hMmio.get(),
reinterpret_cast<HPSTR
>(&out->format), fmtSize);
54 if (size != fmtSize) {
55 throw MmioError(
"mmioRead() failed", size);
58 mmRes = mmioAscend(hMmio.get(), &formatChunk, 0);
59 if (mmRes != MMSYSERR_NOERROR) {
60 throw MmioError(
"mmioAscend() failed", mmRes);
63 MMCKINFO dataChunk = { 0 };
64 dataChunk.ckid = mmioFOURCC(
'd',
'a',
't',
'a');
65 mmRes = mmioDescend(hMmio.get(), &dataChunk, &riffChunk, MMIO_FINDCHUNK);
66 if (mmRes != MMSYSERR_NOERROR) {
67 throw MmioError(
"mmioDescend() failed", mmRes);
71 throw MmioError(
"data size too large", 0);
73 out->samples.resize(dataChunk.cksize);
74 size = mmioRead(hMmio.get(),
reinterpret_cast<HPSTR
>(out->samples.data()), dataChunk.cksize);
75 if (size != dataChunk.cksize) {
76 throw MmioError(
"mmioRead() failed", size);
90 IXAudio2 *ptmpIXAudio2 =
nullptr;
93 flags = XAUDIO2_DEBUG_ENGINE;
95 hr = ::XAudio2Create(&ptmpIXAudio2, flags);
96 checkDXResult<XAudioError>(hr,
"XAudio2Create() failed");
97 m_pIXAudio.reset(ptmpIXAudio2);
99 IXAudio2MasteringVoice *ptmpMasterVoice =
nullptr;
100 hr = m_pIXAudio->CreateMasteringVoice(&ptmpMasterVoice);
101 checkDXResult<XAudioError>(hr,
"IXAudio2::CreateMasteringVoice() failed");
102 m_pMasterVoice.reset(ptmpMasterVoice);
119 auto res = std::make_shared<SoundEffect>();
120 loadWaveFile(res.get(), path);
127 PyaingSeElem *ppEntry = findFreeSeEntry();
128 if (ppEntry ==
nullptr) {
133 SourceVoicePtr &entryVoice = std::get<1>(*ppEntry);
134 ASSERT(entryBuf ==
nullptr);
135 ASSERT(entryVoice ==
nullptr);
137 XAUDIO2_BUFFER buffer = { 0 };
139 buffer.AudioBytes =
static_cast<UINT32
>(se->samples.size());
140 buffer.pAudioData = se->samples.data();
141 buffer.Flags = XAUDIO2_END_OF_STREAM;
145 IXAudio2SourceVoice *ptmpSrcVoice =
nullptr;
146 hr = m_pIXAudio->CreateSourceVoice(&ptmpSrcVoice, &se->format);
147 checkDXResult<XAudioError>(hr,
"IXAudio2::CreateSourceVoice() failed");
148 SourceVoicePtr pSrcVoice(ptmpSrcVoice);
151 hr = pSrcVoice->SubmitSourceBuffer(&buffer);
152 checkDXResult<XAudioError>(hr,
"IXAudio2SourceVoice::SubmitSourceBuffer() failed");
153 hr = pSrcVoice->Start();
154 checkDXResult<XAudioError>(hr,
"IXAudio2SourceVoice::Start() failed");
159 entryVoice = std::move(pSrcVoice);
164 for (
const auto &entry : m_playingSeList) {
166 const SourceVoicePtr &entryVoice = std::get<1>(entry);
167 if (entryBuf ==
nullptr) {
168 ASSERT(entryVoice ==
nullptr);
172 XAUDIO2_VOICE_STATE state = { 0 };
173 entryVoice->GetState(&state);
174 if (state.BuffersQueued != 0) {
184 for (
auto &entry : m_playingSeList) {
186 SourceVoicePtr &entryVoice = std::get<1>(entry);
192 XAudio2::PyaingSeElem *XAudio2::findFreeSeEntry()
194 for (
auto &entry : m_playingSeList) {
196 SourceVoicePtr &entryVoice = std::get<1>(entry);
197 if (entryBuf ==
nullptr) {
198 ASSERT(entryVoice ==
nullptr);
205 void XAudio2::processFrameSe()
208 for (
auto &entry : m_playingSeList) {
210 SourceVoicePtr &entryVoice = std::get<1>(entry);
212 if (entryBuf ==
nullptr) {
213 ASSERT(entryVoice ==
nullptr);
217 XAUDIO2_VOICE_STATE state = { 0 };
218 entryVoice->GetState(&state);
219 if (state.BuffersQueued == 0) {
243 vorbis_info *info = ov_info(bgm->ovFp(), -1);
244 if (info ==
nullptr) {
245 throw OggVorbisError(
"ov_info() failed", 0);
247 debug::writef(L
"ov_info: channels=%d, rate=%ld", info->channels, info->rate);
249 WAVEFORMATEX format = { 0 };
250 format.wFormatTag = WAVE_FORMAT_PCM;
251 format.nChannels = info->channels;
252 format.nSamplesPerSec = info->rate;
253 format.wBitsPerSample = 16;
254 format.nBlockAlign = info->channels * format.wBitsPerSample / 8;
255 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
259 IXAudio2SourceVoice *ptmpSrcVoice =
nullptr;
260 hr = m_pIXAudio->CreateSourceVoice(&ptmpSrcVoice, &format);
261 checkDXResult<XAudioError>(hr,
"IXAudio2::CreateSourceVoice() failed");
262 m_pBgmVoice.reset(ptmpSrcVoice);
267 hr = m_pBgmVoice->Start();
268 checkDXResult<XAudioError>(hr,
"IXAudio2SourceVoice::Start() failed");
278 m_playingBgm.reset();
283 void XAudio2::processFrameBgm()
287 if (m_pBgmVoice ==
nullptr) {
291 ASSERT(m_playingBgm !=
nullptr);
293 XAUDIO2_VOICE_STATE state = { 0 };
294 m_pBgmVoice->GetState(&state);
301 OggVorbis_File *fp = m_playingBgm->ovFp();
302 uint32_t readSum = 0;
305 long size = ::ov_read(fp,
306 &m_pBgmBuffer[base + readSum],
309 throw OggVorbisError(
"ov_read() failed", size);
315 int ret = ::ov_time_seek(fp, 0.0);
317 throw OggVorbisError(
"ov_time_seek() failed", ret);
323 XAUDIO2_BUFFER buffer = { 0 };
324 buffer.AudioBytes =
static_cast<UINT32
>(readSum);
325 buffer.pAudioData =
reinterpret_cast<BYTE *
>(&m_pBgmBuffer[base]);
326 hr = m_pBgmVoice->SubmitSourceBuffer(&buffer);
327 checkDXResult<XAudioError>(hr,
"IXAudio2SourceVoice::SubmitSourceBuffer() failed");
335 m_ovFileBin(ovFileBin),
339 ov_callbacks callbacks = { read, seek, close, tell };
340 int ret = ::ov_open_callbacks(
this, &m_ovFile,
nullptr, 0, callbacks);
342 throw OggVorbisError(
"ov_open_callbacks() failed", ret);
345 m_ovFp.reset(&m_ovFile);
348 size_t Bgm::read(
void *ptr,
size_t size,
size_t nmemb,
void *datasource)
350 auto *obj =
static_cast<Bgm *
>(datasource);
352 uint32_t totalSize =
static_cast<uint32_t
>(obj->m_ovFileBin.size());
353 ASSERT(totalSize >= obj->m_readPos);
354 uint32_t remainSize = totalSize - obj->m_readPos;
355 size_t count = std::min(remainSize / size, nmemb);
356 ::memcpy(ptr, &obj->m_ovFileBin.data()[obj->m_readPos], size * count);
357 obj->m_readPos +=
static_cast<uint32_t
>(size * count);
361 int Bgm::seek(
void *datasource, int64_t
offset,
int whence)
363 auto *obj =
static_cast<Bgm *
>(datasource);
366 uint32_t totalSize =
static_cast<uint32_t
>(obj->m_ovFileBin.size());
369 obj->m_readPos =
static_cast<uint32_t
>(
offset);
372 obj->m_readPos +=
static_cast<uint32_t
>(
offset);
376 obj->m_readPos =
static_cast<uint32_t
>(totalSize +
offset);
381 if (obj->m_readPos > totalSize) {
382 obj->m_readPos = totalSize;
385 else if (obj->m_readPos < 0) {
392 long Bgm::tell(
void *datasource)
394 auto *obj =
static_cast<Bgm *
>(datasource);
395 return obj->m_readPos;
398 int Bgm::close(
void *datasource)
400 auto *obj =
static_cast<Bgm *
>(datasource);
std::shared_ptr< BgmResource > BgmResourcePtr
BgmResourcePtr loadBgm(const wchar_t *path)
const uint32_t SoundFileSizeMax
void writef(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf.
std::vector< uint8_t > loadFile(const wchar_t *fileName)
Load file from abstract file system.
#define ASSERT(x)
Assertion which uses debug framework.
SeResourcePtr loadSoundEffect(const wchar_t *path)
void stopAllSoundEffect()
const uint32_t FileSizeMax
0x7fffffff = 2GiB
void checkDXResult(HRESULT hr, const std::string &msg)
void writeLine(const wchar_t *str=L"") noexcept
Write debug string and new line.
std::shared_ptr< SeResource > SeResourcePtr
Bgm(file::Bytes &&ovFileBin)
std::vector< uint8_t > Bytes
File byte sequence. Vector of uint8_t.
const uint32_t BgmBufferSize
const uint32_t BgmBufferCount
bool isPlayingAnySoundEffect() const
const uint32_t BgmOvReadSize
void playBgm(const BgmResourcePtr &bgm)
void playSoundEffect(const SeResourcePtr &se)