2 * @file main-win-music.cpp
3 * @brief Windows版固有実装(BGM)
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-definition.h"
20 #include "term/z-term.h"
21 #include "util/angband-files.h"
22 #include "world/world.h"
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];
36 concptr ANGBAND_DIR_XTRA_MUSIC;
41 CfgData *music_cfg_data;
43 namespace main_win_music {
46 * @brief action-valに対応する[Basic]セクションのキー名を取得する
47 * @param index "term_xtra()"の第2引数action-valに対応する値
51 static concptr basic_key_at(int index, char *buf)
55 if (index >= MUSIC_BASIC_MAX) {
59 return angband_music_basic_name[index];
63 * @brief action-valに対応する[Dungeon]セクションのキー名を取得する
64 * @param index "term_xtra()"の第2引数action-valに対応する値
68 static concptr dungeon_key_at(int index, char *buf)
70 if (index >= static_cast<int>(dungeons_info.size())) {
74 sprintf(buf, "dungeon%03d", index);
79 * @brief action-valに対応する[Quest]セクションのキー名を取得する
80 * @param index "term_xtra()"の第2引数action-valに対応する値
84 static concptr quest_key_at(int index, char *buf)
86 const auto &quest_list = QuestList::get_instance();
87 if (index > enum2i(quest_list.rbegin()->first)) {
91 sprintf(buf, "quest%03d", index);
96 * @brief action-valに対応する[Town]セクションのキー名を取得する
97 * @param index "term_xtra()"の第2引数action-valに対応する値
101 static concptr town_key_at(int index, char *buf)
103 if (index >= static_cast<int>(max_towns)) {
107 sprintf(buf, "town%03d", index);
112 * @brief action-valに対応する[Monster]セクションのキー名を取得する
113 * @param index "term_xtra()"の第2引数action-valに対応する値
117 static concptr monster_key_at(int index, char *buf)
119 if (index >= static_cast<int>(monraces_info.size())) {
123 sprintf(buf, "monster%04d", index);
128 * @brief BGMの設定を読み込む。
130 * "music_debug.cfg"ファイルを優先して読み込む。無ければ"music.cfg"ファイルを読み込む。
131 * この処理は複数回実行されることを想定していない。複数回実行した場合には前回読み込まれた設定のメモリは解放されない。
133 void load_music_prefs()
135 CfgReader reader(ANGBAND_DIR_XTRA_MUSIC, { "music_debug.cfg", "music.cfg" });
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();
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 }
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;
165 errr stop_music(void)
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");
178 errr play_music(int type, int val)
180 if (type == TERM_XTRA_MUSIC_MUTE) {
184 if (current_music_type == type && current_music_id == val) {
188 concptr filename = music_cfg_data->get_rand(type, val);
193 char buf[MAIN_WIN_MAX_PATH];
194 path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, filename);
196 if (current_music_type != TERM_XTRA_MUSIC_MUTE) {
197 if (0 == strcmp(current_music_path, buf)) {
200 } // now playing same file
202 current_music_type = type;
203 current_music_id = val;
204 strcpy(current_music_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
219 void pause_music(void)
221 mciSendCommandW(mci_open_parms.wDeviceID, MCI_PAUSE, MCI_WAIT, 0);
227 void resume_music(void)
229 mciSendCommandW(mci_open_parms.wDeviceID, MCI_RESUME, MCI_WAIT, 0);
233 * Play a music matches a situation
235 errr play_music_scene(int val)
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) {
249 void set_music_volume(int volume)
251 if (current_music_type == TERM_XTRA_MUSIC_MUTE) {
255 MCI_DGV_SETAUDIO_PARMSW mci_vol{};
256 mci_vol.dwItem = MCI_DGV_SETAUDIO_VOLUME;
257 mci_vol.dwValue = volume;
259 mci_open_parms.wDeviceID,
261 MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE,
268 void on_mci_notify(WPARAM wFlags, LONG lDevID, int volume)
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);