]> git.mxchange.org Git - flightgear.git/blob - src/Sound/fg_fx.cxx
70e67bd638b1eb7d64cda862a7d9c3cf601b3634
[flightgear.git] / src / Sound / fg_fx.cxx
1 // fg_fx.cxx -- Sound effect management class implementation
2 //
3 // Started by David Megginson, October 2001
4 // (Reuses some code from main.cxx, probably by Curtis Olson)
5 //
6 // Copyright (C) 2001  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 <FDM/flight.hxx>       // FIXME: remove direct dependencies
25 #include <Main/fg_props.hxx>
26
27 #include "fg_fx.hxx"
28
29
30 static const char * engine_names[FGFX::MAX_ENGINES] = {
31   "engine0",
32   "engine1"
33 };
34
35 static const char * crank_names[FGFX::MAX_ENGINES] = {
36   "crank0",
37   "crank1"
38 };
39
40
41 FGFX::FGFX ()
42   : _old_flap_position(0),
43     _old_gear_position(0),
44     _wind(0),
45     _stall(0),
46     _rumble(0),
47     _flaps(0),
48     _gear_up(0),
49     _gear_dn(0),
50     _squeal(0),
51     _click(0),
52     _stall_warning_prop(0),
53     _flaps_prop(0),
54     _gear_prop(0)
55 {
56   for (int i = 0; i < MAX_ENGINES; i++) {
57     _engine[i] = 0;
58     _crank[i] = 0;
59     _engine_running_prop[i] = 0;
60     _engine_cranking_prop[i] = 0;
61   }
62 }
63
64 FGFX::~FGFX ()
65 {
66                                 // FIXME: is this right, or does the
67                                 // sound manager assume pointer ownership?
68   for (int i = 0; i < MAX_ENGINES; i++) {
69     delete _engine[i];
70     delete _crank[i];
71   }
72   delete _wind;
73   delete _stall;
74   delete _rumble;
75
76   delete _flaps;
77   delete _gear_up;
78   delete _gear_dn;
79   delete _squeal;
80   delete _click;
81 }
82
83
84 void
85 FGFX::init ()
86 {
87   FGSoundMgr * mgr = globals->get_soundmgr();
88   int i;
89
90   //
91   // Create and add engine-related sounds.
92   //
93   for (i = 0; i < MAX_ENGINES; i++) {
94                                 // Engine
95     _engine[i] =
96       new FGSimpleSound(fgGetString("/sim/sounds/engine/path",
97                                     "Sounds/wasp.wav"));
98     _engine[i]->set_volume(fgGetFloat("/sim/sounds/engine/volume", 1.0));
99     _engine[i]->set_pitch(fgGetFloat("/sim/sounds/engine/pitch", 1.0));
100     mgr->add(_engine[i], engine_names[i]);
101
102                                 // Starter
103     _crank[i] = new FGSimpleSound(fgGetString("/sim/sounds/cranking/path",
104                                               "Sounds/cranking.wav"));
105     _crank[i]->set_volume(fgGetFloat("/sim/sounds/cranking/volume", 0.5));
106     _crank[i]->set_pitch(fgGetFloat("/sim/sounds/cranking/pitch", 1.00));
107     mgr->add(_crank[i], crank_names[i]);
108   }
109
110
111   //
112   // Create and add the wind noise.
113   //
114   _wind = new FGSimpleSound(fgGetString("/sim/sounds/wind/path",
115                                         "Sounds/wind.wav"));
116   _wind->set_volume(fgGetFloat("/sim/sounds/wind/volume", 1.0));
117   _wind->set_pitch(fgGetFloat("/sim/sounds/wind/pitch", 1.0));
118   mgr->add(_wind, "wind");
119
120
121   //
122   // Create and add the stall noise.
123   //
124   _stall = new FGSimpleSound(fgGetString("/sim/sounds/stall/path",
125                                          "Sounds/stall.wav"));
126   _stall->set_volume(fgGetFloat("/sim/sounds/stall/volume", 1.0));
127   _stall->set_pitch(fgGetFloat("/sim/sounds/stall/pitch", 1.0));
128   mgr->add(_stall, "stall");
129
130   //
131   // Create and add the rumble noise.
132   //
133   _rumble = new FGSimpleSound(fgGetString("/sim/sounds/rumble/path",
134                                           "Sounds/rumble.wav"));
135   _rumble->set_volume(fgGetFloat("/sim/sounds/rumble/volume", 1.0));
136   _rumble->set_pitch(fgGetFloat("/sim/sounds/rumble/pitch", 1.0));
137   mgr->add(_rumble, "rumble");
138
139
140   //
141   // Create and add the flaps noise
142   //
143   _flaps = new FGSimpleSound(fgGetString("/sim/sounds/flaps/path",
144                                          "Sounds/flaps.wav"));
145   _flaps->set_volume(fgGetFloat("/sim/sounds/flaps/volume", 0.25));
146   _flaps->set_pitch(fgGetFloat("/sim/sounds/flaps/pitch", 1.0));
147   mgr->add(_flaps, "flaps");
148
149   //
150   // Create and add the gear noises.
151   //
152   _gear_up = new FGSimpleSound(fgGetString("/sim/sounds/gear-up/path",
153                                            "Sounds/gear-up.wav"));
154   _gear_dn = new FGSimpleSound(fgGetString("/sim/sounds/gear-down/path",
155                                            "Sounds/gear-dn.wav"));
156   _gear_up->set_volume(fgGetFloat("/sim/sounds/gear-up/volume", 1.0));
157   _gear_dn->set_volume(fgGetFloat("/sim/sounds/gear-down/volume", 1.0));
158   _gear_up->set_pitch(fgGetFloat("/sim/sounds/gear-up/pitch", 1.0));
159   _gear_dn->set_pitch(fgGetFloat("/sim/sounds/gear-down/pitch", 1.0));
160   mgr->add(_gear_up, "gear-up");
161   mgr->add(_gear_dn, "gear-down");
162
163   //
164   // Create and add the squeal noise.
165   //
166   _squeal = new FGSimpleSound(fgGetString("/sim/sounds/squeal/path",
167                                           "Sounds/squeal.wav"));
168   _squeal->set_volume(fgGetFloat("/sim/sounds/squeal/volume", 1.0));
169   _squeal->set_pitch(fgGetFloat("/sim/sounds/squeal/pitch", 1.0));
170   mgr->add(_squeal, "squeal");
171
172   //
173   // Simplistic wheel spin model for audio effect purposes only.  We
174   // don't want to play a full squeel, if the wheel has only departed
175   // from the ground for a split second.
176   //
177   for (i = 0; i < MAX_GEAR; i++) {
178       _wheel_spin[i] = 0.0;
179   }
180
181   //
182   // Create and add the click noise.
183   _click = new FGSimpleSound(fgGetString("/sim/sounds/click/path",
184                                          "Sounds/click.wav"));
185   _flaps->set_volume(fgGetFloat("/sim/sounds/click/volume", 1.0));
186   _flaps->set_pitch(fgGetFloat("/sim/sounds/click/pitch", 1.0));
187   mgr->add(_click, "click");
188
189
190   ////////////////////////////////////////////////////////////////////
191   // Grab some properties.
192   ////////////////////////////////////////////////////////////////////
193
194   for (i = 0; i < MAX_ENGINES; i++) {
195     SGPropertyNode * node = fgGetNode("engines/engine", i, true);
196     _engine_running_prop[i] = node->getChild("running", 0, true);
197     _engine_cranking_prop[i] = node->getChild("cranking", 0, true);
198   }
199   _stall_warning_prop = fgGetNode("/sim/aero/alarms/stall-warning", true);
200   _vc_prop = fgGetNode("/velocities/airspeed-kt", true);
201   _flaps_prop = fgGetNode("/controls/flaps", true);
202   _gear_prop = fgGetNode("/controls/gear-down", true);
203 }
204
205 void
206 FGFX::bind ()
207 {
208 }
209
210 void
211 FGFX::unbind ()
212 {
213 }
214
215 void
216 FGFX::update (int dt)
217 {
218   FGSoundMgr * mgr = globals->get_soundmgr();
219   int i;
220
221   ////////////////////////////////////////////////////////////////////
222   // Update the engine sound.
223   ////////////////////////////////////////////////////////////////////
224   
225   for (i = 0; i < MAX_ENGINES; i++) {
226
227     SGPropertyNode * node = fgGetNode("engines/engine", i, true);
228
229     if (_engine_running_prop[i]->getBoolValue()) {
230           // pitch corresponds to rpm
231           // volume corresponds to manifold pressure
232
233       double rpm_factor;
234       rpm_factor = node->getDoubleValue("rpm") / 2500.0;
235
236       double pitch = 0.3 + rpm_factor * 3.0;
237
238       // don't run at absurdly slow rates -- not realistic
239       // and sounds bad to boot.  :-)
240       if (pitch < 0.7)
241         pitch = 0.7;
242       if (pitch > 5.0)
243         pitch = 5.0;
244
245       double mp_factor;
246       mp_factor = node->getDoubleValue("mp-osi") / 100;
247
248       double volume = 0.15 + mp_factor / 2.0;
249
250       if (volume < 0.15)
251         volume = 0.15;
252       if (volume > 0.5)
253         volume = 0.5;
254
255       _engine[i]->set_pitch( pitch );
256       _engine[i]->set_volume( volume );
257       set_playing(engine_names[i], true);
258     } else {
259       set_playing(engine_names[i], false);
260     }
261
262                                 // FIXME
263     set_playing(crank_names[i], _engine_cranking_prop[i]->getBoolValue());
264   }
265
266
267   ////////////////////////////////////////////////////////////////////
268   // Update the wind noise.
269   ////////////////////////////////////////////////////////////////////
270
271                                 // The wind sound is airspeed and altitude
272                                 // dependent. The wind noise should become
273                                 // silent at about 65000 feet, and is based
274                                 // on a logarithmic scale.
275   float rel_wind = cur_fdm_state->get_V_rel_wind(); // FPS
276   float alt_vol = 1-log(cur_fdm_state->get_Altitude()/65000.0)/4.81;
277   if ((rel_wind > 2.0)  && (alt_vol > 0.2)) {
278     float volume = rel_wind/937;
279     double pitch = 1.0+(rel_wind/53.0);
280     _wind->set_volume(volume*alt_vol/2);
281     _wind->set_pitch(pitch);
282     set_playing("wind", true);
283   } else {
284     set_playing("wind", false);
285   }
286
287
288   ////////////////////////////////////////////////////////////////////
289   // Update the stall horn.
290   ////////////////////////////////////////////////////////////////////
291
292   double stall = _stall_warning_prop->getDoubleValue();
293   double vc = _vc_prop->getDoubleValue();
294   if (stall > 0.0 && vc > 30.0) {
295     _stall->set_volume(stall);
296     set_playing("stall", true);
297   } else {
298     set_playing("stall", false);
299   }
300
301
302   ////////////////////////////////////////////////////////////////////
303   // Update the rumble.
304   ////////////////////////////////////////////////////////////////////
305
306   float gearOnGround = 0;
307
308
309                                 // Calculate whether a squeal is
310                                 // required, and set the volume.
311                                 // Currently, the squeal volume is the
312                                 // current local down velocity in feet
313                                 // per second divided by 10.0, and
314                                 // will not be played if under 0.1.
315
316                                 // FIXME: take rotational velocities
317                                 // into account as well.
318   for (i = 0; i < MAX_GEAR; i++) {
319     SGPropertyNode * node = fgGetNode("gear/gear", i, true);
320     // cout << "air speed = " << cur_fdm_state->get_V_equiv_kts();
321     // cout << "  wheel " << i << " speed = " << _wheel_spin[i];
322     if (node->getBoolValue("wow")) {
323       gearOnGround++;
324       if (!_gear_on_ground[i]) {
325           // wheel just touched down
326           // 3 parts horizontal velocity + 1 part vertical velocity
327           double squeal_volume = 0.75 * cur_fdm_state->get_V_equiv_kts() / 90.0
328               + 0.25 * cur_fdm_state->get_V_down() / 5.0;
329
330           // scale volume by difference between wheel spin speed and
331           // ground speed
332           double diff = fabs( cur_fdm_state->get_V_equiv_kts()
333                               - _wheel_spin[i] );
334           // cout << " speed diff = " << diff;
335           double scale_factor = 0.0;
336           if ( diff > 10 ) {
337               scale_factor = ( diff - 10.0 ) / 30.0f;
338               if ( scale_factor > 1.0 ) { scale_factor = 1.0; }
339           }
340           // cout << " scale_factor = " << scale_factor;
341           squeal_volume *= scale_factor;
342
343           if (squeal_volume > 0.1) {
344               _squeal->set_volume(squeal_volume);
345               _squeal->set_pitch(1.25);
346               mgr->play_once("squeal");
347           }
348           _gear_on_ground[i] = true;
349       }
350       // cout << " wow";
351       _wheel_spin[i] = cur_fdm_state->get_V_equiv_kts();
352     } else {
353         // cout << " airborn";
354         _gear_on_ground[i] = false;
355         /* fix me: wheel spindown is currently frame rate dependent which
356            it shouldn't be */
357         _wheel_spin[i] -= 0.2;
358         if ( _wheel_spin[i] < 0.0 ) { _wheel_spin[i] = 0.0; }
359     }
360     // cout << endl;
361   }
362
363                                 // Now, if any of the gear is in
364                                 // contact with the ground play the
365                                 // rumble sound.  The volume is a
366                                 // logarthmic scale of the absolute
367                                 // velocity in knots divided by 12.0.
368                                 // No rumble will be played if the
369                                 // velocity is under ~0.25 kt.
370   double speed = cur_fdm_state->get_V_equiv_kts();
371   if (gearOnGround > 0  && speed > 0.5) {
372     double volume = 2.0 * (gearOnGround/MAX_GEAR) * log(speed)/12; //(speed/60.0);
373     _rumble->set_volume(volume);
374     set_playing("rumble", true);
375   } else {
376     set_playing("rumble", false);
377   }
378
379
380   ////////////////////////////////////////////////////////////////////
381   // Check for flap movement.
382   ////////////////////////////////////////////////////////////////////
383
384   double flap_position = _flaps_prop->getDoubleValue();
385   if (fabs(flap_position - _old_flap_position) > 0.1) {
386     mgr->play_once("flaps");
387     _old_flap_position = flap_position;
388   }
389
390
391   ////////////////////////////////////////////////////////////////////
392   // Check for gear movement.
393   ////////////////////////////////////////////////////////////////////
394   
395   double gear_position = _gear_prop->getDoubleValue();
396   if (gear_position != _old_gear_position) {
397     if (gear_position < _old_gear_position) {
398       mgr->play_once("gear-up");
399     } else {
400       mgr->play_once("gear-down");
401     }
402     _old_gear_position = gear_position;
403   }
404
405   // TODO: click
406
407 }
408
409
410 void
411 FGFX::set_playing (const char * soundName, bool state)
412 {
413   FGSoundMgr * mgr = globals->get_soundmgr();
414   bool playing = mgr->is_playing(soundName);
415   if (state && !playing)
416     mgr->play_looped(soundName);
417   else if (!state && playing)
418     mgr->stop(soundName);
419 }
420
421 // end of fg_fx.cxx