OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[hengbandforosx/hengbandosx.git] / src / main-win / main-win-music.cpp
1 /*!
2  * @file main-win-music.cpp
3  * @brief Windows版固有実装(BGM)
4  */
5
6 #include "main-win/main-win-music.h"
7 #include "dungeon/quest.h"
8 #include "floor/floor-town.h"
9 #include "main-win/main-win-define.h"
10 #include "main-win/main-win-file-utils.h"
11 #include "main-win/main-win-mci.h"
12 #include "main-win/main-win-mmsystem.h"
13 #include "main-win/main-win-tokenizer.h"
14 #include "main-win/main-win-utils.h"
15 #include "main/scene-table.h"
16 #include "main/sound-of-music.h"
17 #include "monster-race/monster-race.h"
18 #include "system/dungeon-info.h"
19 #include "system/monster-race-info.h"
20 #include "term/z-term.h"
21 #include "util/angband-files.h"
22 #include "world/world.h"
23 #include <algorithm>
24 #include <digitalv.h>
25 #include <limits>
26
27 bool use_pause_music_inactive = false;
28 static int current_music_type = TERM_XTRA_MUSIC_MUTE;
29 static int current_music_id = 0;
30 // current filename being played
31 static char current_music_path[MAIN_WIN_MAX_PATH];
32
33 /*
34  * Directory name
35  */
36 concptr ANGBAND_DIR_XTRA_MUSIC;
37
38 /*
39  * "music.cfg" data
40  */
41 CfgData *music_cfg_data;
42
43 namespace main_win_music {
44
45 /*!
46  * @brief action-valに対応する[Basic]セクションのキー名を取得する
47  * @param index "term_xtra()"の第2引数action-valに対応する値
48  * @param buf 使用しない
49  * @return 対応するキー名を返す
50  */
51 static concptr basic_key_at(int index, char *buf)
52 {
53     (void)buf;
54
55     if (index >= MUSIC_BASIC_MAX) {
56         return nullptr;
57     }
58
59     return angband_music_basic_name[index];
60 }
61
62 /*!
63  * @brief action-valに対応する[Dungeon]セクションのキー名を取得する
64  * @param index "term_xtra()"の第2引数action-valに対応する値
65  * @param buf バッファ
66  * @return 対応するキー名を返す
67  */
68 static concptr dungeon_key_at(int index, char *buf)
69 {
70     if (index >= static_cast<int>(dungeons_info.size())) {
71         return nullptr;
72     }
73
74     sprintf(buf, "dungeon%03d", index);
75     return buf;
76 }
77
78 /*!
79  * @brief action-valに対応する[Quest]セクションのキー名を取得する
80  * @param index "term_xtra()"の第2引数action-valに対応する値
81  * @param buf バッファ
82  * @return 対応するキー名を返す
83  */
84 static concptr quest_key_at(int index, char *buf)
85 {
86     const auto &quest_list = QuestList::get_instance();
87     if (index > enum2i(quest_list.rbegin()->first)) {
88         return nullptr;
89     }
90
91     sprintf(buf, "quest%03d", index);
92     return buf;
93 }
94
95 /*!
96  * @brief action-valに対応する[Town]セクションのキー名を取得する
97  * @param index "term_xtra()"の第2引数action-valに対応する値
98  * @param buf バッファ
99  * @return 対応するキー名を返す
100  */
101 static concptr town_key_at(int index, char *buf)
102 {
103     if (index >= static_cast<int>(max_towns)) {
104         return nullptr;
105     }
106
107     sprintf(buf, "town%03d", index);
108     return buf;
109 }
110
111 /*!
112  * @brief action-valに対応する[Monster]セクションのキー名を取得する
113  * @param index "term_xtra()"の第2引数action-valに対応する値
114  * @param buf バッファ
115  * @return 対応するキー名を返す
116  */
117 static concptr monster_key_at(int index, char *buf)
118 {
119     if (index >= static_cast<int>(monraces_info.size())) {
120         return nullptr;
121     }
122
123     sprintf(buf, "monster%04d", index);
124     return buf;
125 }
126
127 /*!
128  * @brief BGMの設定を読み込む。
129  * @details
130  * "music_debug.cfg"ファイルを優先して読み込む。無ければ"music.cfg"ファイルを読み込む。
131  * この処理は複数回実行されることを想定していない。複数回実行した場合には前回読み込まれた設定のメモリは解放されない。
132  */
133 void load_music_prefs()
134 {
135     CfgReader reader(ANGBAND_DIR_XTRA_MUSIC, { "music_debug.cfg", "music.cfg" });
136
137     char device_type[256];
138     GetPrivateProfileStringA("Device", "type", "MPEGVideo", device_type, _countof(device_type), reader.get_cfg_path());
139     mci_device_type = to_wchar(device_type).wc_str();
140
141     // clang-format off
142     music_cfg_data = reader.read_sections({
143         { "Basic", TERM_XTRA_MUSIC_BASIC, basic_key_at },
144         { "Dungeon", TERM_XTRA_MUSIC_DUNGEON, dungeon_key_at },
145         { "Quest", TERM_XTRA_MUSIC_QUEST, quest_key_at },
146         { "Town", TERM_XTRA_MUSIC_TOWN, town_key_at },
147         { "Monster", TERM_XTRA_MUSIC_MONSTER, monster_key_at, &has_monster_music }
148         });
149     // clang-format on
150
151     if (!has_monster_music) {
152         int type = TERM_XTRA_MUSIC_BASIC;
153         for (int val = MUSIC_BASIC_UNIQUE; val <= MUSIC_BASIC_HIGHER_LEVEL_MONSTER; val++) {
154             if (music_cfg_data->has_key(type, val)) {
155                 has_monster_music = true;
156                 break;
157             }
158         }
159     }
160 }
161
162 /*
163  * Stop a music
164  */
165 errr stop_music(void)
166 {
167     mciSendCommandW(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
168     mciSendCommandW(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
169     current_music_type = TERM_XTRA_MUSIC_MUTE;
170     current_music_id = 0;
171     strcpy(current_music_path, "\0");
172     return 0;
173 }
174
175 /*
176  * Play a music
177  */
178 errr play_music(int type, int val)
179 {
180     if (type == TERM_XTRA_MUSIC_MUTE) {
181         return stop_music();
182     }
183
184     if (current_music_type == type && current_music_id == val) {
185         return 0;
186     } // now playing
187
188     concptr filename = music_cfg_data->get_rand(type, val);
189     if (!filename) {
190         return 1;
191     } // no setting
192
193     char buf[MAIN_WIN_MAX_PATH];
194     path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, filename);
195
196     if (current_music_type != TERM_XTRA_MUSIC_MUTE) {
197         if (0 == strcmp(current_music_path, buf)) {
198             return 0;
199         }
200     } // now playing same file
201
202     current_music_type = type;
203     current_music_id = val;
204     strcpy(current_music_path, buf);
205
206     to_wchar path(buf);
207     mci_open_parms.lpstrDeviceType = mci_device_type.data();
208     mci_open_parms.lpstrElementName = path.wc_str();
209     mciSendCommandW(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
210     mciSendCommandW(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
211     mciSendCommandW(mci_open_parms.wDeviceID, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_NOTIFY, (DWORD)&mci_open_parms);
212     // Send MCI_PLAY in the notification event once MCI_OPEN is completed
213     return 0;
214 }
215
216 /*
217  * Pause a music
218  */
219 void pause_music(void)
220 {
221     mciSendCommandW(mci_open_parms.wDeviceID, MCI_PAUSE, MCI_WAIT, 0);
222 }
223
224 /*
225  * Resume a music
226  */
227 void resume_music(void)
228 {
229     mciSendCommandW(mci_open_parms.wDeviceID, MCI_RESUME, MCI_WAIT, 0);
230 }
231
232 /*
233  * Play a music matches a situation
234  */
235 errr play_music_scene(int val)
236 {
237     // リストの先頭から順に再生を試み、再生できたら抜ける
238     auto &list = get_scene_type_list(val);
239     const errr err_sucsess = 0;
240     for (auto &item : list) {
241         if (play_music(item.type, item.val) == err_sucsess) {
242             break;
243         }
244     }
245
246     return 0;
247 }
248
249 void set_music_volume(int volume)
250 {
251     if (current_music_type == TERM_XTRA_MUSIC_MUTE) {
252         return;
253     }
254
255     MCI_DGV_SETAUDIO_PARMSW mci_vol{};
256     mci_vol.dwItem = MCI_DGV_SETAUDIO_VOLUME;
257     mci_vol.dwValue = volume;
258     mciSendCommandW(
259         mci_open_parms.wDeviceID,
260         MCI_SETAUDIO,
261         MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE,
262         (DWORD)&mci_vol);
263 }
264
265 /*
266  * Notify event
267  */
268 void on_mci_notify(WPARAM wFlags, LONG lDevID, int volume)
269 {
270     if (wFlags == MCI_NOTIFY_SUCCESSFUL) {
271         // play a music (repeat)
272         set_music_volume(volume);
273         mciSendCommandW(lDevID, MCI_SEEK, MCI_SEEK_TO_START | MCI_WAIT, 0);
274         mciSendCommandW(lDevID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mci_play_parms);
275     }
276 }
277
278 }