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