14539e61f25fe2515025c4a9626cdab2994d43e8
[supertux.git] / src / object / ambient_sound.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include <limits>
18 #include <math.h>
19
20 #include "audio/sound_manager.hpp"
21 #include "audio/sound_source.hpp"
22 #include "object/ambient_sound.hpp"
23 #include "object/camera.hpp"
24 #include "scripting/squirrel_util.hpp"
25 #include "supertux/object_factory.hpp"
26 #include "supertux/sector.hpp"
27 #include "util/reader.hpp"
28
29 AmbientSound::AmbientSound(const Reader& lisp) :
30   name(),
31   position(),
32   dimension(),
33   sample(),
34   sound_source(),
35   latency(),
36   distance_factor(),
37   distance_bias(),
38   silence_distance(),
39   maximumvolume(),
40   targetvolume(),
41   currentvolume(),
42   volume_ptr()
43 {
44   name="";
45   position.x = 0;
46   position.y = 0;
47
48   dimension.x = 0;
49   dimension.y = 0;
50
51   distance_factor = 0;
52   distance_bias = 0;
53   maximumvolume = 1;
54   sample = "";
55   currentvolume = 0;
56
57   if (!(lisp.get("x", position.x)&&lisp.get("y", position.y))) {
58     log_warning << "No Position in ambient_sound" << std::endl;
59   }
60
61   lisp.get("name" , name);
62   lisp.get("width" , dimension.x);
63   lisp.get("height", dimension.y);
64
65   lisp.get("distance_factor",distance_factor);
66   lisp.get("distance_bias"  ,distance_bias  );
67   lisp.get("sample"         ,sample         );
68   lisp.get("volume"         ,maximumvolume  );
69
70   // set dimension to zero if smaller than 64, which is default size in flexlay
71
72   if ((dimension.x <= 64) || (dimension.y <= 64)) {
73     dimension.x = 0;
74     dimension.y = 0;
75   }
76
77   // square all distances (saves us a sqrt later)
78
79   distance_bias*=distance_bias;
80   distance_factor*=distance_factor;
81
82   // set default silence_distance
83
84   if (distance_factor == 0)
85     silence_distance = std::numeric_limits<float>::max();
86   else
87     silence_distance = 1/distance_factor;
88
89   lisp.get("silence_distance",silence_distance);
90
91   sound_source.reset(); // not playing at the beginning
92   SoundManager::current()->preload(sample);
93   latency=0;
94 }
95
96 AmbientSound::AmbientSound(Vector pos, float factor, float bias, float vol, std::string file) :
97   name(),
98   position(),
99   dimension(),
100   sample(),
101   sound_source(),
102   latency(),
103   distance_factor(),
104   distance_bias(),
105   silence_distance(),
106   maximumvolume(),
107   targetvolume(),
108   currentvolume(),
109   volume_ptr()
110 {
111   position.x=pos.x;
112   position.y=pos.y;
113
114   dimension.x=0;
115   dimension.y=0;
116
117   distance_factor=factor*factor;
118   distance_bias=bias*bias;
119   maximumvolume=vol;
120   sample=file;
121
122   // set default silence_distance
123
124   if (distance_factor == 0)
125     silence_distance = std::numeric_limits<float>::max();
126   else
127     silence_distance = 1/distance_factor;
128
129   sound_source = 0; // not playing at the beginning
130   SoundManager::current()->preload(sample);
131   latency=0;
132 }
133
134 AmbientSound::~AmbientSound()
135 {
136   stop_playing();
137 }
138
139 void
140 AmbientSound::hit(Player& )
141 {
142 }
143
144 void
145 AmbientSound::stop_playing()
146 {
147   sound_source.reset();
148 }
149
150 void
151 AmbientSound::start_playing()
152 {
153   try {
154     sound_source = SoundManager::current()->create_sound_source(sample);
155     if(!sound_source)
156       throw std::runtime_error("file not found");
157
158     sound_source->set_gain(0);
159     sound_source->set_looping(true);
160     currentvolume=targetvolume=1e-20f;
161     sound_source->play();
162   } catch(std::exception& e) {
163     log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl;
164     sound_source.reset();
165     remove_me();
166   }
167 }
168
169 void
170 AmbientSound::update(float deltat)
171 {
172   if (latency-- <= 0) {
173     float px,py;
174     float rx,ry;
175
176     if (!Sector::current() || !Sector::current()->camera) return;
177     // Camera position
178     px=Sector::current()->camera->get_center().x;
179     py=Sector::current()->camera->get_center().y;
180
181     // Relate to which point in the area
182     rx=px<position.x?position.x:
183       (px<position.x+dimension.x?px:position.x+dimension.x);
184     ry=py<position.y?position.y:
185       (py<position.y+dimension.y?py:position.y+dimension.y);
186
187     // calculate square of distance
188     float sqrdistance=(px-rx)*(px-rx)+(py-ry)*(py-ry);
189     sqrdistance-=distance_bias;
190
191     // inside the bias: full volume (distance 0)
192     if (sqrdistance<0)
193       sqrdistance=0;
194
195     // calculate target volume - will never become 0
196     targetvolume=1/(1+sqrdistance*distance_factor);
197     float rise=targetvolume/currentvolume;
198
199     // rise/fall half life?
200     currentvolume*=pow(rise,deltat*10);
201     currentvolume += 1e-6f; // volume is at least 1e-6 (0 would never rise)
202
203     if (sound_source != 0) {
204
205       // set the volume
206       sound_source->set_gain(currentvolume*maximumvolume);
207
208       if (sqrdistance>=silence_distance && currentvolume<1e-3)
209         stop_playing();
210       latency=0;
211     } else {
212       if (sqrdistance<silence_distance) {
213         start_playing();
214         latency=0;
215       }
216       else // set a reasonable latency
217         latency=(int)(0.001/distance_factor);
218       //(int)(10*((sqrdistance-silence_distance)/silence_distance));
219     }
220   }
221
222   // heuristically measured "good" latency maximum
223
224   //  if (latency>0.001/distance_factor)
225   // latency=
226 }
227
228 void
229 AmbientSound::draw(DrawingContext &)
230 {
231 }
232
233 void
234 AmbientSound::expose(HSQUIRRELVM vm, SQInteger table_idx)
235 {
236   scripting::AmbientSound* _this = static_cast<scripting::AmbientSound*> (this);
237   expose_object(vm, table_idx, _this, name, false);
238 }
239
240 void
241 AmbientSound::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
242 {
243   scripting::unexpose_object(vm, table_idx, name);
244 }
245
246 void
247 AmbientSound::set_pos(float x, float y)
248 {
249   position.x = x;
250   position.y = y;
251 }
252
253 float
254 AmbientSound::get_pos_x() const
255 {
256   return position.x;
257 }
258
259 float
260 AmbientSound::get_pos_y() const
261 {
262   return position.y;
263 }
264
265 /* EOF */