]> git.mxchange.org Git - flightgear.git/blob - src/Sound/fg_sound.cxx
Changes corresponding to recent property manager changes
[flightgear.git] / src / Sound / fg_sound.cxx
1 // fg_sound.hxx -- Sound class implementation
2 //
3 // Started by Erik Hofman, February 2002
4 // (Reuses some code from  fg_fx.cxx created by David Megginson)
5 //
6 // Copyright (C) 2002  Curtis L. Olson - curt@flightgear.org
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24 #include <simgear/compiler.h>
25
26 #ifdef SG_HAVE_STD_INCLUDES
27 #  include <cmath>
28 #else
29 #  include <math.h>
30 #endif
31 #include <string.h>
32
33 #include <simgear/debug/logstream.hxx>
34
35 #include <Main/fg_props.hxx>
36
37 #include "fg_sound.hxx"
38
39
40 // static double _fg_lin(double v)   { return v; };
41 static double _fg_inv(double v)   { return (v == 0) ? 1e99 : 1/v; };
42 static double _fg_abs(double v)   { return (v >= 0) ? v : -v; };
43 static double _fg_sqrt(double v)  { return (v < 0) ? sqrt(-v) : sqrt(v); };
44 static double _fg_log10(double v) { return (v < 1) ? 0 : log10(v+1); };
45 static double _fg_log(double v)   { return (v < 1) ? 0 : log(v+1); };
46 // static double _fg_sqr(double v)   { return pow(v, 2); };
47 // static double _fg_pow3(double v)  { return pow(v, 3); };
48
49 static const struct {
50         char *name;
51         double (*fn)(double);
52 } __fg_snd_fn[] = {
53 //      {"lin", _fg_lin},
54         {"inv", _fg_inv},
55         {"abs", _fg_abs},
56         {"sqrt", _fg_sqrt},
57         {"log", _fg_log10},
58         {"ln", _fg_log},
59 //      {"sqr", _fg_sqr},
60 //      {"pow3", _fg_pow3},
61         {"", NULL}
62 };
63
64 FGSound::FGSound(const SGPropertyNode * node)
65   : _node(node),
66     _sample(NULL),
67     _active(false),
68     _mode(FGSound::ONCE),
69     _type(FGSound::LEVEL),
70     _name(""),  
71     _factor(1.0)
72 {
73 }
74
75 FGSound::~FGSound()
76 {
77    delete _sample;
78 }
79
80 void
81 FGSound::init()
82 {
83
84    _property = fgGetNode(_node->getStringValue("property"), true);
85
86    //
87    // set global sound properties
88    //
89    _name = _node->getStringValue("name");
90
91    if ((_factor = _node->getDoubleValue("factor")) == 0.0)
92       _factor = 1.0;
93
94    _offset = _node->getDoubleValue("offset");
95
96    SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name );
97
98    const char *mode_str = _node->getStringValue("mode");
99    if ( !strcmp(mode_str,"looped") ) {
100        _mode = FGSound::LOOPED;
101
102    } else {
103       _mode = FGSound::ONCE;
104
105       if ( strcmp(mode_str, "") )
106          SG_LOG(SG_GENERAL,SG_INFO, "  Unknown sound mode, default to 'once'");
107    }
108
109    const char *type_str = _node->getStringValue("type");
110    if ( !strcmp(type_str, "flipflop") ) {
111       _type = FGSound::FLIPFLOP;
112
113    } else if ( !strcmp(type_str, "inverted") ) {
114       _type = FGSound::INVERTED;
115
116    } else if ( !strcmp(type_str, "raise") ) {
117       _type = FGSound::RAISE;
118
119    } else if ( !strcmp(type_str, "fall") ) {
120       _type = FGSound::FALL;
121
122    } else {
123       _type = FGSound::LEVEL;
124
125       if ( strcmp(type_str, "") )
126          SG_LOG(SG_GENERAL,SG_INFO, "  Unknown sound type, default to 'level'");
127    }
128
129 #if 0
130    //
131    // set position properties
132    //
133    _pos.dist = _node->getDoubleValue("dist");
134    _pos.hor = _node->getDoubleValue("pos_hor");
135    _pos.vert = _node->getDoubleValue("pos_vert"); 
136 #endif
137
138    //
139    // set volume properties
140    //
141    unsigned int i;
142    float v = 0.0;
143    vector<const SGPropertyNode *> kids = _node->getChildren("volume");
144    for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
145       _snd_prop volume;
146
147       volume.prop = fgGetNode(kids[i]->getStringValue("property"), true);
148
149       if ((volume.factor = kids[i]->getDoubleValue("factor")) == 0.0)
150          volume.factor = 1.0;
151
152       else 
153          if (volume.factor < 0.0) {
154             volume.factor = -volume.factor;
155             volume.subtract = true;
156
157          } else
158             volume.subtract = false;
159
160       volume.fn = NULL;
161       const char *type_str = kids[i]->getStringValue("type");
162       if ( strcmp(type_str, "") ) {
163
164          for (int j=0; __fg_snd_fn[j].fn; j++)
165            if ( !strcmp(type_str, __fg_snd_fn[j].name) ) {
166                volume.fn = __fg_snd_fn[j].fn;
167                break;
168             }
169
170          if (!volume.fn)
171             SG_LOG(SG_GENERAL,SG_INFO,
172                    "  Unknown volume type, default to 'lin'");
173       }
174
175       volume.offset = kids[i]->getDoubleValue("offset");
176
177       if ((volume.min = kids[i]->getDoubleValue("min")) < 0.0) {
178          SG_LOG( SG_GENERAL, SG_WARN,
179           "Volume minimum value below 0. Forced to 0.");
180
181          volume.min = 0.0;
182       }
183
184       volume.max = kids[i]->getDoubleValue("max");
185       if (volume.max && (volume.max < volume.min) ) {
186          SG_LOG(SG_GENERAL,SG_ALERT,
187                 "  Volume maximum below minimum. Neglected.");
188
189         volume.max = 0.0;
190       }
191
192       _volume.push_back(volume);
193       v += volume.offset;
194
195    }
196
197
198    //
199    // set pitch properties
200    //
201    float p = 0.0;
202    kids = _node->getChildren("pitch");
203    for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
204       _snd_prop pitch;
205
206       pitch.prop = fgGetNode(kids[i]->getStringValue("property"), true);
207
208       if ((pitch.factor = kids[i]->getDoubleValue("factor")) == 0.0)
209          pitch.factor = 1.0;
210
211       else
212          if (pitch.factor < 0.0) {
213             pitch.factor = -pitch.factor;
214             pitch.subtract = true;
215
216          } else
217             pitch.subtract = false;
218
219       pitch.fn = NULL;
220       const char *type_str = kids[i]->getStringValue("type");
221       if ( strcmp(type_str, "") ) {
222
223          for (int j=0; __fg_snd_fn[j].fn; j++) 
224             if ( !strcmp(type_str, __fg_snd_fn[j].name) ) {
225                pitch.fn = __fg_snd_fn[j].fn;
226                break;
227             }
228
229          if (!pitch.fn)
230             SG_LOG(SG_GENERAL,SG_INFO,
231                    "  Unknown pitch type, default to 'lin'");
232       }
233      
234       if ((pitch.offset = kids[i]->getDoubleValue("offset")) == 0.0)
235          pitch.offset = 1.0;
236
237       if ((pitch.min = kids[i]->getDoubleValue("min")) < 0.0) {
238          SG_LOG(SG_GENERAL,SG_WARN,
239                 "  Pitch minimum value below 0. Forced to 0.");
240
241          pitch.min = 0.0;
242       }
243
244       pitch.max = kids[i]->getDoubleValue("max");
245       if (pitch.max && (pitch.max < pitch.min) ) {
246          SG_LOG(SG_GENERAL,SG_ALERT,
247                 "  Pitch maximum below minimum. Neglected");
248
249          pitch.max = 0.0;
250       }
251
252       _pitch.push_back(pitch);
253       p += pitch.offset;
254    }
255
256    //
257    // Initialize the sample
258    //
259    FGSoundMgr * mgr = globals->get_soundmgr();
260    if ((_sample = mgr->find(_name)) == NULL)
261       _sample = mgr->add(_name, _node->getStringValue("path"));
262
263    _sample->set_volume(v);
264    _sample->set_pitch(p);
265 }
266
267 void
268 FGSound::bind ()
269 {
270 }
271
272 void
273 FGSound::unbind ()
274 {
275 }
276
277 void
278 FGSound::update (int dt)
279 {
280    FGSoundMgr * mgr = globals->get_soundmgr();
281
282    //
283    // Do we have something to do?
284    //
285
286    // if (!_property)
287    //   return;
288
289    if ((_type == FGSound::LEVEL)  || (_type == FGSound::INVERTED)) {
290
291       //
292       // use an integer to get false when:  -1 < check < 1
293       //
294       bool check = (int)(_offset + _property->getDoubleValue() * _factor);
295       if (_type == FGSound::INVERTED)
296          check = !check;
297
298       //
299       // If the state changes to false, stop playing.
300       //
301       if (!check) {
302          if (_active) {
303             SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
304             _sample->stop( mgr->get_scheduler(), false );
305             _active = false;
306          }
307
308          return;
309       }
310
311       //
312       // If the sound is already playing we have nothing to do.
313       //
314       if (_active && (_mode == FGSound::ONCE))
315          return;
316
317    } else {             // FLIPFLOP, RAISE, FALL
318
319       bool check = (int)(_offset + _property->getDoubleValue() * _factor);
320       if (check == _active)
321             return;
322
323       //
324       // Check for state changes.
325       // If the state changed, and the sound is still playing: stop playing.
326       //
327       if (_sample->is_playing()) {
328          SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name);
329          _sample->stop( mgr->get_scheduler() );
330       }
331
332       if ( ((_type == FGSound::RAISE) && !check) ||
333            ((_type == FGSound::FALL) && check) )
334          return;
335
336    }
337
338    {
339       int i, max;
340
341       //
342       // Update the volume
343       //
344       max = _volume.size();
345       double volume = 1.0;
346       double volume_offset = 0.0;
347
348       for(i = 0; i < max; i++) {
349          double v = _volume[i].prop->getDoubleValue();
350
351          if (_volume[i].fn)
352             v = _volume[i].fn(v);
353
354          v *= _volume[i].factor;
355
356          if (!_volume[i].max && (v > _volume[i].max))
357             v = _volume[i].max;
358
359          else if (!_volume[i].min && (v < _volume[i].min))
360             v = _volume[i].min;
361
362          if (_volume[i].subtract)                               // Hack!
363             volume = _volume[i].offset - v;
364          else {
365             volume_offset += _volume[i].offset;
366             volume *= v;
367          }
368       }
369
370       //
371       // Update the pitch
372       //
373       max = _pitch.size();
374       double pitch = 1.0;
375       double pitch_offset = 0.0;
376
377       for(i = 0; i < max; i++) {
378          double p = _pitch[i].prop->getDoubleValue();
379
380          if (_pitch[i].fn)
381             p = _pitch[i].fn(p);
382
383          p *= _pitch[i].factor;
384
385          if (!_pitch[i].max && (p > _pitch[i].max))
386             p = _pitch[i].max;
387
388          else if (!_pitch[i].min && (p < _pitch[i].min))
389             p = _pitch[i].min;
390
391          if (_pitch[i].subtract)                                // Hack!
392             pitch = _pitch[i].offset - p;
393          else {
394             pitch_offset += _pitch[i].offset;
395             pitch *= p;
396          }
397       }
398
399       //
400       // Change sample state
401       //
402       _sample->set_pitch( pitch_offset + pitch );
403       _sample->set_volume( volume_offset + volume );
404    }
405
406    //
407    // Do we need to start playing the sample?
408    //
409    if (_active && ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)))
410          return;
411
412    //
413    // This is needed for FGSound::FLIPFLOP and it works for 
414    // FGSound::LEVEl. Doing it this way saves an extra 'if'.
415    //
416    _active = !_active;
417
418    if (_mode == FGSound::ONCE)
419       _sample->play(mgr->get_scheduler(), false);
420    else
421       _sample->play(mgr->get_scheduler(), true);
422
423    SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name);
424    SG_LOG(SG_GENERAL, SG_BULK,
425     "Playing " << ((_mode == ONCE) ? "once" : "looped"));
426 }