]> git.mxchange.org Git - flightgear.git/blob - src/Sound/soundmgr.cxx
Modified FGSubsystem::update() to take an int parameter for delta time
[flightgear.git] / src / Sound / soundmgr.cxx
1 // soundmgr.cxx -- Sound effect management class
2 //
3 // Sound manager initially written by David Findlay
4 // <david_j_findlay@yahoo.com.au> 2001
5 //
6 // C++-ified by Curtis Olson, started March 2001.
7 //
8 // Copyright (C) 2001  Curtis L. Olson - curt@flightgear.org
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 //
24 // $Id$
25
26
27 #include <simgear/debug/logstream.hxx>
28 #include <simgear/misc/sg_path.hxx>
29
30 #include <Main/globals.hxx>
31
32 #include "soundmgr.hxx"
33
34 #define FG_SOUND_SAFETY_MULT 3
35 #define FG_MAX_SOUND_SAFETY ( 1.0 / FG_SOUND_SAFETY_MULT )
36
37 // constructor
38 FGSimpleSound::FGSimpleSound( string file ) {
39     SGPath slfile( globals->get_fg_root() );
40     slfile.append( file );
41     sample = new slSample ( (char *)slfile.c_str() );
42     pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
43     volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
44     pitch_envelope->setStep ( 0, 0.01, 1.0 );
45     volume_envelope->setStep ( 0, 0.01, 1.0 );
46 }
47
48 FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len ) {
49     sample = new slSample ( buffer, len );
50     pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
51     volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
52     pitch_envelope->setStep ( 0, 0.01, 1.0 );
53     volume_envelope->setStep ( 0, 0.01, 1.0 );
54 }
55
56 // destructor
57 FGSimpleSound::~FGSimpleSound() {
58     delete pitch_envelope;
59     delete volume_envelope;
60     delete sample;
61 }
62
63
64 // constructor
65 FGSoundMgr::FGSoundMgr() {
66     audio_sched = new slScheduler( 8000 );
67     audio_sched -> setMaxConcurrent ( 6 ); 
68
69     audio_mixer = new smMixer;
70
71     SG_LOG( SG_GENERAL, SG_INFO,
72             "Rate = " << audio_sched->getRate()
73             << "  Bps = " << audio_sched->getBps()
74             << "  Stereo = " << audio_sched->getStereo() );
75 }
76
77 // destructor
78
79 FGSoundMgr::~FGSoundMgr() {
80     sound_map_iterator current = sounds.begin();
81     sound_map_iterator end = sounds.end();
82     for ( ; current != end; ++current ) {
83         FGSimpleSound *s = current->second;
84         delete s->get_sample();
85         delete s;
86     }
87
88     delete audio_sched;
89     delete audio_mixer;
90 }
91
92
93 // initialize the sound manager
94 void FGSoundMgr::init() {
95     last.stamp();
96     safety = FG_MAX_SOUND_SAFETY;
97
98     // audio_mixer -> setMasterVolume ( 80 ) ;  /* 80% of max volume. */
99     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
100
101     sound_map_iterator current = sounds.begin();
102     sound_map_iterator end = sounds.end();
103     for ( ; current != end; ++current ) {
104         FGSimpleSound *s = current->second;
105         delete s->get_sample();
106         delete s;
107     }
108     sounds.clear();
109
110 }
111
112 void FGSoundMgr::bind ()
113 {
114   // no properties yet
115 }
116
117 void FGSoundMgr::unbind ()
118 {
119   // no properties yet
120 }
121
122
123 // run the audio scheduler
124 void FGSoundMgr::update(int dt) {
125     SGTimeStamp current;
126     current.stamp();
127
128     double elapsed = (double)(current - last) / 1000000.0;
129     last = current;
130
131     if ( elapsed > safety ) {
132         safety = elapsed;
133     } else {
134         safety = safety * 0.99 + elapsed * 0.01;
135     }
136     if ( safety > FG_MAX_SOUND_SAFETY ) {
137         safety = FG_MAX_SOUND_SAFETY;
138     }
139     // cout << "safety = " << safety << endl;
140     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
141
142     if ( !audio_sched->not_working() )
143         audio_sched -> update();
144 }
145
146
147 // add a sound effect, return true if successful
148 bool FGSoundMgr::add( FGSimpleSound *sound, const string& refname  ) {
149     sounds[refname] = sound;
150
151     return true;
152 }
153
154
155 // remove a sound effect, return true if successful
156 bool FGSoundMgr::remove( const string& refname ) {
157
158     sound_map_iterator it = sounds.find( refname );
159     if ( it != sounds.end() ) {
160         // first stop the sound from playing (so we don't bomb the
161         // audio scheduler)
162         FGSimpleSound *sample = it->second;
163
164         // cout << "Playing " << sample->get_sample()->getPlayCount()
165         //      << " instances!" << endl;
166
167         audio_sched->stopSample( sample->get_sample() );
168         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
169                                         NULL,
170                                         SL_PITCH_ENVELOPE );
171         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
172                                         NULL,
173                                         SL_VOLUME_ENVELOPE );
174
175 #if defined ( PLIB_1_2_X )
176         // if PLIB_1_2_X, we can't reliably remove sounds
177         // that are currently being played. :-( So, let's just not
178         // remove them and return false.  The effects of this are that
179         // the sound sample will continue to finish playing (or
180         // continue to loop forever.)  And the sound sample will
181         // remain registered in the plib audio system.  This is a
182         // memory leak, and eventually this could cause us to max out
183         // the total number of allowed sound samples in plib, but what
184         // are you going to do?  Hopefully the plib team will do a new
185         // stable relase with these problems fixed.
186
187         // cout << "plib broken audio, skipping actual remove" << endl;
188
189         return false;
190 #else
191         // must call audio_sched->update() after stopping the sound
192         // but before deleting it.
193         audio_sched -> update();
194         // cout << "Still playing " << sample->get_sample()->getPlayCount()
195         //      << " instances!" << endl;
196
197         delete sample;
198         sounds.erase( it );
199
200         return true;
201 #endif
202    } else {
203         return false;
204     }
205 }
206
207
208 // return true of the specified sound exists in the sound manager system
209 bool FGSoundMgr::exists( const string& refname ) {
210     sound_map_iterator it = sounds.find( refname );
211     if ( it != sounds.end() ) {
212         return true;
213    } else {
214         return false;
215     }
216 }
217
218
219 // return a pointer to the FGSimpleSound if the specified sound exists
220 // in the sound manager system, otherwise return NULL
221 FGSimpleSound *FGSoundMgr::find( const string& refname ) {
222     sound_map_iterator it = sounds.find( refname );
223     if ( it != sounds.end() ) {
224         return it->second;
225    } else {
226         return NULL;
227     }
228 }
229
230
231 // tell the scheduler to play the indexed sample in a continuous
232 // loop
233 bool FGSoundMgr::play_looped( const string& refname ) {
234     sound_map_iterator it = sounds.find( refname );
235     if ( it != sounds.end() ) {
236         FGSimpleSound *sample = it->second;
237         audio_sched->loopSample( sample->get_sample() );
238         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
239                                         sample->get_pitch_envelope(),
240                                         SL_PITCH_ENVELOPE );
241         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
242                                         sample->get_volume_envelope(),
243                                         SL_VOLUME_ENVELOPE );
244         
245         return true;
246     } else {
247         return false;
248     }
249 }
250
251
252 // tell the scheduler to play the indexed sample once
253 bool FGSoundMgr::play_once( const string& refname ) {
254     sound_map_iterator it = sounds.find( refname );
255     if ( it != sounds.end() ) {
256         FGSimpleSound *sample = it->second;
257         audio_sched->stopSample( sample->get_sample() );
258         audio_sched->playSample( sample->get_sample() );
259         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
260                                         sample->get_pitch_envelope(),
261                                         SL_PITCH_ENVELOPE );
262         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
263                                         sample->get_volume_envelope(),
264                                         SL_VOLUME_ENVELOPE );
265         
266         return true;
267     } else {
268         return false;
269     }
270 }
271
272
273 // return true of the specified sound is currently being played
274 bool FGSoundMgr::is_playing( const string& refname ) {
275     sound_map_iterator it = sounds.find( refname );
276     if ( it != sounds.end() ) {
277         FGSimpleSound *sample = it->second;
278         return (sample->get_sample()->getPlayCount() > 0 );
279         return true;
280     } else {
281         return false;
282     }
283 }
284
285
286 // immediate stop playing the sound
287 bool FGSoundMgr::stop( const string& refname ) {
288     sound_map_iterator it = sounds.find( refname );
289     if ( it != sounds.end() ) {
290         FGSimpleSound *sample = it->second;
291         audio_sched->stopSample( sample->get_sample() );
292         return true;
293     } else {
294         return false;
295     }
296 }