]> git.mxchange.org Git - flightgear.git/blob - src/Sound/fg_sound.cxx
Added static port system and a new altimeter model connected to it.
[flightgear.git] / src / Sound / fg_sound.cxx
1 // fg_sound.cxx -- 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); }
45 static double _fg_log(double v)   { return (v < 1) ? 0 : log(v); }
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()
65   : _sample(NULL),
66     _condition(NULL),
67     _property(NULL),
68     _active(false),
69     _name(""),
70     _mode(FGSound::ONCE),
71     _prev_value(0),
72     _dt_play(0.0),
73     _dt_stop(0.0)
74 {
75 }
76
77 FGSound::~FGSound()
78 {
79    if (_property)
80       delete _property;
81
82    if (_condition)
83       delete _condition;
84
85    delete _sample;
86 }
87
88 void
89 FGSound::init(SGPropertyNode *node)
90 {
91
92    //
93    // set global sound properties
94    //
95    
96    _name = node->getStringValue("name", "");
97    SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name );
98
99    const char *mode_str = node->getStringValue("mode", "");
100    if ( !strcmp(mode_str, "looped") ) {
101        _mode = FGSound::LOOPED;
102
103    } else if ( !strcmp(mode_str, "in-transit") ) {
104        _mode = FGSound::IN_TRANSIT;
105
106    } else {
107       _mode = FGSound::ONCE;
108
109       if ( strcmp(mode_str, "") )
110          SG_LOG(SG_GENERAL,SG_INFO, "  Unknown sound mode, default to 'once'");
111    }
112
113    _property = fgGetNode(node->getStringValue("property", ""), true);
114    SGPropertyNode *condition = node->getChild("condition");
115    if (condition != NULL)
116       _condition = fgReadCondition(condition);
117
118    if (!_property && !_condition)
119       SG_LOG(SG_GENERAL, SG_WARN,
120              "  Neither a condition nor a property specified");
121
122    //
123    // set volume properties
124    //
125    unsigned int i;
126    float v = 0.0;
127    vector<SGPropertyNode_ptr> kids = node->getChildren("volume");
128    for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
129       _snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false};
130
131       if (strcmp(kids[i]->getStringValue("property"), ""))
132          volume.prop = fgGetNode(kids[i]->getStringValue("property", ""), true);
133
134       const char *intern_str = kids[i]->getStringValue("internal", "");
135       if (!strcmp(intern_str, "dt_play"))
136          volume.intern = &_dt_play;
137       else if (!strcmp(intern_str, "dt_stop"))
138          volume.intern = &_dt_stop;
139
140       if ((volume.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0)
141          if (volume.factor < 0.0) {
142             volume.factor = -volume.factor;
143             volume.subtract = true;
144          }
145
146       const char *type_str = kids[i]->getStringValue("type", "");
147       if ( strcmp(type_str, "") ) {
148
149          for (int j=0; __fg_snd_fn[j].fn; j++)
150            if ( !strcmp(type_str, __fg_snd_fn[j].name) ) {
151                volume.fn = __fg_snd_fn[j].fn;
152                break;
153             }
154
155          if (!volume.fn)
156             SG_LOG(SG_GENERAL,SG_INFO,
157                    "  Unknown volume type, default to 'lin'");
158       }
159
160       volume.offset = kids[i]->getDoubleValue("offset", 0.0);
161
162       if ((volume.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0)
163          SG_LOG( SG_GENERAL, SG_WARN,
164           "Volume minimum value below 0. Forced to 0.");
165
166       volume.max = kids[i]->getDoubleValue("max", 0.0);
167       if (volume.max && (volume.max < volume.min) )
168          SG_LOG(SG_GENERAL,SG_ALERT,
169                 "  Volume maximum below minimum. Neglected.");
170
171       _volume.push_back(volume);
172       v += volume.offset;
173
174    }
175
176
177    //
178    // set pitch properties
179    //
180    float p = 0.0;
181    kids = node->getChildren("pitch");
182    for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) {
183       _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false};
184
185       if (strcmp(kids[i]->getStringValue("property", ""), ""))
186          pitch.prop = fgGetNode(kids[i]->getStringValue("property", ""), true);
187
188       const char *intern_str = kids[i]->getStringValue("internal", "");
189       if (!strcmp(intern_str, "dt_play"))
190          pitch.intern = &_dt_play;
191       else if (!strcmp(intern_str, "dt_stop"))
192          pitch.intern = &_dt_stop;
193
194       if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0)
195          if (pitch.factor < 0.0) {
196             pitch.factor = -pitch.factor;
197             pitch.subtract = true;
198          }
199
200       const char *type_str = kids[i]->getStringValue("type", "");
201       if ( strcmp(type_str, "") ) {
202
203          for (int j=0; __fg_snd_fn[j].fn; j++) 
204             if ( !strcmp(type_str, __fg_snd_fn[j].name) ) {
205                pitch.fn = __fg_snd_fn[j].fn;
206                break;
207             }
208
209          if (!pitch.fn)
210             SG_LOG(SG_GENERAL,SG_INFO,
211                    "  Unknown pitch type, default to 'lin'");
212       }
213      
214       pitch.offset = kids[i]->getDoubleValue("offset", 1.0);
215
216       if ((pitch.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0)
217          SG_LOG(SG_GENERAL,SG_WARN,
218                 "  Pitch minimum value below 0. Forced to 0.");
219
220       pitch.max = kids[i]->getDoubleValue("max", 0.0);
221       if (pitch.max && (pitch.max < pitch.min) )
222          SG_LOG(SG_GENERAL,SG_ALERT,
223                 "  Pitch maximum below minimum. Neglected");
224
225       _pitch.push_back(pitch);
226       p += pitch.offset;
227    }
228
229    //
230    // Initialize the sample
231    //
232    _mgr = globals->get_soundmgr();
233    if ((_sample = _mgr->find(_name)) == NULL)
234       _sample = _mgr->add(_name, node->getStringValue("path", ""));
235
236    _sample->set_volume(v);
237    _sample->set_pitch(p);
238 }
239
240 void
241 FGSound::bind ()
242 {
243 }
244
245 void
246 FGSound::unbind ()
247 {
248 }
249
250 void
251 FGSound::update (double dt)
252 {
253    double curr_value = 0.0;
254
255    //
256    // If the state changes to false, stop playing.
257    //
258    if (_property)
259       curr_value = _property->getDoubleValue();
260
261    if (                                                 // Lisp, anyone?
262          (_condition && !_condition->test()) ||
263          (!_condition && _property &&
264             (
265                !curr_value ||
266                ( (_mode == FGSound::IN_TRANSIT) && (curr_value == _prev_value) )
267             )
268          )
269       )
270    {
271
272       if (_sample->is_playing()) {
273          SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play
274                                       << " sec: " << _name );
275          _sample->stop( _mgr->get_scheduler() );
276       }
277
278       _active = false;
279       _dt_stop += dt;
280       _dt_play = 0.0;
281
282       return;
283
284    }
285
286    //
287    // If the mode is ONCE and the sound is still playing,
288    //  we have nothing to do anymore.
289    //
290    if (_active && (_mode == FGSound::ONCE)) {
291
292       if (!_sample->is_playing()) {
293          _dt_stop += dt;
294          _dt_play = 0.0;
295
296       } else
297          _dt_play += dt;
298
299       return;
300    }
301
302    //
303    // Update playing time and cache the current value.
304    //
305     _dt_play += dt;
306    _prev_value = curr_value;
307
308    //
309    // Update the volume
310    //
311    int i;
312    int max = _volume.size();
313    double volume = 1.0;
314    double volume_offset = 0.0;
315
316    for(i = 0; i < max; i++) {
317       double v = 1.0;
318
319       if (_volume[i].prop)
320          v = _volume[i].prop->getDoubleValue();
321
322       else if (_volume[i].intern)
323          v = *_volume[i].intern;
324
325       if (_volume[i].fn)
326          v = _volume[i].fn(v);
327
328       v *= _volume[i].factor;
329
330       if (_volume[i].max && (v > _volume[i].max))
331          v = _volume[i].max;
332
333       else if (v < _volume[i].min)
334          v = _volume[i].min;
335
336       if (_volume[i].subtract)                          // Hack!
337          volume = _volume[i].offset - v;
338
339       else {
340          volume_offset += _volume[i].offset;
341          volume *= v;
342       }
343    }
344
345    //
346    // Update the pitch
347    //
348    max = _pitch.size();
349    double pitch = 1.0;
350    double pitch_offset = 0.0;
351
352    for(i = 0; i < max; i++) {
353       double p = 1.0;
354
355       if (_pitch[i].prop)
356          p = _pitch[i].prop->getDoubleValue();
357
358       else if (_pitch[i].intern)
359          p = *_pitch[i].intern;
360
361       if (_pitch[i].fn)
362          p = _pitch[i].fn(p);
363
364       p *= _pitch[i].factor;
365
366       if (_pitch[i].max && (p > _pitch[i].max))
367          p = _pitch[i].max;
368
369       else if (p < _pitch[i].min)
370          p = _pitch[i].min;
371
372       if (_pitch[i].subtract)                           // Hack!
373          pitch = _pitch[i].offset - p;
374
375       else {
376          pitch_offset += _pitch[i].offset;
377          pitch *= p;
378       }
379    }
380
381    //
382    // Change sample state
383    //
384    _sample->set_pitch( pitch_offset + pitch );
385    _sample->set_volume( volume_offset + volume );
386
387
388    //
389    // Do we need to start playing the sample?
390    //
391    if (!_active) {
392
393       if (_mode == FGSound::ONCE)
394          _sample->play(_mgr->get_scheduler(), false);
395
396       else
397          _sample->play(_mgr->get_scheduler(), true);
398
399       SG_LOG(SG_GENERAL, SG_INFO, "Playing audio after " << _dt_stop 
400                                    << " sec: " << _name);
401       SG_LOG(SG_GENERAL, SG_BULK,
402                          "Playing " << ((_mode == ONCE) ? "once" : "looped"));
403
404       _active = true;
405       _dt_stop = 0.0;
406    }
407 }