OSDN Git Service

[Feature] Windows版の効果音再生waveOut APIへ移行
[hengbandforosx/hengbandosx.git] / src / main-win / main-win-sound.cpp
1 /*!
2  * @file main-win-sound.cpp
3  * @brief Windows版固有実装(効果音)
4  */
5
6 #include "main-win/main-win-sound.h"
7 #include "main-win/main-win-cfg-reader.h"
8 #include "main-win/main-win-define.h"
9 #include "main-win/main-win-file-utils.h"
10 #include "main-win/main-win-utils.h"
11 #include "main-win/wav-reader.h"
12 #include "util/angband-files.h"
13
14 #include "main/sound-definitions-table.h"
15
16 #include <memory>
17
18 #include <mmsystem.h>
19
20 /*
21  * Directory name
22  */
23 concptr ANGBAND_DIR_XTRA_SOUND;
24
25 /*
26  * "sound.cfg" data
27  */
28 CfgData *sound_cfg_data;
29
30 /*!
31  * 効果音データ
32  */
33 struct sound_res {
34     sound_res(BYTE* _buf)
35     {
36         buf.reset(_buf);
37     }
38     ~sound_res()
39     {
40         dispose();
41     }
42
43     HWAVEOUT hwo = NULL;
44     /*!
45      * PCMデータバッファ
46      */
47     std::unique_ptr<BYTE[]> buf;
48     WAVEHDR wh = { 0 };
49
50     void dispose()
51     {
52         if (hwo != NULL) {
53             ::waveOutReset(hwo);
54             ::waveOutUnprepareHeader(hwo, &wh, sizeof(WAVEHDR));
55             ::waveOutClose(hwo);
56             hwo = NULL;
57             wh.lpData = nullptr;
58         }
59     }
60 };
61 /*!
62  * 効果音リソースの管理キュー
63  */
64 std::queue<sound_res *> sound_queue;
65
66 /*!
67  * 効果音の再生と管理キューへの追加.
68  * 
69  * @param wf WAVEFORMATEXへのポインタ
70  * @param buf PCMデータバッファ
71  * @param bufsize バッファサイズ
72  * @retval true 正常に処理された
73  * @retval false 処理エラー
74  */
75 static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize)
76 {
77     while (!sound_queue.empty()) {
78         auto res = sound_queue.front();
79         if (res->hwo == NULL || (res->wh.dwFlags & WHDR_DONE)) {
80             delete res;
81             sound_queue.pop();
82             continue;
83         }
84         break;
85     }
86
87     auto res = new sound_res(buf);
88     sound_queue.push(res);
89
90     MMRESULT mr = ::waveOutOpen(&res->hwo, WAVE_MAPPER, wf, NULL, NULL, CALLBACK_NULL);
91     if (mr != MMSYSERR_NOERROR) {
92         return false;
93     }
94
95     WAVEHDR *wh = &res->wh;
96     wh->lpData = (LPSTR)buf;
97     wh->dwBufferLength = bufsize;
98     wh->dwFlags = 0;
99
100     ::waveOutPrepareHeader(res->hwo, wh, sizeof(WAVEHDR));
101     ::waveOutWrite(res->hwo, wh, sizeof(WAVEHDR));
102
103     return true;
104 }
105
106 /*!
107  * 指定ファイルを再生する
108  * 
109  * @param buf ファイル名
110  * @retval true 正常に処理された
111  * @retval false 処理エラー
112  */
113 static bool play_sound_impl(char *filename)
114 {
115     wav_reader reader;
116     if (!reader.open(filename))
117         return false;
118     auto wf = reader.get_waveformat();
119
120     auto data_buffer = reader.read_data();
121     if (data_buffer == nullptr)
122         return false;
123
124     return add_sound_queue(wf, data_buffer, reader.get_data_chunk()->cksize);
125 }
126
127 /*!
128  * @brief action-valに対応する[Sound]セクションのキー名を取得する
129  * @param index "term_xtra()"の第2引数action-valに対応する値
130  * @param buf 使用しない
131  * @return 対応するキー名を返す
132  */
133 static concptr sound_key_at(int index, char *buf)
134 {
135     (void)buf;
136
137     if (index >= SOUND_MAX)
138         return NULL;
139
140     return angband_sound_name[index];
141 }
142
143 /*!
144  * @brief 効果音の設定を読み込む。
145  * @details
146  * "sound_debug.cfg"ファイルを優先して読み込む。無ければ"sound.cfg"ファイルを読み込む。
147  * この処理は複数回実行されることを想定していない。複数回実行した場合には前回読み込まれた設定のメモリは解放されない。
148  */
149 void load_sound_prefs(void)
150 {
151     CfgReader reader(ANGBAND_DIR_XTRA_SOUND, { "sound_debug.cfg", "sound.cfg" });
152     sound_cfg_data = reader.read_sections({ { "Sound", TERM_XTRA_SOUND, sound_key_at } });
153 }
154
155 /*!
156  * @brief 効果音の終了処理
157  */
158 void finalize_sound(void)
159 {
160     while (!sound_queue.empty()) {
161         auto res = sound_queue.front();
162         delete res;
163         sound_queue.pop();
164     }
165 }
166
167 /*!
168  * @brief 指定の効果音を鳴らす。
169  * @param val see sound_type
170  * @retval 0 正常終了
171  * @retval 1 設定なし
172  * @retval -1 PlaySoundの戻り値が正常終了以外
173  */
174 errr play_sound(int val)
175 {
176     concptr filename = sound_cfg_data->get_rand(TERM_XTRA_SOUND, val);
177     if (!filename) {
178         return 1;
179     }
180
181     char buf[MAIN_WIN_MAX_PATH];
182     path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, filename);
183
184     if (play_sound_impl(buf)) {
185         return 0;
186     }
187
188     return -1;
189 }