fix
[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 #include <physfs.h>
27
28 #include "audio/sound_manager.h"
29
30 #include "audio/musicref.h"
31 #include "physfs/physfs_sdl.h"
32 #include "moving_object.h"
33 #include "resources.h"
34
35 SoundManager::SoundManager()
36   : current_music(0), m_music_enabled(true) , m_sound_enabled(true),
37     audio_device(false)
38 {
39 }
40
41 SoundManager::~SoundManager()
42 {
43   for(Sounds::iterator i = sounds.begin(); i != sounds.end(); ++i) {
44     Mix_FreeChunk(i->second);
45   }
46   sounds.clear();
47 }
48
49 int
50 SoundManager::play_sound(const std::string& name,int loops)
51 {
52   if(!audio_device || !m_sound_enabled)
53     return -1;
54   
55   Mix_Chunk* chunk = preload_sound(name);
56   if(chunk == 0) {
57     std::cerr << "Sound '" << name << "' not found.\n";
58     return -1;
59   }
60   int chan=Mix_PlayChannel(-1, chunk, loops);  
61   Mix_Volume(chan,MIX_MAX_VOLUME);
62   return chan;
63 }
64
65
66 int
67 SoundManager::play_sound(const std::string& sound, const MovingObject* object,
68     const Vector& pos)
69 {
70   // TODO keep track of the object later and move the sound along with the
71   // object.
72   return play_sound(sound, object->get_pos(), pos);
73 }
74
75 int
76 SoundManager::play_sound(const std::string& sound, const Vector& pos,
77     const Vector& pos2)
78 {
79   if(!audio_device || !m_sound_enabled)
80     return -1;
81
82   Mix_Chunk* chunk = preload_sound(sound);
83   if(chunk == 0) {
84     std::cerr << "Sound '" << sound << "' not found.\n";
85     return -1;                                               
86   }
87
88   // TODO make sure this formula is good
89   float distance 
90     = pos2.x- pos.x;
91   int loud = int(255.0/float(1600) * fabsf(distance));
92   if(loud > 255)
93     return -1;
94
95   int chan = Mix_PlayChannel(-1, chunk, 0);
96   if(chan < 0)
97     return -1;         
98   Mix_Volume(chan,MIX_MAX_VOLUME);                         
99   Mix_SetDistance(chan, loud);
100
101   // very bad way to do this...
102   if(distance > 100)
103     Mix_SetPanning(chan, 230, 24);
104   else if(distance < -100)
105     Mix_SetPanning(chan, 24, 230);
106   return chan;
107 }
108
109 MusicRef
110 SoundManager::load_music(const std::string& file)
111 {
112   if(!audio_device)
113     return MusicRef(0);
114
115   if(!exists_music(file)) {
116     std::stringstream msg;
117     msg << "Couldn't load musicfile '" << file << "': " << SDL_GetError();
118     throw std::runtime_error(msg.str());
119   }
120
121   std::map<std::string, MusicResource>::iterator i = musics.find(file);
122   assert(i != musics.end());
123   return MusicRef(& (i->second));
124 }
125
126 bool
127 SoundManager::exists_music(const std::string& filename)
128 {
129   if(!audio_device)
130     return true;
131   
132   // song already loaded?
133   std::map<std::string, MusicResource>::iterator i = musics.find(filename);
134   if(i != musics.end()) {
135     return true;                                      
136   }
137  
138   const char* dir = PHYSFS_getRealDir(filename.c_str());
139   if(dir == 0)
140     return false;
141   Mix_Music* song = Mix_LoadMUS( (std::string(dir) + "/" + filename).c_str() );
142   if(song == 0)
143     return false;
144
145   // insert into music list
146   std::pair<std::map<std::string, MusicResource>::iterator, bool> result = 
147     musics.insert(
148         std::make_pair<std::string, MusicResource> (filename, MusicResource()));
149   MusicResource& resource = result.first->second;
150   resource.manager = this;
151   resource.music = song;
152
153   return true;
154 }
155
156 void
157 SoundManager::free_music(MusicResource* )
158 {
159   // TODO free music, currently we can't do this since SDL_mixer seems to have
160   // some bugs if you load/free alot of mod files.  
161 }
162
163 void
164 SoundManager::play_music(const MusicRef& musicref, int loops)
165 {
166   if(!audio_device)
167     return;
168
169   if(musicref.music == 0 || current_music == musicref.music)
170     return;
171
172   if(current_music)
173     current_music->refcount--;
174   
175   current_music = musicref.music;
176   current_music->refcount++;
177   
178   if(m_music_enabled)
179     Mix_PlayMusic(current_music->music, loops);
180 }
181
182 void
183 SoundManager::halt_music()
184 {
185   if(!audio_device)
186     return;
187   
188   Mix_HaltMusic();
189   
190   if(current_music) {
191     current_music->refcount--;
192     if(current_music->refcount == 0)
193       free_music(current_music);
194     current_music = 0;
195   }
196 }
197
198 void
199 SoundManager::enable_music(bool enable)
200 {
201   if(!audio_device)
202     return;
203
204   if(enable == m_music_enabled)
205     return;
206   
207   m_music_enabled = enable;
208   if(m_music_enabled == false) {
209     Mix_HaltMusic();
210   } else {
211     if(current_music)
212       Mix_PlayMusic(current_music->music, -1);
213   }
214 }
215
216 void
217 SoundManager::enable_sound(bool enable)
218 {
219   if(!audio_device)
220     return;
221   
222   m_sound_enabled = enable;
223 }
224
225 SoundManager::MusicResource::~MusicResource()
226 {
227   // don't free music buggy SDL_Mixer crashs for some mod files
228   // Mix_FreeMusic(music);
229 }
230
231 Mix_Chunk* SoundManager::preload_sound(const std::string& name)
232 {
233   if(!audio_device)
234     return 0;
235
236   Sounds::iterator i = sounds.find(name);
237   if(i != sounds.end()) {
238     return i->second;
239   }
240
241   std::string filename = "sounds/";
242   filename += name;
243   filename += ".wav";
244   
245   Mix_Chunk* chunk = Mix_LoadWAV_RW(get_physfs_SDLRWops(filename), true);
246   if(chunk != 0) {
247     sounds.insert(std::make_pair(name, chunk));
248   }
249
250   return chunk;
251 }
252