initial commit, lordsawar source, slightly modified
[lordsawar] / src / sound.cpp
1 // Copyright (C) 2006 Ulf Lorenz
2 // Copyright (C) 2006 Andrea Paternesi
3 // Copyright (C) 2006, 2007, 2008 Ben Asselstine
4 //
5 //  This program is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation; either version 3 of the License, or
8 //  (at your option) any later version.
9 //
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 Library General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
18 //  02110-1301, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <iostream>
25 #include <sigc++/functors/mem_fun.h>
26 #include "sound.h"
27 #include "File.h"
28 #include "Configuration.h"
29 #include "defs.h"
30
31 #define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
32 //#define debug(x)
33
34
35 Sound* Sound::s_instance = 0;
36
37 #ifdef FL_SOUND
38 // used for fading in a music piece after another faded out
39 Mix_Music* _my_toplay = 0;
40
41 // used for removing a music piece (we only need to have 1 removed at a time
42 Mix_Music* _my_toremove = 0;
43
44 // how often the music piece is played
45 int loopcount=1;
46
47 void _startNext()
48 {
49     if (_my_toplay != 0)
50     {
51         Mix_FadeInMusic(_my_toplay, loopcount, 1500);
52         _my_toplay = 0;
53     }
54
55     if (_my_toremove != 0)
56     {
57         Mix_FreeMusic(_my_toremove);
58         _my_toremove = 0;
59     }
60
61     Sound::getInstance()->nextPiece();
62 }
63 #endif
64
65 Sound* Sound::getInstance()
66 {
67     if (s_instance == 0)
68         s_instance = new Sound();
69
70     return s_instance;
71 }
72
73 void Sound::deleteInstance()
74 {
75     if (s_instance == 0)
76         return;
77
78     delete s_instance;
79     s_instance = 0;
80 }
81
82 Sound::Sound()
83     :d_broken(false), d_background(false), d_block(false)
84 {
85     debug("Sound constructor")
86 #ifdef FL_SOUND
87     d_music = 0;
88
89     if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
90     {
91         std::cerr << "Couldn't initialize SDL-mixer\n";
92         std::cerr <<Mix_GetError() <<std::endl;
93         d_broken = true;
94         return;
95     }
96     
97     XML_Helper helper(File::getMusicFile("music.xml"), std::ios::in, false);
98     helper.registerTag("piece", sigc::mem_fun(this, &Sound::loadMusic));
99
100     if (!helper.parse())
101     {
102         std::cerr<< "Error loading music descriptions; disable music\n";
103         d_broken = true;
104         Mix_CloseAudio();
105         return;
106     }
107
108     Mix_HookMusicFinished(_startNext);
109 #endif
110     debug("Music list contains " <<d_musicMap.size() <<" entries.")
111     debug("background list has " <<d_bgMap.size() <<" entries.")
112 }
113
114 Sound::~Sound()
115 {
116     debug("Sound destructor")
117 #ifdef FL_SOUND
118     haltMusic(false);
119     disableBackground(false);
120     
121     // remove all music pieces
122     std::map<std::string, MusicItem*>::iterator it;
123     for (it = d_musicMap.begin(); it != d_musicMap.end(); it++)
124         delete (*it).second;
125             
126     if (d_music)
127       {
128         Mix_FreeMusic(d_music);
129         d_music = NULL;
130       }
131     
132     //if (!d_broken)
133         //Mix_CloseAudio();
134 #endif
135 }
136
137 bool Sound::setMusic(bool enable, int volume)
138 {
139     if (volume < 0 || volume > 128)
140         return false;
141
142     Configuration::s_musicenable = enable;
143     Configuration::s_musicvolume = volume;
144 #ifdef FL_SOUND
145     Mix_VolumeMusic(volume);
146 #endif
147
148     return true;
149 }
150
151 bool Sound::isMusicEnabled()
152 {
153     return Configuration::s_musicenable;
154 }
155
156 int Sound::getMusicVolume()
157 {
158     return Configuration::s_musicvolume;
159 }
160
161 bool Sound::playMusic(std::string piece, int nloops, bool fade)
162 {
163     debug("playing Music")
164 #ifdef FL_SOUND
165     if (d_broken || !Configuration::s_musicenable)
166         return true;
167
168     // first, load the music piece
169     if (d_musicMap[piece] == 0)
170         return false;
171
172     if (Mix_PlayingMusic() || Mix_FadingMusic())
173         haltMusic(false);
174
175     Mix_Music* music = Mix_LoadMUS(File::getMusicFile(d_musicMap[piece]->file).c_str());
176     if (!music)
177     {
178         //try to load an alternative
179         std::string alias = d_musicMap[piece]->alias;
180         music = Mix_LoadMUS(File::getMusicFile(d_musicMap[alias]->file).c_str());
181         if (!music)
182             return false;
183     }
184
185     // now start to block playing music (well, a mutex/synchronization 
186     // would propably be a good idea here)
187     d_block = true;
188
189     // then end other music pieces and/or start ours.
190     if (Mix_PlayingMusic() || Mix_FadingMusic())
191     {
192         _my_toremove = d_music;
193         _my_toplay = music;
194         d_music = music;
195         loopcount = nloops;
196
197         if (fade)
198             Mix_FadeOutMusic(1500);
199         else
200             Mix_HaltMusic();
201         return true;
202     }
203
204     // no other music playing => start ours.
205     Mix_FadeInMusic(music, nloops, 1500);
206 #endif
207     return true;
208 }
209
210 bool Sound::haltMusic(bool fade)
211 {
212     debug("stopping music")
213 #ifdef FL_SOUND
214     d_block = false;
215
216     if (Mix_PlayingMusic() || Mix_FadingMusic())
217     {
218         _my_toremove = d_music;
219         d_music = 0;
220
221         if (fade)
222             Mix_FadeOutMusic(1500);
223         else
224             Mix_HaltMusic();
225         return true;
226     }
227
228     // no music is playing => call nextPiece manually
229     nextPiece();
230 #endif
231     return true;
232 }
233
234 void Sound::enableBackground()
235 {
236     debug("enabling background music")
237     d_background = true;
238     nextPiece();
239 }
240
241 void Sound::disableBackground(bool fade)
242 {
243     debug("disabling background music")
244     d_background = false;
245
246     if (d_block)
247         return;
248
249 #ifdef FL_SOUND
250     if (Mix_PlayingMusic() || Mix_FadingMusic())
251     {
252         _my_toremove = d_music;
253         d_music = 0;
254
255         if (fade)
256             Mix_FadeOutMusic(1500);
257         else
258             Mix_HaltMusic();
259     }
260 #endif
261 }
262
263 void Sound::nextPiece()
264 {
265     debug("Sound::nextPiece")
266     if (d_block || !d_background || !isMusicEnabled())
267         return;
268
269 #ifdef FL_SOUND
270     if (Mix_PlayingMusic() || Mix_FadingMusic())
271         return;
272
273     // remove the old music
274     if (d_music)
275       {
276         Mix_FreeMusic(d_music);
277         d_music = NULL;
278       }
279
280     // select a random music piece from the list of background pieces
281     while (!d_music && !d_bgMap.empty())
282     {
283         int select = rand() % d_bgMap.size();
284         d_music = Mix_LoadMUS(File::getMusicFile(d_musicMap[d_bgMap[select]]->file).c_str());
285         if (!d_music)
286         {
287             // file does not exist. Remove this entry from the list
288             d_bgMap[select] = d_bgMap[0];
289             d_bgMap.erase(d_bgMap.begin());
290         }
291     }
292
293     if (!d_music)
294         // well, no background music without sound files...
295         return;
296
297     Mix_FadeInMusic(d_music, 1, 1500);
298 #endif
299 }
300
301 bool Sound::loadMusic(std::string tag, XML_Helper* helper)
302 {
303     if (tag != "piece")
304     {
305         std::cerr <<"Loading music: Wrong tag name\n";
306         return false;
307     }
308
309     std::string tagname;
310     MusicItem* item = new MusicItem();
311     
312     bool retval = true;
313     retval &= helper->getData(tagname, "name");
314     retval &= helper->getData(item->file, "filename");
315     retval &= helper->getData(item->background, "background");
316     retval &= helper->getData(item->alias, "alias");
317     
318     if (retval)
319     {
320         d_musicMap[tagname] = item;
321         if (item->background)
322             d_bgMap.push_back(tagname);
323     }
324     
325     return retval;
326 }