SVN-Revision: 2418
[supertux.git] / src / audio / sound_manager.cpp
1 //  $Id: sound_manager.cpp 2334 2005-04-04 16:26:14Z grumbel $
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 #include <config.h>
20
21 #include <cmath>
22 #include <cassert>
23 #include <iostream>
24 #include <stdexcept>
25 #include <sstream>
26
27 #include "audio/sound_manager.h"
28 #include "audio/musicref.h"
29 #include "moving_object.h"
30 #include "resources.h"
31
32 SoundManager::SoundManager()
33   : current_music(0), m_music_enabled(true) , m_sound_enabled(true),
34     audio_device(false)
35 {
36 }
37
38 SoundManager::~SoundManager()
39 {
40   for(Sounds::iterator i = sounds.begin(); i != sounds.end(); ++i) {
41     Mix_FreeChunk(i->second);
42   }
43   sounds.clear();
44 }
45
46 void
47 SoundManager::play_sound(const std::string& name)
48 {
49   if(!audio_device || !m_sound_enabled)
50     return;
51   
52   Mix_Chunk* chunk = preload_sound(name);
53   if(chunk == 0) {
54     std::cerr << "Sound '" << name << "' not found.\n";
55     return;
56   }
57   Mix_PlayChannel(-1, chunk, 0);  
58 }
59
60 int
61 SoundManager::play_sound(const std::string& name,int loops)
62 {
63   if(!audio_device || !m_sound_enabled)
64     return -1;
65   
66   Mix_Chunk* chunk = preload_sound(name);
67   if(chunk == 0) {
68     std::cerr << "Sound '" << name << "' not found.\n";
69     return -1;
70   }
71   return Mix_PlayChannel(-1, chunk, loops);  
72 }
73
74 void
75 SoundManager::play_sound(const std::string& sound, const MovingObject* object,
76     const Vector& pos)
77 {
78   // TODO keep track of the object later and move the sound along with the
79   // object.
80   play_sound(sound, object->get_pos(), pos);
81 }
82
83 void
84 SoundManager::play_sound(const std::string& sound, const Vector& pos,
85     const Vector& pos2)
86 {
87   if(!audio_device || !m_sound_enabled)
88     return;
89
90   Mix_Chunk* chunk = preload_sound(sound);
91   if(chunk == 0) {
92     std::cerr << "Sound '" << sound << "' not found.\n";
93     return;                                              
94   }
95
96   // TODO make sure this formula is good
97   float distance 
98     = pos2.x- pos.x;
99   int loud = int(255.0/float(1600) * fabsf(distance));
100   if(loud > 255)
101     return;
102
103   int chan = Mix_PlayChannel(-1, chunk, 0);
104   if(chan < 0)
105     return;                                  
106   Mix_SetDistance(chan, loud);
107
108   // very bad way to do this...
109   if(distance > 100)
110     Mix_SetPanning(chan, 230, 24);
111   else if(distance < -100)
112     Mix_SetPanning(chan, 24, 230);
113 }
114
115 // Register a sound effect function - basti_
116
117 void 
118 SoundManager::register_effect(int channel,Mix_EffectFunc_t f,
119                               Mix_EffectDone_t d,void * arg) {
120
121   if(!audio_device || !m_sound_enabled)
122     return;
123   Mix_RegisterEffect(channel,f,d,arg);
124 }
125
126 // Adjust the Volume of a channel "on line". Needs sizeof(float) static data.
127
128 #define __ATYPE__ signed short int
129
130 void 
131 SoundManager::volume_adjust(int chan, void *stream, int len, void *udata) {
132   ((float *)udata)[1]=((float *)udata)[1]*0.95+
133     (((float *)udata)[0]-
134      ((float *)udata)[1])*0.05; // decay towards [0] - declick
135   float vol=((float*)udata)[1];
136
137   for (int i=0;i<len/2;i++)
138     ((__ATYPE__ *)stream)[i]=
139       ((__ATYPE__)(((signed short int *)stream)[i]*vol)); 
140   // FIXME: This should be audio-type dependant - how to do this correctly?
141   
142   chan=0; // -Werror sucks
143 }
144
145
146
147 MusicRef
148 SoundManager::load_music(const std::string& file)
149 {
150   if(!audio_device)
151     return MusicRef(0);
152
153   if(!exists_music(file)) {
154     std::stringstream msg;
155     msg << "Couldn't load musicfile '" << file << "': " << SDL_GetError();
156     throw std::runtime_error(msg.str());
157   }
158
159   std::map<std::string, MusicResource>::iterator i = musics.find(file);
160   assert(i != musics.end());
161   return MusicRef(& (i->second));
162 }
163
164 bool
165 SoundManager::exists_music(const std::string& file)
166 {
167   if(!audio_device)
168     return true;
169   
170   // song already loaded?
171   std::map<std::string, MusicResource>::iterator i = musics.find(file);
172   if(i != musics.end()) {
173     return true;                                      
174   }
175   
176   Mix_Music* song = Mix_LoadMUS(file.c_str());
177   if(song == 0)
178     return false;
179
180   // insert into music list
181   std::pair<std::map<std::string, MusicResource>::iterator, bool> result = 
182     musics.insert(
183         std::make_pair<std::string, MusicResource> (file, MusicResource()));
184   MusicResource& resource = result.first->second;
185   resource.manager = this;
186   resource.music = song;
187
188   return true;
189 }
190
191 void
192 SoundManager::free_music(MusicResource* )
193 {
194   // TODO free music, currently we can't do this since SDL_mixer seems to have
195   // some bugs if you load/free alot of mod files.  
196 }
197
198 void
199 SoundManager::play_music(const MusicRef& musicref, int loops)
200 {
201   if(!audio_device)
202     return;
203
204   if(musicref.music == 0 || current_music == musicref.music)
205     return;
206
207   if(current_music)
208     current_music->refcount--;
209   
210   current_music = musicref.music;
211   current_music->refcount++;
212   
213   if(m_music_enabled)
214     Mix_PlayMusic(current_music->music, loops);
215 }
216
217 void
218 SoundManager::halt_music()
219 {
220   if(!audio_device)
221     return;
222   
223   Mix_HaltMusic();
224   
225   if(current_music) {
226     current_music->refcount--;
227     if(current_music->refcount == 0)
228       free_music(current_music);
229     current_music = 0;
230   }
231 }
232
233 void
234 SoundManager::enable_music(bool enable)
235 {
236   if(!audio_device)
237     return;
238
239   if(enable == m_music_enabled)
240     return;
241   
242   m_music_enabled = enable;
243   if(m_music_enabled == false) {
244     Mix_HaltMusic();
245   } else {
246     if(current_music)
247       Mix_PlayMusic(current_music->music, -1);
248   }
249 }
250
251 void
252 SoundManager::enable_sound(bool enable)
253 {
254   if(!audio_device)
255     return;
256   
257   m_sound_enabled = enable;
258 }
259
260 SoundManager::MusicResource::~MusicResource()
261 {
262   // don't free music buggy SDL_Mixer crashs for some mod files
263   // Mix_FreeMusic(music);
264 }
265
266 Mix_Chunk* SoundManager::preload_sound(const std::string& name)
267 {
268   if(!audio_device)
269     return 0;
270
271   Sounds::iterator i = sounds.find(name);
272   if(i != sounds.end()) {
273     return i->second;
274   }
275
276   std::string filename = "sounds/";
277   filename += name;
278   filename += ".wav";
279   filename = get_resource_filename(filename);
280   
281   Mix_Chunk* chunk = Mix_LoadWAV(filename.c_str());
282   if(chunk != 0) {
283     sounds.insert(std::make_pair(name, chunk));
284   }
285
286   return chunk;
287 }
288