2
* Copyright (C) 2002,2003,2004 Daniel Heck
3
* Copyright (C) 2007,2008,2009 Andreas Lochmann
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include "MusicManager.hh"
23
#include "SoundEngine.hh"
25
#include "lev/RatingManager.hh"
26
#include "lev/Index.hh"
29
#include "SDL_mixer.h"
34
using namespace enigma;
35
using namespace sound;
37
/* -------------------- Interface Functions -------------------- */
39
void sound::StartMenuMusic()
41
MusicManager::instance()->setMusicContext(MUSIC_MENU);
44
void sound::StartLevelLoadMusic()
46
MusicManager::instance()->setMusicContext(MUSIC_LEVEL_LOADING);
49
void sound::StartLevelMusic()
51
MusicManager::instance()->setMusicContext(MUSIC_GAME);
54
void sound::SetInGameMusicActive(bool active)
56
MusicManager::instance()->setInGameMusicActive(active);
59
void sound::MusicTick(double dtime)
61
if(!sound::IsMusicMute())
62
MusicManager::instance()->tick(dtime);
65
void sound::InitMusic()
67
MusicManager::instance()->init();
70
void sound::DefineMusicSingle(std::string title, std::string filename,
71
float affinity_intelligence, float affinity_dexterity, float affinity_patience,
72
float affinity_knowledge, float affinity_speed)
75
Log << "Warning: Tried to define music single '" << title
76
<< "' without file name. Skipped.\n";
79
MusicManager::instance()->defineMusicSingle(title, filename,
80
affinity_intelligence, affinity_dexterity, affinity_patience,
81
affinity_knowledge, affinity_speed);
84
/* -------------------- Sound option helpers -------------------- */
86
/*! These functions are used in OptionsMenu.cc for the MenuMusicButton. */
88
int sound::GetOptionMenuMusicCount()
90
return MusicManager::instance()->getMenuMusicQueueCount();
93
int sound::GetOptionMenuMusic()
95
std::string music_queue = app.state->getString("MenuMusicQueue");
96
int pos = MusicManager::instance()->getMusicQueueButtonPosition(music_queue);
101
void sound::SetOptionMenuMusic(int value)
103
std::string music_queue = MusicManager::instance()->getMusicQueueByPosition(value);
104
app.state->setProperty("MenuMusicQueue", music_queue);
105
MusicManager::instance()->setMenuMusicQueue(music_queue);
108
std::string sound::GetOptionMenuMusicText(int value)
110
return MusicManager::instance()->getMusicQueueByPosition(value);
113
/* -------------------- MusicManager -------------------- */
115
MusicManager *MusicManager::theSingleton = 0;
117
MusicManager* MusicManager::instance() {
118
if (theSingleton == 0) {
119
theSingleton = new MusicManager();
124
MusicManager::MusicManager()
125
: music_singles(), music_queues(), active_music_queue_title(""),
126
ingame_music_queue_title(""), menu_music_queue_title(""),
127
wait_length(-1), music_context(MUSIC_NONE), is_waiting(false),
128
ingame_music_active(false)
131
void MusicManager::tick(double dtime)
133
static double cumulated_dtime = 0;
134
if(!sound::IsMusicPlaying()) {
135
if(is_waiting && (dtime > 0.0)) {
136
// No music is playing, but we are actively counting seconds:
137
// This is a silent phase of determined length ("waiting").
138
cumulated_dtime += dtime;
139
if(cumulated_dtime > wait_length) {
142
music_queues[active_music_queue_title].onWaitEnded();
145
// Music has really ended or not even begun.
146
switch(getMusicContext()) {
150
if(ingame_music_active)
151
music_queues[active_music_queue_title].onMusicEnded(false);
154
if(active_music_queue_title != "")
155
music_queues[active_music_queue_title].onMusicEnded(false);
157
case MUSIC_LEVEL_LOADING:
158
// Fadeout ended while level is still loading.
159
// Just wait until load is complete.
162
Log << "Error: getMusicContext() returns an invalid type. Will ignore this.\n";
168
void MusicManager::setMusicContext(MusicContext new_context)
170
if(new_context == music_context)
171
return; // Nothing to do.
173
switch(new_context) {
175
//sound::FadeoutMusic();
177
//if(active_music_queue_title != "") {
178
// // onMusicEnded(true) means: force music, no waiting!
179
// music_queues[active_music_queue_title].onMusicEnded(true);
181
Log << "Switching to menu music.\n";
182
music_context = new_context;
183
setActiveMusicQueue(getMenuMusicQueueTitle());
185
case MUSIC_LEVEL_LOADING:
186
Log << "Switching to level load music.\n";
187
if(music_context == MUSIC_GAME) {
188
// Switching from one level to another.
189
music_context = new_context;
191
// Switching from menu to level, stop menu music.
192
music_queues[active_music_queue_title].calculate_level_points();
193
music_context = new_context;
194
setActiveMusicQueue(getInGameMusicQueueTitle());
198
Log << "Switching to level music.\n";
199
// TODO: Stop music if current music is not suitable.
200
music_queues[active_music_queue_title].calculate_level_points();
201
music_context = new_context;
202
//sound::FadeoutMusic();
203
setActiveMusicQueue(getInGameMusicQueueTitle());
204
// We do not force music here, but leave it to "tick" to handle.
207
Log << "Switching to no music.\n";
208
sound::FadeoutMusic(false);
213
void MusicManager::init()
215
// TODO: This is only temporary. Information will come
216
// from an xml file later.
219
music_singles["Esprit"] =
220
MusicSingle("Esprit", "music/menu/esprit.ogg");
221
music_singles["Pentagonal Dreams"] =
222
MusicSingle("Pentagonal Dreams", "music/menu/pentagonal_dreams.s3m");
225
music_singles["Across The Ice"] =
226
MusicSingle("Across The Ice", "music/game/across_the_ice.ogg", 0, 0.5, 0, 0, 0.5);
227
music_singles["In Space"] =
228
MusicSingle("In Space", "music/game/in_space.ogg", 0.3, 0, 0.4, 0, -0.3);
229
music_singles["Meditation"] =
230
MusicSingle("Meditation", "music/game/meditation.ogg", 0.4, 0.2, 0.4, 0, 0);
231
music_singles["On The Water"] =
232
MusicSingle("On The Water", "music/game/on_the_water.ogg", 0.2, 0.2, 0.2, 0.2, 0.4);
233
music_singles["Puzzle"] =
234
MusicSingle("Puzzle", "music/game/puzzle.ogg", 0.4, 0, 0.3, 0, 0.3);
235
music_singles["Skull Stones"] =
236
MusicSingle("Skull Stones", "music/game/skull_stones.ogg", 0, 0.5, -0.5, 0, 0);
237
music_singles["Swamp"] =
238
MusicSingle("Swamp", "music/game/swamp.ogg", 0.4, 0.1, -0.3, 0, 0.2);
240
// Menu and Level Music
241
music_singles["Enigma Rag"] =
242
MusicSingle("Enigma Rag", "music/menu/enigma_rag.ogg", 0.4, -0.2, 0, 0, 0.4);
245
music_queues["Default"] = MusicQueue("Default", MUSICQUEUE_NEXT, 0);
246
music_queues["Default"].appendSingle("Esprit", false);
247
music_queues["Default"].appendSingle("Esprit", false);
248
music_queues["Default"].appendSingleThenWait("Esprit", true, 8.0);
249
music_queues["Default"].appendSingleThenWait("Enigma Rag", true, 8.0);
250
music_queues["Default"].appendSingleThenWait("Pentagonal Dreams", true, 8.0);
252
music_queues["Esprit"] = MusicQueue("Esprit", MUSICQUEUE_NEXT, 1);
253
music_queues["Esprit"].appendSingle("Esprit", false);
255
music_queues["Enigma Rag"] = MusicQueue("Enigma Rag", MUSICQUEUE_NEXT, 2);
256
music_queues["Enigma Rag"].appendSingleThenWait("Enigma Rag", false, 8.0);
258
music_queues["Pentagonal Dreams"] = MusicQueue("Pentagonal Dreams", MUSICQUEUE_NEXT, 3);
259
music_queues["Pentagonal Dreams"].appendSingle("Pentagonal Dreams", true);
261
active_music_queue_title = app.state->getString("MenuMusicQueue");
262
// Set the default menu music queue, if saved queue doesn't exist.
263
if(music_queues.find(active_music_queue_title) == music_queues.end())
265
Log << "Warning: Did not find specified menu music queue '"
266
<< active_music_queue_title << "', will switch to default.\n";
267
active_music_queue_title = "Default";
268
app.state->setProperty("MenuMusicQueue", active_music_queue_title);
272
music_queues["In Game"] = MusicQueue("In Game", MUSICQUEUE_LEVEL, 4);
273
music_queues["In Game"].appendSingleThenWait("Across The Ice", true, 3.0);
274
music_queues["In Game"].appendSingleThenWait("In Space", true, 3.0);
275
music_queues["In Game"].appendSingleThenWait("Meditation", true, 3.0);
276
music_queues["In Game"].appendSingleThenWait("On The Water", true, 3.0);
277
music_queues["In Game"].appendSingleThenWait("Puzzle", true, 3.0);
278
music_queues["In Game"].appendSingleThenWait("Skull Stones", true, 3.0);
279
music_queues["In Game"].appendSingleThenWait("Swamp", true, 3.0);
280
music_queues["In Game"].appendSingleThenWait("Enigma Rag", true, 3.0);
282
menu_music_queue_title = app.state->getString("MenuMusicQueue");
283
// Set the default menu music queue, if saved queue doesn't exist.
284
if(music_queues.find(menu_music_queue_title) == music_queues.end())
286
Log << "Warning: Did not find specified menu music queue '"
287
<< menu_music_queue_title << "', will switch to default.\n";
288
menu_music_queue_title = "Default";
289
app.state->setProperty("MenuMusicQueue", menu_music_queue_title);
291
ingame_music_active = app.prefs->getBool("InGameMusic");
292
ingame_music_queue_title = "In Game";
294
// setMusicContext will set active_music_queue_title as well;
295
// after this, tick will start the music.
296
setMusicContext(MUSIC_MENU);
300
bool MusicManager::defineMusicSingle(std::string title, std::string filename,
301
float affinity_intelligence, float affinity_dexterity, float affinity_patience,
302
float affinity_knowledge, float affinity_speed)
304
music_singles[title] = MusicSingle(title, filename, affinity_intelligence,
305
affinity_dexterity, affinity_patience, affinity_knowledge, affinity_speed);
306
Log << "Added music single '" << title << "'.\n";
310
bool MusicManager::playMusicSingle(std::string title)
312
if(music_singles.find(title) == music_singles.end()) {
313
// Single doesn't exist! Returning "false" will make the queue
314
// mark its entry as "no_music", so this single will be ignored.
317
return music_singles[title].start();
320
void MusicManager::setWaiting(float seconds)
323
wait_length = seconds;
326
void MusicManager::stopWaiting()
332
bool MusicManager::setActiveMusicQueue(std::string music_queue_title)
334
if (music_queue_title == "") {
335
Log << "Warning: Tried to choose empty music queue title as active music queue.\n";
338
if (music_queue_title == getActiveMusicQueueTitle()) {
339
// Current queue is aready running.
342
// Stop current music and leave this queue.
343
sound::FadeoutMusic();
344
if (active_music_queue_title != "")
345
music_queues[active_music_queue_title].leave();
346
// Switch to new queue if possible.
348
active_music_queue_title = music_queue_title;
349
Log << "Switched to music queue '" << music_queue_title << "'.\n";
351
// We leave it to tick to start the new queue.
354
bool MusicManager::setMenuMusicQueue(std::string music_queue_title)
356
if (music_queue_title == "") {
357
Log << "Warning: Tried to choose empty music queue title as menu music queue.\n";
360
if (menu_music_queue_title == music_queue_title)
362
menu_music_queue_title = music_queue_title;
363
if(getMusicContext() == MUSIC_MENU)
364
return setActiveMusicQueue(music_queue_title);
368
bool MusicManager::setInGameMusicQueue(std::string music_queue_title)
370
if (music_queue_title == "") {
371
Log << "Warning: Tried to choose empty music queue title as in-game music queue.\n";
374
if (ingame_music_queue_title == music_queue_title)
376
ingame_music_queue_title = music_queue_title;
377
if(getMusicContext() == MUSIC_GAME)
378
return setActiveMusicQueue(music_queue_title);
382
std::string MusicManager::getMusicQueueByPosition(int button_position)
384
for (MusicQueueRepository::iterator i = music_queues.begin();
385
i != music_queues.end(); ++i)
386
if((*i).second.getButtonPosition() == button_position)
391
int MusicManager::getMenuMusicQueueCount()
394
for (MusicQueueRepository::iterator i = music_queues.begin();
395
i != music_queues.end(); ++i)
396
if((*i).second.getButtonPosition() != -1)
401
std::string MusicManager::getCurrentMusicTitle() {
402
if(sound::IsMusicPlaying() && (active_music_queue_title != ""))
403
return music_queues[active_music_queue_title].getCurrentMusicTitle();
408
void MusicManager::setInGameMusicActive(bool active)
410
ingame_music_active = active;
411
if ((getMusicContext() == MUSIC_GAME) && (!ingame_music_active))
413
sound::FadeoutMusic();
414
if (active_music_queue_title != "")
415
music_queues[active_music_queue_title].leave();
419
MusicSingle MusicManager::getMusicSingle(std::string title)
421
if(music_singles.find(title) != music_singles.end())
422
return music_singles[title];
425
Log << "Warning: Did not find music single " << title << ".\n";
426
return MusicSingle();
430
/* -------------------- Music Single -------------------- */
432
bool MusicSingle::start()
434
if(title == MusicManager::instance()->getCurrentMusicTitle())
436
if(sound::PlayMusic(filename))
441
float MusicSingle::affinity(float lev_int, float lev_dex, float lev_pat,
442
float lev_kno, float lev_spe)
444
return (affinity_intelligence * lev_int)
445
+ (affinity_dexterity * lev_dex)
446
+ (affinity_patience * lev_pat)
447
+ (affinity_knowledge * lev_kno)
448
+ (affinity_speed * lev_spe);
452
/* -------------------- Music Queue -------------------- */
454
std::string MusicQueue::getCurrentMusicTitle()
456
if(current_position_in_queue == -1)
459
return queue_entry[current_position_in_queue].title;
462
void MusicQueue::appendSingle(std::string title, bool fadeout_on_end)
464
MusicQueueEntry new_entry;
465
new_entry.type = MUSICQUEUE_SINGLE;
466
new_entry.title = title;
467
new_entry.fadeout_on_end = fadeout_on_end;
468
// TODO: fadeout_on_end currently doesn't work, as the music
469
// ends before it is noticed that the next entry of the
470
// queue should be read.
471
new_entry.wait_length = -1;
472
new_entry.no_music = false;
473
new_entry.points_transient = 0;
474
new_entry.points_level = 0;
475
new_entry.points_total = 0;
476
queue_entry.push_back(new_entry);
479
void MusicQueue::appendSingleThenWait(std::string title, bool fadeout_on_end, float seconds)
481
appendSingle(title, fadeout_on_end);
482
(queue_entry.back()).wait_length = seconds;
485
void MusicQueue::appendWait(float seconds)
487
MusicQueueEntry new_entry;
488
new_entry.type = MUSICQUEUE_WAIT;
489
new_entry.title = "";
490
new_entry.fadeout_on_end = false;
491
new_entry.wait_length = seconds;
492
new_entry.no_music = true;
493
new_entry.points_transient = 0;
494
new_entry.points_level = 0;
495
new_entry.points_total = 0;
496
queue_entry.push_back(new_entry);
499
bool MusicQueue::start()
501
current_position_in_queue = -1;
503
// We explicitly allow the queue to start silent.
504
// Otherwise use next(true), i.e. force_music.
505
// "next()" will also take care of empty queues.
508
void MusicQueue::calculate_level_points()
510
lev::RatingManager *theRatingMgr = lev::RatingManager::instance();
511
lev::Proxy *currentLevel = (lev::Index::getCurrentIndex())->getCurrent();
512
MusicManager *theMusicMgr = MusicManager::instance();
513
float currentInt = theRatingMgr->getIntelligence(currentLevel);
514
float currentDex = theRatingMgr->getDexterity(currentLevel);
515
float currentPat = theRatingMgr->getPatience(currentLevel);
516
float currentKno = theRatingMgr->getKnowledge(currentLevel);
517
float currentSpe = theRatingMgr->getSpeed(currentLevel);
518
// If the requested level does not exist, theRatingMgr will
519
// return 0 instead. We shift ratings such that 2.8 is seen as 0.
520
// Special handling for knowledge == 6 (set to 0).
521
//Log << currentInt << "/" << currentDex << "/" << currentPat << "/" <<
522
// currentKno << "/" << currentSpe << "\n";
523
currentInt = (currentInt == 0) ? 0 : (currentInt - 2.8);
524
currentDex = (currentDex == 0) ? 0 : (currentDex - 2.8);
525
currentPat = (currentPat == 0) ? 0 : (currentPat - 2.8);
526
currentKno = ((currentKno == 0) || (currentKno == 6)) ? 0 : (currentKno - 2.8);
527
currentSpe = (currentSpe == 0) ? 0 : (currentSpe - 2.8);
528
//Log << currentInt << "/" << currentDex << "/" << currentPat << "/" <<
529
// currentKno << "/" << currentSpe << "\n";
530
for(int j = 0; j < queue_entry.size(); j++)
531
if(queue_entry[j].type == MUSICQUEUE_SINGLE)
533
std::string title = queue_entry[j].title;
534
queue_entry[j].points_level =
535
(theMusicMgr->getMusicSingle(title)).affinity(
536
currentInt, currentDex, currentPat, currentKno, currentSpe);
537
//Log << title << ": " << queue_entry[j].points_level << "\n";
541
bool MusicQueue::next(bool force_music)
546
if(queue_entry.size() <= 0) {
547
Log << "Music queue is empty. No music will be played.\n";
551
if(current_position_in_queue != -1) {
552
// Another queue entry ended before this. Maybe we have to fade out.
553
MusicQueueEntry old_entry = queue_entry[current_position_in_queue];
554
if(old_entry.fadeout_on_end && sound::IsMusicPlaying())
555
sound::FadeoutMusic();
558
int new_position = (current_position_in_queue >= 0) ? current_position_in_queue : 0;
559
switch(shuffle_type) {
560
case MUSICQUEUE_NEXT:
561
// Jump to next position in queue.
562
new_position = (current_position_in_queue + 1) % queue_entry.size();
564
case MUSICQUEUE_RANDOM:
565
// Jump to a random position in queue other than the current.
566
if(queue_entry.size() < 2)
567
break; // Don't change current_position.
568
while (new_position == current_position_in_queue)
569
new_position = IntegerRand(0, queue_entry.size() - 1, false);
571
case MUSICQUEUE_LEVEL:
572
// Calculate the optimal single (or maybe silence phase).
573
if(current_position_in_queue >= 0)
574
queue_entry[current_position_in_queue].points_transient += -10;
575
for(int j = 0; j < queue_entry.size(); j++)
577
queue_entry[j].points_transient = queue_entry[j].points_transient / 2.0;
578
queue_entry[j].points_total = queue_entry[j].points_transient
579
+ queue_entry[j].points_level + IntegerRand(0, 300, false) / 1000.0;
580
Log << j << ": " << queue_entry[j].title << ": " << queue_entry[j].points_level
581
<< " + " << queue_entry[j].points_transient << " -> " <<
582
queue_entry[j].points_total << "\n";
584
float best_value = queue_entry[new_position].points_total;
585
for(int j = 0; j < queue_entry.size(); j++)
586
if (queue_entry[j].points_total >= best_value)
588
best_value = queue_entry[j].points_total;
591
Log << "Chose " << queue_entry[new_position].title << "\n";
594
current_position_in_queue = new_position;
596
MusicQueueEntry current_entry = queue_entry[current_position_in_queue];
597
bool success = false;
598
switch(current_entry.type) {
599
case MUSICQUEUE_SINGLE:
600
Log << "Play next in queue " << title << ": " << current_entry.title << ".\n";
601
success = MusicManager::instance()->playMusicSingle(current_entry.title);
602
queue_entry[current_position_in_queue].no_music = !success;
604
assureQueueHasMusic();
607
case MUSICQUEUE_WAIT:
609
return false; // this will cause "tick" to call "next" again.
610
Log << "Music queue starts waiting (" << current_entry.wait_length << "s).\n";
611
MusicManager::instance()->setWaiting(current_entry.wait_length);
615
Log << "Error: Current music queue entry is of invalid type. Will ignore this.\n";
620
bool MusicQueue::onMusicEnded(bool force_music)
622
if((current_position_in_queue >= 0) && (current_position_in_queue < queue_entry.size())) {
623
MusicQueueEntry current_entry = queue_entry[current_position_in_queue];
624
if((current_entry.wait_length > 0) && (!current_entry.no_music) && (!force_music))
626
Log << "Music queue starts conditional waiting (" << current_entry.wait_length << "s).\n";
627
MusicManager::instance()->setWaiting(current_entry.wait_length);
630
return next(force_music);
635
bool MusicQueue::onWaitEnded()
640
void MusicQueue::leave()
642
/*! We have the following choices to determine where to start the queue
643
next time. Remember that "next" will be called and auto-increase
644
current_position_in_queue.
645
Complete reset: current_position_in_queue = -1;
646
Start with n-th song: current_position_in_queue = n - 2;
647
Restart the current: decrease current_position_in_queue by one.
648
Start the next position in queue: don't change anything.
649
However, what we want is: Jump to the next real music, skip all kinds of
650
silent breaks. Cycle through until the end of the queue; if nothing is
651
found, always start the queue from the beginning. */
652
int total = queue_entry.size();
653
while( (queue_entry[(current_position_in_queue + 1) % total].type != MUSICQUEUE_SINGLE)
654
&& (current_position_in_queue < total)) {
655
current_position_in_queue++;
657
if(current_position_in_queue >= total)
658
current_position_in_queue = -1;
661
void MusicQueue::assureQueueHasMusic()
663
/*! This is called when a queue might have no music at all,
664
e.g. because all of its referenced files don't exist. Each time a
665
file is missing, its entry is changed to "no_music = true". Pure
666
waiting phases have "no_music = true" just as well. If all entries
667
are "no_music = true", then the queue is set to "defunc = true". */
668
bool queue_has_no_music = true;
669
for(std::vector<MusicQueueEntry>::const_iterator iter = queue_entry.begin();
670
iter != queue_entry.end(); ++iter)
671
queue_has_no_music = queue_has_no_music && (*iter).no_music;
672
if(queue_has_no_music) {
674
Log << "Warning: Queue " << title << " has no playable music! (Files missing?) Deactivated it.\n";