From aebd9d191d6511b3d423e38d8b7257745a86a559 Mon Sep 17 00:00:00 2001 From: shimitei Date: Tue, 8 Jun 2021 20:49:54 +0900 Subject: [PATCH] =?utf8?q?[Feature]=20Windows=E7=89=88=E3=81=AE=E5=8A=B9?= =?utf8?q?=E6=9E=9C=E9=9F=B3=E5=86=8D=E7=94=9FwaveOut=20API=E3=81=B8?= =?utf8?q?=E7=A7=BB=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 効果音の複数同時再生に対応する。 --- Hengband/Hengband/Hengband.vcxproj | 2 + Hengband/Hengband/Hengband.vcxproj.filters | 6 ++ src/Makefile.am | 1 + src/main-win.cpp | 1 + src/main-win/main-win-sound.cpp | 118 ++++++++++++++++++++++++++++- src/main-win/main-win-sound.h | 1 + src/main-win/wav-reader.cpp | 69 +++++++++++++++++ src/main-win/wav-reader.h | 57 ++++++++++++++ 8 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 src/main-win/wav-reader.cpp create mode 100644 src/main-win/wav-reader.h diff --git a/Hengband/Hengband/Hengband.vcxproj b/Hengband/Hengband/Hengband.vcxproj index 2b3b8d674..c095bd67c 100644 --- a/Hengband/Hengband/Hengband.vcxproj +++ b/Hengband/Hengband/Hengband.vcxproj @@ -271,6 +271,7 @@ + @@ -949,6 +950,7 @@ + diff --git a/Hengband/Hengband/Hengband.vcxproj.filters b/Hengband/Hengband/Hengband.vcxproj.filters index 68bf4a69c..7b23675a1 100644 --- a/Hengband/Hengband/Hengband.vcxproj.filters +++ b/Hengband/Hengband/Hengband.vcxproj.filters @@ -2292,6 +2292,9 @@ main-win + + main-win + @@ -4929,6 +4932,9 @@ main-win + + main-win + diff --git a/src/Makefile.am b/src/Makefile.am index a300b63e1..f5ab4bcc9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -939,6 +939,7 @@ EXTRA_hengband_SOURCES = \ main-win/main-win-term.cpp main-win/main-win-term.h \ main-win/main-win-tokenizer.cpp main-win/main-win-tokenizer.h \ main-win/main-win-utils.cpp main-win/main-win-utils.h \ + main-win/wav-reader.cpp main-win/wav-reader.h \ wall.bmp \ stdafx.cpp stdafx.h diff --git a/src/main-win.cpp b/src/main-win.cpp index e2166c3e4..b58a871a0 100644 --- a/src/main-win.cpp +++ b/src/main-win.cpp @@ -2497,6 +2497,7 @@ static void hook_quit(concptr str) DeleteObject(hbrYellow); finalize_bg(); graphic.finalize(); + finalize_sound(); UnregisterClassW(AppName, hInstance); if (hIcon) diff --git a/src/main-win/main-win-sound.cpp b/src/main-win/main-win-sound.cpp index 448ff7eb3..af158f8fb 100644 --- a/src/main-win/main-win-sound.cpp +++ b/src/main-win/main-win-sound.cpp @@ -7,12 +7,16 @@ #include "main-win/main-win-cfg-reader.h" #include "main-win/main-win-define.h" #include "main-win/main-win-file-utils.h" -#include "main-win/main-win-mmsystem.h" #include "main-win/main-win-utils.h" +#include "main-win/wav-reader.h" #include "util/angband-files.h" #include "main/sound-definitions-table.h" +#include + +#include + /* * Directory name */ @@ -24,6 +28,103 @@ concptr ANGBAND_DIR_XTRA_SOUND; CfgData *sound_cfg_data; /*! + * 効果音データ + */ +struct sound_res { + sound_res(BYTE* _buf) + { + buf.reset(_buf); + } + ~sound_res() + { + dispose(); + } + + HWAVEOUT hwo = NULL; + /*! + * PCMデータバッファ + */ + std::unique_ptr buf; + WAVEHDR wh = { 0 }; + + void dispose() + { + if (hwo != NULL) { + ::waveOutReset(hwo); + ::waveOutUnprepareHeader(hwo, &wh, sizeof(WAVEHDR)); + ::waveOutClose(hwo); + hwo = NULL; + wh.lpData = nullptr; + } + } +}; +/*! + * 効果音リソースの管理キュー + */ +std::queue sound_queue; + +/*! + * 効果音の再生と管理キューへの追加. + * + * @param wf WAVEFORMATEXへのポインタ + * @param buf PCMデータバッファ + * @param bufsize バッファサイズ + * @retval true 正常に処理された + * @retval false 処理エラー + */ +static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize) +{ + while (!sound_queue.empty()) { + auto res = sound_queue.front(); + if (res->hwo == NULL || (res->wh.dwFlags & WHDR_DONE)) { + delete res; + sound_queue.pop(); + continue; + } + break; + } + + auto res = new sound_res(buf); + sound_queue.push(res); + + MMRESULT mr = ::waveOutOpen(&res->hwo, WAVE_MAPPER, wf, NULL, NULL, CALLBACK_NULL); + if (mr != MMSYSERR_NOERROR) { + return false; + } + + WAVEHDR *wh = &res->wh; + wh->lpData = (LPSTR)buf; + wh->dwBufferLength = bufsize; + wh->dwFlags = 0; + + ::waveOutPrepareHeader(res->hwo, wh, sizeof(WAVEHDR)); + ::waveOutWrite(res->hwo, wh, sizeof(WAVEHDR)); + + return true; +} + +/*! + * 指定ファイルを再生する + * + * @param buf ファイル名 + * @retval true 正常に処理された + * @retval false 処理エラー + */ +static bool play_sound_impl(char *filename) +{ + wav_reader reader; + if (!reader.open(filename)) + return false; + auto wf = reader.get_waveformat(); + + auto data_buffer = reader.read_data(); + if (data_buffer == nullptr) + return false; + + return add_sound_queue(wf, data_buffer, reader.get_data_chunk()->cksize); +} + +/*! * @brief action-valに対応する[Sound]セクションのキー名を取得する * @param index "term_xtra()"の第2引数action-valに対応する値 * @param buf 使用しない @@ -52,6 +153,18 @@ void load_sound_prefs(void) } /*! + * @brief 効果音の終了処理 + */ +void finalize_sound(void) +{ + while (!sound_queue.empty()) { + auto res = sound_queue.front(); + delete res; + sound_queue.pop(); + } +} + +/*! * @brief 指定の効果音を鳴らす。 * @param val see sound_type * @retval 0 正常終了 @@ -68,8 +181,9 @@ errr play_sound(int val) char buf[MAIN_WIN_MAX_PATH]; path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, filename); - if (::PlaySoundW(to_wchar(buf).wc_str(), 0, SND_FILENAME | SND_ASYNC)) { + if (play_sound_impl(buf)) { return 0; } + return -1; } diff --git a/src/main-win/main-win-sound.h b/src/main-win/main-win-sound.h index 0139503dd..d85701dc5 100644 --- a/src/main-win/main-win-sound.h +++ b/src/main-win/main-win-sound.h @@ -7,4 +7,5 @@ extern concptr ANGBAND_DIR_XTRA_SOUND; extern CfgData *sound_cfg_data; void load_sound_prefs(void); +void finalize_sound(void); errr play_sound(int val); diff --git a/src/main-win/wav-reader.cpp b/src/main-win/wav-reader.cpp new file mode 100644 index 000000000..0cb3478da --- /dev/null +++ b/src/main-win/wav-reader.cpp @@ -0,0 +1,69 @@ +/*! + * @file wav-reader.cpp + * @brief Windows版固有実装(WAVファイル読込) + */ + +#include "main-win/wav-reader.h" +#include "main-win/main-win-utils.h" + +bool wav_reader::open(char *filename) +{ + close(); + + this->hmmio = ::mmioOpenW(to_wchar(filename).wc_str(), NULL, MMIO_READ); + if (this->hmmio == NULL) + return false; + + MMRESULT mmresult; + LONG read_size; + LONG readed_size; + + this->riff_chunk.fccType = mmioFOURCC('W', 'A', 'V', 'E'); + mmresult = ::mmioDescend(this->hmmio, &this->riff_chunk, NULL, MMIO_FINDRIFF); + if (mmresult != MMSYSERR_NOERROR) + return false; + + this->fmt_chunk.ckid = mmioFOURCC('f', 'm', 't', ' '); + mmresult = ::mmioDescend(this->hmmio, &this->fmt_chunk, &this->riff_chunk, MMIO_FINDCHUNK); + if (mmresult != MMSYSERR_NOERROR) + return false; + + if (this->fmt_chunk.cksize > sizeof(this->waveformatex)) + return false; + read_size = this->fmt_chunk.cksize; + readed_size = ::mmioRead(this->hmmio, (HPSTR) & this->waveformatex, read_size); + if (readed_size != read_size) + return false; + if (this->waveformatex.wFormatTag != WAVE_FORMAT_PCM) + return false; + mmresult = ::mmioAscend(this->hmmio, &this->fmt_chunk, 0); + if (mmresult != MMSYSERR_NOERROR) + return false; + + this->data_chunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); + mmresult = ::mmioDescend(this->hmmio, &this->data_chunk, &riff_chunk, MMIO_FINDCHUNK); + if (mmresult != MMSYSERR_NOERROR) + return false; + + this->buffer.reset(new BYTE[data_chunk.cksize]); + read_size = this->data_chunk.cksize; + readed_size = ::mmioRead(this->hmmio, (HPSTR)this->buffer.get(), read_size); + if (readed_size != read_size) { + return false; + } + + return true; +} + +BYTE *wav_reader::read_data() +{ + return this->buffer.release(); +} + +void wav_reader::close() +{ + if (this->hmmio != NULL) { + ::mmioClose(this->hmmio, 0); + this->hmmio = NULL; + } +} diff --git a/src/main-win/wav-reader.h b/src/main-win/wav-reader.h new file mode 100644 index 000000000..a175508d4 --- /dev/null +++ b/src/main-win/wav-reader.h @@ -0,0 +1,57 @@ +#pragma once +/*! + * @file wav-reader.h + * @brief Windows版固有実装(WAVファイル読込)ヘッダ + */ + +#include + +#include +#include + +/*! + * WAVファイルの読み込み + */ +class wav_reader { +public: + wav_reader() + : hmmio(NULL) + { + } + ~wav_reader() + { + close(); + } + + /*! + * WAVファイルを開く + * + * @param filename + * @retval true 正常に処理された + * @retval false 処理エラー + */ + bool open(char *filename); + /*! + * PCMデータ取得 + * @details 呼び出し元でdelete[]すること + * @return PCMデータ + */ + BYTE* read_data(); + const WAVEFORMATEX *get_waveformat() + { + return &waveformatex; + } + const MMCKINFO *get_data_chunk() + { + return &data_chunk; + } + void close(); + +protected: + HMMIO hmmio; + MMCKINFO riff_chunk{}; + MMCKINFO fmt_chunk{}; + WAVEFORMATEX waveformatex{}; + MMCKINFO data_chunk{}; + std::unique_ptr buffer; +}; -- 2.11.0