Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
MusicDirector.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: MusicDirector.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Music Director class to manage selection, setup, and playback
13  of background music tracks for both menu and game modes
14 */
15 
16 
17 #include "MemDebug.h"
18 #include "MusicDirector.h"
19 #include "MusicTrack.h"
20 
21 #include "Random.h"
22 #include "DataLoader.h"
23 #include "FormatUtil.h"
24 #include "Sound.h"
25 
26 static MusicDirector* music_director = 0;
27 
28 // +-------------------------------------------------------------------+
29 
31 : mode(0), transition(0), track(0), next_track(0), no_music(true),
32 hproc(0)
33 {
34  music_director = this;
35 
36  ScanTracks();
37 
38  if (!no_music)
39  StartThread();
40 }
41 
43 {
44  StopThread();
45 
46  delete track;
47  delete next_track;
48 
61 
62  if (this == music_director)
63  music_director = 0;
64 }
65 
66 // +--------------------------------------------------------------------+
67 
68 void
70 {
71  if (music_director) delete music_director;
72  music_director = new(__FILE__,__LINE__) MusicDirector();
73 }
74 
75 void
77 {
78  delete music_director;
79  music_director = 0;
80 }
81 
84 {
85  return music_director;
86 }
87 
88 // +-------------------------------------------------------------------+
89 
90 void
92 {
93  if (no_music) return;
94 
96 
97  if (next_track && !track) {
98  track = next_track;
99  next_track = 0;
100  }
101 
102  if (track) {
103  if (track->IsDone()) {
104  if (mode != NONE && mode != SHUTDOWN && next_track == 0) {
106  }
107 
108  delete track;
109  track = next_track;
110  next_track = 0;
111  }
112 
113  else if (track->IsLooped()) {
114  if (mode != NONE && mode != SHUTDOWN && next_track == 0) {
116  }
117 
118  track->FadeOut();
119  track->ExecFrame();
120  }
121 
122  else {
123  track->ExecFrame();
124  }
125  }
126 
127  if (next_track) {
128  if (next_track->IsDone()) {
129  delete next_track;
130  next_track = 0;
131  }
132 
133  else if (next_track->IsLooped()) {
134  next_track->FadeOut();
136  }
137 
138  else {
140  }
141  }
142 }
143 
144 // +-------------------------------------------------------------------+
145 
146 void
148 {
149  DataLoader* loader = DataLoader::GetLoader();
150 
151  bool old_file_system = loader->IsFileSystemEnabled();
152  loader->UseFileSystem(true);
153  loader->SetDataPath("Music/");
154 
155  List<Text> files;
156  loader->ListFiles("*.ogg", files, true);
157 
158  if (files.size() == 0) {
159  loader->UseFileSystem(old_file_system);
160  no_music = true;
161  return;
162  }
163 
164  no_music = false;
165 
166  ListIter<Text> iter = files;
167  while (++iter) {
168  Text* name = iter.value();
169  Text* file = new(__FILE__,__LINE__) Text("Music/");
170 
171  name->setSensitive(false);
172  file->append(*name);
173 
174  if (name->indexOf("Menu") == 0) {
175  menu_tracks.append(file);
176  }
177 
178  else if (name->indexOf("Intro") == 0) {
179  intro_tracks.append(file);
180  }
181 
182  else if (name->indexOf("Brief") == 0) {
183  brief_tracks.append(file);
184  }
185 
186  else if (name->indexOf("Debrief") == 0) {
187  debrief_tracks.append(file);
188  }
189 
190  else if (name->indexOf("Promot") == 0) {
191  promote_tracks.append(file);
192  }
193 
194  else if (name->indexOf("Flight") == 0) {
195  flight_tracks.append(file);
196  }
197 
198  else if (name->indexOf("Combat") == 0) {
199  combat_tracks.append(file);
200  }
201 
202  else if (name->indexOf("Launch") == 0) {
203  launch_tracks.append(file);
204  }
205 
206  else if (name->indexOf("Recovery") == 0) {
207  recovery_tracks.append(file);
208  }
209 
210  else if (name->indexOf("Victory") == 0) {
211  victory_tracks.append(file);
212  }
213 
214  else if (name->indexOf("Defeat") == 0) {
215  defeat_tracks.append(file);
216  }
217 
218  else if (name->indexOf("Credit") == 0) {
219  credit_tracks.append(file);
220  }
221 
222  else {
223  menu_tracks.append(file);
224  }
225 
226  delete iter.removeItem();
227  }
228 
229  loader->UseFileSystem(old_file_system);
230 
231  menu_tracks.sort();
232  intro_tracks.sort();
233  brief_tracks.sort();
243 }
244 
245 // +-------------------------------------------------------------------+
246 
247 const char*
249 {
250  switch (mode) {
251  case NONE: return "NONE";
252  case MENU: return "MENU";
253  case INTRO: return "INTRO";
254  case BRIEFING: return "BRIEFING";
255  case DEBRIEFING: return "DEBRIEFING";
256  case PROMOTION: return "PROMOTION";
257  case FLIGHT: return "FLIGHT";
258  case COMBAT: return "COMBAT";
259  case LAUNCH: return "LAUNCH";
260  case RECOVERY: return "RECOVERY";
261  case VICTORY: return "VICTORY";
262  case DEFEAT: return "DEFEAT";
263  case CREDITS: return "CREDITS";
264  case SHUTDOWN: return "SHUTDOWN";
265  }
266 
267  return "UNKNOWN?";
268 }
269 
270 // +-------------------------------------------------------------------+
271 
272 void
274 {
275  if (!music_director || music_director->no_music) return;
276 
277  AutoThreadSync a(music_director->sync);
278 
279  // stay in intro mode until it is complete:
280  if (mode == MENU && (music_director->GetMode() == NONE ||
281  music_director->GetMode() == INTRO))
282  mode = INTRO;
283 
284  mode = music_director->CheckMode(mode);
285 
286  if (mode != music_director->mode) {
287  ::Print("MusicDirector::SetMode() old: %s new: %s\n",
288  GetModeName(music_director->mode),
289  GetModeName(mode));
290 
291  music_director->mode = mode;
292 
293  MusicTrack* t = music_director->track;
294  if (t && t->GetState() && !t->IsDone()) {
295  if (mode == NONE || mode == SHUTDOWN)
296  t->SetFadeTime(0.5);
297 
298  t->FadeOut();
299  }
300 
301  t = music_director->next_track;
302  if (t && t->GetState() && !t->IsDone()) {
303  if (mode == NONE || mode == SHUTDOWN)
304  t->SetFadeTime(0.5);
305  t->FadeOut();
306 
307  delete music_director->track;
308  music_director->track = t;
309  music_director->next_track = 0;
310  }
311 
312  music_director->ShuffleTracks();
313  music_director->GetNextTrack(0);
314 
315  if (music_director->next_track)
316  music_director->next_track->FadeIn();
317  }
318 }
319 
320 int
322 {
323  if (req_mode == RECOVERY && recovery_tracks.size() == 0)
324  req_mode = LAUNCH;
325 
326  if (req_mode == LAUNCH && launch_tracks.size() == 0)
327  req_mode = FLIGHT;
328 
329  if (req_mode == COMBAT && combat_tracks.size() == 0)
330  req_mode = FLIGHT;
331 
332  if (req_mode == FLIGHT && flight_tracks.size() == 0)
333  req_mode = NONE;
334 
335  if (req_mode == PROMOTION && promote_tracks.size() == 0)
336  req_mode = VICTORY;
337 
338  if (req_mode == DEBRIEFING && debrief_tracks.size() == 0)
339  req_mode = BRIEFING;
340 
341  if (req_mode == BRIEFING && brief_tracks.size() == 0)
342  req_mode = MENU;
343 
344  if (req_mode == INTRO && intro_tracks.size() == 0)
345  req_mode = MENU;
346 
347  if (req_mode == VICTORY && victory_tracks.size() == 0)
348  req_mode = MENU;
349 
350  if (req_mode == DEFEAT && defeat_tracks.size() == 0)
351  req_mode = MENU;
352 
353  if (req_mode == CREDITS && credit_tracks.size() == 0)
354  req_mode = MENU;
355 
356  if (req_mode == MENU && menu_tracks.size() == 0)
357  req_mode = NONE;
358 
359  return req_mode;
360 }
361 
362 // +-------------------------------------------------------------------+
363 
364 bool
366 {
367  if (music_director)
368  return music_director->no_music;
369 
370  return true;
371 }
372 
373 // +-------------------------------------------------------------------+
374 
375 void
377 {
378  List<Text>* tracks = 0;
379 
380  switch (mode) {
381  case MENU: tracks = &menu_tracks; break;
382  case INTRO: tracks = &intro_tracks; break;
383  case BRIEFING: tracks = &brief_tracks; break;
384  case DEBRIEFING: tracks = &debrief_tracks; break;
385  case PROMOTION: tracks = &promote_tracks; break;
386  case FLIGHT: tracks = &flight_tracks; break;
387  case COMBAT: tracks = &combat_tracks; break;
388  case LAUNCH: tracks = &launch_tracks; break;
389  case RECOVERY: tracks = &recovery_tracks; break;
390  case VICTORY: tracks = &victory_tracks; break;
391  case DEFEAT: tracks = &defeat_tracks; break;
392  case CREDITS: tracks = &credit_tracks; break;
393  default: tracks = 0; break;
394  }
395 
396  if (tracks && tracks->size()) {
397  if (next_track)
398  delete next_track;
399 
400  if (index < 0 || index >= tracks->size()) {
401  index = 0;
402 
403  if (mode == INTRO) {
404  mode = MENU;
405  ShuffleTracks();
406  tracks = &menu_tracks;
407 
408  ::Print("MusicDirector: INTRO mode complete, switching to MENU\n");
409 
410  if (!tracks || !tracks->size())
411  return;
412  }
413  }
414 
415  next_track = new(__FILE__,__LINE__) MusicTrack(*tracks->at(index), mode, index);
416  next_track->FadeIn();
417  }
418 
419  else if (next_track) {
420  next_track->FadeOut();
421  }
422 }
423 
424 // +-------------------------------------------------------------------+
425 
426 void
428 {
429  List<Text>* tracks = 0;
430 
431  switch (mode) {
432  case MENU: tracks = &menu_tracks; break;
433  case INTRO: tracks = &intro_tracks; break;
434  case BRIEFING: tracks = &brief_tracks; break;
435  case DEBRIEFING: tracks = &debrief_tracks; break;
436  case PROMOTION: tracks = &promote_tracks; break;
437  case FLIGHT: tracks = &flight_tracks; break;
438  case COMBAT: tracks = &combat_tracks; break;
439  case LAUNCH: tracks = &launch_tracks; break;
440  case RECOVERY: tracks = &recovery_tracks; break;
441  case VICTORY: tracks = &victory_tracks; break;
442  case DEFEAT: tracks = &defeat_tracks; break;
443  case CREDITS: tracks = &credit_tracks; break;
444  default: tracks = 0; break;
445  }
446 
447  if (tracks && tracks->size() > 1) {
448  tracks->sort();
449 
450  Text* t = tracks->at(0);
451 
452  if (!isdigit(*t[0]))
453  tracks->shuffle();
454  }
455 }
456 
457 // +--------------------------------------------------------------------+
458 
459 DWORD WINAPI MusicDirectorThreadProc(LPVOID link);
460 
461 void
463 {
464  if (hproc != 0) {
465  DWORD result = 0;
466  GetExitCodeThread(hproc, &result);
467 
468  if (result != STILL_ACTIVE) {
469  CloseHandle(hproc);
470  hproc = 0;
471  }
472  else {
473  return;
474  }
475  }
476 
477  if (hproc == 0) {
478  DWORD thread_id = 0;
479  hproc = CreateThread(0, 4096, MusicDirectorThreadProc, (LPVOID) this, 0, &thread_id);
480 
481  if (hproc == 0) {
482  static int report = 10;
483  if (report > 0) {
484  ::Print("WARNING: MusicDirector failed to create thread (err=%08x)\n", GetLastError());
485  report--;
486 
487  if (report == 0)
488  ::Print(" Further warnings of this type will be supressed.\n");
489  }
490  }
491  }
492 }
493 
494 void
496 {
497  if (hproc != 0) {
498  SetMode(SHUTDOWN);
499  WaitForSingleObject(hproc, 1500);
500  CloseHandle(hproc);
501  hproc = 0;
502  }
503 }
504 
505 DWORD WINAPI MusicDirectorThreadProc(LPVOID link)
506 {
507  MusicDirector* dir = (MusicDirector*) link;
508 
509  if (dir) {
510  while (dir->GetMode() != MusicDirector::SHUTDOWN) {
511  dir->ExecFrame();
512  Sleep(100);
513  }
514 
515  return 0;
516  }
517 
518  return (DWORD) E_POINTER;
519 }
520