- Avoid some expensive SDL_GetTicks() calls
[supertux.git] / src / object / ambient_sound.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 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 <math.h>
23 #include <stdexcept>
24 #include <iostream>
25
26 #include "ambient_sound.hpp"
27 #include "object_factory.hpp"
28 #include "lisp/lisp.hpp"
29 #include "sector.hpp"
30 #include "audio/sound_manager.hpp"
31 #include "audio/sound_source.hpp"
32 #include "log.hpp"
33
34 AmbientSound::AmbientSound(const lisp::Lisp& lisp)
35 {
36   position.x = 0;
37   position.y = 0;
38
39   dimension.x = 0;
40   dimension.y = 0;
41
42   distance_factor = 0;
43   distance_bias = 0;
44   maximumvolume = 1;
45   sample = "";
46   currentvolume = 0;
47
48   if (!(lisp.get("x", position.x)&&lisp.get("y", position.y))) {
49     log_warning << "No Position in ambient_sound" << std::endl;
50   }
51
52   lisp.get("width" , dimension.x);
53   lisp.get("height", dimension.y);
54
55   lisp.get("distance_factor",distance_factor);
56   lisp.get("distance_bias"  ,distance_bias  );
57   lisp.get("sample"         ,sample         );
58   lisp.get("volume"         ,maximumvolume  );
59
60   // set dimension to zero if smaller than 64, which is default size in flexlay
61   
62   if ((dimension.x <= 64) || (dimension.y <= 64)) {
63     dimension.x = 0;
64     dimension.y = 0;
65   }
66   
67   // square all distances (saves us a sqrt later)
68
69   distance_bias*=distance_bias;
70   distance_factor*=distance_factor;
71
72   // set default silence_distance
73
74   if (distance_factor == 0)
75     silence_distance = 10e99;
76   else
77     silence_distance = 1/distance_factor;
78   
79   lisp.get("silence_distance",silence_distance);
80
81   sound_source = 0; // not playing at the beginning
82   latency=0;
83 }
84
85 AmbientSound::AmbientSound(Vector pos, float factor, float bias, float vol, std::string file)
86 {
87   position.x=pos.x;
88   position.y=pos.y;
89
90   dimension.x=0;
91   dimension.y=0;
92
93   distance_factor=factor*factor;
94   distance_bias=bias*bias;
95   maximumvolume=vol;
96   sample=file;
97   
98   // set default silence_distance
99
100   if (distance_factor == 0)
101     silence_distance = 10e99;
102   else
103     silence_distance = 1/distance_factor;
104
105   sound_source = 0; // not playing at the beginning
106   latency=0;
107 }
108
109 AmbientSound::~AmbientSound() {
110   stop_playing();
111 }
112
113 void
114 AmbientSound::hit(Player& )
115 {
116 }
117
118 void
119 AmbientSound::stop_playing() {
120   delete sound_source;
121   sound_source = 0;
122 }
123
124 void
125 AmbientSound::start_playing()
126 {
127   try {
128     sound_source = sound_manager->create_sound_source(sample);
129     if(!sound_source)
130       throw std::runtime_error("file not found");
131    
132     sound_source->set_gain(0);
133     sound_source->set_looping(true);
134     currentvolume=targetvolume=1e-20;
135     sound_source->play();
136   } catch(std::exception& e) {
137     log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl;
138     delete sound_source;
139     sound_source = 0;
140     remove_me();
141   }
142 }
143
144 void
145 AmbientSound::update(float deltat) 
146
147   if (latency-- <= 0) {
148     float px,py;
149     float rx,ry;
150
151     // Player position
152     px=Sector::current()->player->get_pos().x;
153     py=Sector::current()->player->get_pos().y;
154
155     // Relate to which point in the area
156     rx=px<position.x?position.x:
157       (px<position.x+dimension.x?px:position.x+dimension.x);
158     ry=py<position.y?position.y:
159       (py<position.y+dimension.y?py:position.y+dimension.y);
160
161     // calculate square of distance
162     float sqrdistance=(px-rx)*(px-rx)+(py-ry)*(py-ry);
163     sqrdistance-=distance_bias;
164
165     // inside the bias: full volume (distance 0)
166     if (sqrdistance<0)
167       sqrdistance=0;
168     
169     // calculate target volume - will never become 0
170     targetvolume=1/(1+sqrdistance*distance_factor);
171     float rise=targetvolume/currentvolume;
172
173     // rise/fall half life?
174     currentvolume*=pow(rise,deltat*10);
175     currentvolume += 1e-6; // volume is at least 1e-6 (0 would never rise)
176
177     if (sound_source != 0) {
178
179       // set the volume
180       sound_source->set_gain(currentvolume*maximumvolume);
181
182       if (sqrdistance>=silence_distance && currentvolume<1e-3)
183         stop_playing();
184       latency=0;
185     } else {
186       if (sqrdistance<silence_distance) {
187         start_playing();
188         latency=0;
189       }
190       else // set a reasonable latency
191         latency=(int)(0.001/distance_factor);
192       //(int)(10*((sqrdistance-silence_distance)/silence_distance));
193     }
194   } 
195
196   // heuristically measured "good" latency maximum
197
198   //  if (latency>0.001/distance_factor)
199   // latency=
200 }
201
202 void
203 AmbientSound::draw(DrawingContext &) 
204 {
205 }
206
207 IMPLEMENT_FACTORY(AmbientSound, "ambient_sound");