OSDN Git Service

[Feature] Windows版の効果音再生waveOut APIへ移行
authorshimitei <shimitei@gmail.com>
Tue, 8 Jun 2021 11:49:54 +0000 (20:49 +0900)
committershimitei <shimitei@gmail.com>
Thu, 10 Jun 2021 11:08:30 +0000 (20:08 +0900)
効果音の複数同時再生に対応する。

Hengband/Hengband/Hengband.vcxproj
Hengband/Hengband/Hengband.vcxproj.filters
src/Makefile.am
src/main-win.cpp
src/main-win/main-win-sound.cpp
src/main-win/main-win-sound.h
src/main-win/wav-reader.cpp [new file with mode: 0644]
src/main-win/wav-reader.h [new file with mode: 0644]

index 2b3b8d6..c095bd6 100644 (file)
     <ClCompile Include="..\..\src\main-win\commandline-win.cpp" />\r
     <ClCompile Include="..\..\src\main-win\graphics-win.cpp" />\r
     <ClCompile Include="..\..\src\main-win\main-win-term.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\wav-reader.cpp" />\r
     <ClCompile Include="..\..\src\object-enchant\apply-magic-amulet.cpp" />\r
     <ClCompile Include="..\..\src\object-enchant\apply-magic-ring.cpp" />\r
     <ClCompile Include="..\..\src\object\object-index-list.cpp" />\r
     <ClInclude Include="..\..\src\main-win\commandline-win.h" />\r
     <ClInclude Include="..\..\src\main-win\graphics-win.h" />\r
     <ClInclude Include="..\..\src\main-win\main-win-term.h" />\r
+    <ClInclude Include="..\..\src\main-win\wav-reader.h" />\r
     <ClInclude Include="..\..\src\object-enchant\apply-magic-amulet.h" />\r
     <ClInclude Include="..\..\src\object-enchant\apply-magic-ring.h" />\r
     <ClInclude Include="..\..\src\object\object-index-list.h" />\r
index 68bf4a6..7b23675 100644 (file)
     <ClCompile Include="..\..\src\main-win\main-win-term.cpp">\r
       <Filter>main-win</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\wav-reader.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\src\combat\shoot.h">\r
     <ClInclude Include="..\..\src\main-win\main-win-term.h">\r
       <Filter>main-win</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\wav-reader.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="..\..\src\wall.bmp" />\r
index a300b63..f5ab4bc 100644 (file)
@@ -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
 
index e2166c3..b58a871 100644 (file)
@@ -2497,6 +2497,7 @@ static void hook_quit(concptr str)
     DeleteObject(hbrYellow);
     finalize_bg();
     graphic.finalize();
+    finalize_sound();
 
     UnregisterClassW(AppName, hInstance);
     if (hIcon)
index 448ff7e..af158f8 100644 (file)
@@ -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 <memory>
+
+#include <mmsystem.h>
+
 /*
  * 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<BYTE[]> 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_res *> 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;
 }
index 0139503..d85701d 100644 (file)
@@ -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 (file)
index 0000000..0cb3478
--- /dev/null
@@ -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 (file)
index 0000000..a175508
--- /dev/null
@@ -0,0 +1,57 @@
+#pragma once
+/*!
+ * @file wav-reader.h
+ * @brief Windows版固有実装(WAVファイル読込)ヘッダ
+ */
+
+#include <memory>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+/*!
+ * 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<BYTE[]> buffer;
+};