OSDN Git Service

Merge pull request #765 from sikabane-works/release/3.0.0Alpha17
[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/scene-table.h"
15 #include "main/sound-of-music.h"
16 #include "monster-race/monster-race.h"
17 #include "term/z-term.h"
18 #include "util/angband-files.h"
19 #include "world/world.h"
20
21 static int current_music_type = TERM_XTRA_MUSIC_MUTE;
22 static int current_music_id = 0;
23 // current filename being played
24 static char current_music_path[MAIN_WIN_MAX_PATH];
25
26 /*
27  * Directory name
28  */
29 concptr ANGBAND_DIR_XTRA_MUSIC;
30
31 /*
32  * "music.cfg" data
33  */
34 CfgData *music_cfg_data;
35
36 namespace main_win_music {
37
38 /*!
39  * @brief action-valに対応する[Basic]セクションのキー名を取得する
40  * @param index "term_xtra()"の第2引数action-valに対応する値
41  * @param buf 使用しない
42  * @return 対応するキー名を返す
43  */
44 static concptr basic_key_at(int index, char *buf)
45 {
46     (void)buf;
47
48     if (index >= MUSIC_BASIC_MAX)
49         return NULL;
50
51     return angband_music_basic_name[index];
52 }
53
54 static inline DUNGEON_IDX get_dungeon_count()
55 {
56     return current_world_ptr->max_d_idx;
57 }
58
59 /*!
60  * @brief action-valに対応する[Dungeon]セクションのキー名を取得する
61  * @param index "term_xtra()"の第2引数action-valに対応する値
62  * @param buf バッファ
63  * @return 対応するキー名を返す
64  */
65 static concptr dungeon_key_at(int index, char *buf)
66 {
67     if (index >= get_dungeon_count())
68         return NULL;
69
70     sprintf(buf, "dungeon%03d", index);
71     return buf;
72 }
73
74 static inline QUEST_IDX get_quest_count()
75 {
76     return max_q_idx;
77 }
78
79 /*!
80  * @brief action-valに対応する[Quest]セクションのキー名を取得する
81  * @param index "term_xtra()"の第2引数action-valに対応する値
82  * @param buf バッファ
83  * @return 対応するキー名を返す
84  */
85 static concptr quest_key_at(int index, char *buf)
86 {
87     if (index >= get_quest_count())
88         return NULL;
89
90     sprintf(buf, "quest%03d", index);
91     return buf;
92 }
93
94 static inline TOWN_IDX get_town_count()
95 {
96     return max_towns;
97 }
98
99 /*!
100  * @brief action-valに対応する[Town]セクションのキー名を取得する
101  * @param index "term_xtra()"の第2引数action-valに対応する値
102  * @param buf バッファ
103  * @return 対応するキー名を返す
104  */
105 static concptr town_key_at(int index, char *buf)
106 {
107     if (index >= get_town_count())
108         return NULL;
109
110     sprintf(buf, "town%03d", index);
111     return buf;
112 }
113
114 static inline MONRACE_IDX get_monster_count()
115 {
116     return max_r_idx;
117 }
118
119 /*!
120  * @brief action-valに対応する[Monster]セクションのキー名を取得する
121  * @param index "term_xtra()"の第2引数action-valに対応する値
122  * @param buf バッファ
123  * @return 対応するキー名を返す
124  */
125 static concptr monster_key_at(int index, char *buf)
126 {
127     if (index >= get_monster_count())
128         return NULL;
129
130     sprintf(buf, "monster%04d", index);
131     return buf;
132 }
133
134 /*!
135  * @brief BGMの設定を読み込む。
136  * @details
137  * "music_debug.cfg"ファイルを優先して読み込む。無ければ"music.cfg"ファイルを読み込む。
138  * この処理は複数回実行されることを想定していない。複数回実行した場合には前回読み込まれた設定のメモリは解放されない。
139  */
140 void load_music_prefs()
141 {
142     CfgReader reader(ANGBAND_DIR_XTRA_MUSIC, { "music_debug.cfg", "music.cfg" });
143
144     GetPrivateProfileString("Device", "type", "MPEGVideo", mci_device_type, _countof(mci_device_type), reader.get_cfg_path());
145
146     // clang-format off
147     music_cfg_data = reader.read_sections({
148         { "Basic", TERM_XTRA_MUSIC_BASIC, basic_key_at },
149         { "Dungeon", TERM_XTRA_MUSIC_DUNGEON, dungeon_key_at },
150         { "Quest", TERM_XTRA_MUSIC_QUEST, quest_key_at },
151         { "Town", TERM_XTRA_MUSIC_TOWN, town_key_at },
152         { "Monster", TERM_XTRA_MUSIC_MONSTER, monster_key_at, &has_monster_music }
153         });
154     // clang-format on
155
156     if (!has_monster_music) {
157         int type = TERM_XTRA_MUSIC_BASIC;
158         for (int val = MUSIC_BASIC_UNIQUE; val <= MUSIC_BASIC_HIGHER_LEVEL_MONSTER; val++) {
159             if (music_cfg_data->has_key(type, val)) {
160                 has_monster_music = true;
161                 break;
162             }
163         }
164     }
165 }
166
167 /*
168  * Stop a music
169  */
170 errr stop_music(void)
171 {
172     mciSendCommand(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
173     mciSendCommand(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
174     current_music_type = TERM_XTRA_MUSIC_MUTE;
175     current_music_id = 0;
176     strcpy(current_music_path, "\0");
177     return 0;
178 }
179
180 /*
181  * Play a music
182  */
183 errr play_music(int type, int val)
184 {
185     if (type == TERM_XTRA_MUSIC_MUTE)
186         return stop_music();
187
188     if (current_music_type == type && current_music_id == val)
189         return 0; // now playing
190
191     concptr filename = music_cfg_data->get_rand(type, val);
192     if (!filename)
193         return 1; // no setting
194
195     char buf[MAIN_WIN_MAX_PATH];
196     path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, filename);
197
198     if (current_music_type != TERM_XTRA_MUSIC_MUTE)
199         if (0 == strcmp(current_music_path, buf))
200             return 0; // now playing same file
201
202     current_music_type = type;
203     current_music_id = val;
204     strcpy(current_music_path, buf);
205
206     mci_open_parms.lpstrDeviceType = mci_device_type;
207     mci_open_parms.lpstrElementName = buf;
208     mciSendCommand(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
209     mciSendCommand(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
210     mciSendCommand(mci_open_parms.wDeviceID, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_NOTIFY, (DWORD)&mci_open_parms);
211     // Send MCI_PLAY in the notification event once MCI_OPEN is completed
212     return 0;
213 }
214
215 /*
216  * Play a music matches a situation
217  */
218 errr play_music_scene(int val)
219 {
220     // リストの先頭から順に再生を試み、再生できたら抜ける
221     auto list = get_scene_type_list(val);
222     const errr err_sucsess = 0;
223     for (auto &item : list) {
224         if (play_music(item.type, item.val) == err_sucsess) {
225             break;
226         }
227     }
228
229     return 0;
230 }
231
232 /*
233  * Notify event
234  */
235 void on_mci_notify(WPARAM wFlags, LONG lDevID)
236 {
237     UNREFERENCED_PARAMETER(lDevID);
238
239     if (wFlags == MCI_NOTIFY_SUCCESSFUL) {
240         // play a music (repeat)
241         mciSendCommand(mci_open_parms.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START | MCI_WAIT, 0);
242         mciSendCommand(mci_open_parms.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mci_play_parms);
243     }
244 }
245
246 }