283fbba2ca1c30446390e7fb2a9a93bcf5e537d1
[supertux.git] / lib / audio / sound_manager.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Matthias Braun <matze@braunis.de
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include <cmath>
23 #include <cassert>
24
25 #include "audio/sound_manager.h"
26 #include "audio/musicref.h"
27 #include "app/globals.h"
28 #include "app/setup.h"
29 #include "special/moving_object.h"
30
31 using namespace SuperTux;
32
33 SoundManager* SoundManager::instance_ = 0;
34
35 SoundManager::SoundManager()
36   : current_music(0), m_music_enabled(true) , m_sound_enabled(true) , audio_device(true)
37 {
38 }
39
40 SoundManager::~SoundManager()
41 {
42   if(audio_device)
43     Mix_HaltMusic();
44
45 sounds.clear();
46 destroy_instance();
47 }
48
49 void
50 SoundManager::play_sound(Mix_Chunk* sound)
51 {
52   if(!audio_device || !m_sound_enabled)
53     return;
54
55   Mix_PlayChannel(-1, sound, 0);  
56 }
57
58 void
59 SoundManager::play_sound(Mix_Chunk* sound, const MovingObject* object, const Vector& pos)
60 {
61   // TODO keep track of the object later and move the sound along with the
62   // object.
63   play_sound(sound, object->get_pos(), pos);
64 }
65
66 void
67 SoundManager::play_sound(Mix_Chunk* sound, const Vector& pos, const Vector& pos2)
68 {
69   if(!audio_device || !m_sound_enabled)
70     return;
71
72   // TODO make sure this formula is good
73   float distance 
74     = pos2.x- pos.x;
75   int loud = int(255.0/float(screen->w*2) * fabsf(distance));
76   if(loud > 255)
77     return;
78
79   int chan = Mix_PlayChannel(-1, sound, 0);
80   if(chan < 0)
81     return;                                  
82   Mix_SetDistance(chan, loud);
83
84   // very bad way to do this...
85   if(distance > 100)
86     Mix_SetPanning(chan, 230, 24);
87   else if(distance < -100)
88     Mix_SetPanning(chan, 24, 230);
89 }
90
91 MusicRef
92 SoundManager::load_music(const std::string& file)
93 {
94   if(!audio_device)
95     return MusicRef(0);
96
97   if(!exists_music(file))
98     Termination::abort("Couldn't load musicfile ", file.c_str());
99
100   std::map<std::string, MusicResource>::iterator i = musics.find(file);
101   assert(i != musics.end());
102   return MusicRef(& (i->second));
103 }
104
105 bool
106 SoundManager::exists_music(const std::string& file)
107 {
108   if(!audio_device)
109     return true;
110   
111   // song already loaded?
112   std::map<std::string, MusicResource>::iterator i = musics.find(file);
113   if(i != musics.end()) {
114     return true;                                      
115   }
116   
117   Mix_Music* song = Mix_LoadMUS(file.c_str());
118   if(song == 0)
119     return false;
120
121   // insert into music list
122   std::pair<std::map<std::string, MusicResource>::iterator, bool> result = 
123     musics.insert(
124         std::make_pair<std::string, MusicResource> (file, MusicResource()));
125   MusicResource& resource = result.first->second;
126   resource.manager = this;
127   resource.music = song;
128
129   return true;
130 }
131
132 void
133 SoundManager::free_music(MusicResource* )
134 {
135   // TODO free music, currently we can't do this since SDL_mixer seems to have
136   // some bugs if you load/free alot of mod files.  
137 }
138
139 void
140 SoundManager::play_music(const MusicRef& musicref, int loops)
141 {
142   if(!audio_device)
143     return;
144
145   if(musicref.music == 0 || current_music == musicref.music)
146     return;
147
148   if(current_music)
149     current_music->refcount--;
150   
151   current_music = musicref.music;
152   current_music->refcount++;
153   
154   if(m_music_enabled)
155     Mix_PlayMusic(current_music->music, loops);
156 }
157
158 void
159 SoundManager::halt_music()
160 {
161   if(!audio_device)
162     return;
163   
164   Mix_HaltMusic();
165   
166   if(current_music) {
167     current_music->refcount--;
168     if(current_music->refcount == 0)
169       free_music(current_music);
170     current_music = 0;
171   }
172 }
173
174 void
175 SoundManager::enable_music(bool enable)
176 {
177   if(!audio_device)
178     return;
179
180   if(enable == m_music_enabled)
181     return;
182   
183   m_music_enabled = enable;
184   if(m_music_enabled == false) {
185     Mix_HaltMusic();
186   } else {
187     Mix_PlayMusic(current_music->music, -1);
188   }
189 }
190
191 void
192 SoundManager::enable_sound(bool enable)
193 {
194   if(!audio_device)
195     return;
196   
197   m_sound_enabled = enable;
198 }
199
200 SoundManager::MusicResource::~MusicResource()
201 {
202   // don't free music buggy SDL_Mixer crashs for some mod files
203   // Mix_FreeMusic(music);
204 }
205
206 /* --- LOAD A SOUND --- */
207
208 Mix_Chunk* SoundManager::load_sound(const std::string& file)
209 {
210   if(!audio_device)
211     return 0;
212   
213   Mix_Chunk* snd = Mix_LoadWAV(file.c_str());
214
215   /*if (snd == 0)
216     Termination::abort("Can't load", file);*/
217
218   return(snd);
219 }
220
221 void SoundManager::free_chunk(Mix_Chunk *chunk)
222 {
223   Mix_FreeChunk( chunk );
224 }
225
226
227 /* --- OPEN THE AUDIO DEVICE --- */
228
229 int SoundManager::open_audio (int frequency, Uint16 format, int channels, int chunksize)
230 {
231   if (Mix_OpenAudio( frequency, format, channels, chunksize ) < 0)
232     return -1;
233
234   // allocate 16 channels for mixing
235   if (Mix_AllocateChannels(8)  != 8)
236     return -2;
237   
238   return 0;
239 }
240
241
242 /* --- CLOSE THE AUDIO DEVICE --- */
243
244 void SoundManager::close_audio( void )
245 {
246   if (audio_device) {
247     Mix_CloseAudio();
248   }
249 }
250
251 Mix_Chunk* SuperTux::IDToSound(int id)
252 {
253   return SoundManager::get()->sounds[id];
254 }
255