]> git.mxchange.org Git - flightgear.git/blob - src/Sound/soundmgr.cxx
Tweaking sound safety margin.
[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_mixer = new smMixer;
68
69     SG_LOG( SG_GENERAL, SG_INFO,
70             "Rate = " << audio_sched->getRate()
71             << "  Bps = " << audio_sched->getBps()
72             << "  Stereo = " << audio_sched->getStereo() );
73 }
74
75 // destructor
76 FGSoundMgr::~FGSoundMgr() {
77     sound_map_iterator current = sounds.begin();
78     sound_map_iterator end = sounds.end();
79     for ( ; current != end; ++current ) {
80         FGSimpleSound *s = current->second;
81         delete s->get_sample();
82         delete s;
83     }
84
85     delete audio_sched;
86     delete audio_mixer;
87 }
88
89
90 // initialize the sound manager
91 bool FGSoundMgr::init() {
92     last.stamp();
93     safety = FG_MAX_SOUND_SAFETY;
94
95     audio_mixer -> setMasterVolume ( 80 ) ;  /* 80% of max volume. */
96     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
97
98     sound_map_iterator current = sounds.begin();
99     sound_map_iterator end = sounds.end();
100     for ( ; current != end; ++current ) {
101         FGSimpleSound *s = current->second;
102         delete s->get_sample();
103         delete s;
104     }
105     sounds.clear();
106
107     if ( audio_sched->not_working() ) {
108         return false;
109     } else {
110         return true;
111     }
112 }
113
114
115 // run the audio scheduler
116 bool FGSoundMgr::update() {
117     SGTimeStamp current;
118     current.stamp();
119
120     double elapsed = (double)(current - last) / 1000000.0;
121     last = current;
122
123     if ( elapsed > safety ) {
124         safety = elapsed;
125     } else {
126         safety = safety * 0.99 + elapsed * 0.01;
127     }
128     if ( safety > FG_MAX_SOUND_SAFETY ) {
129         safety = FG_MAX_SOUND_SAFETY;
130     }
131     // cout << "safety = " << safety << endl;
132     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
133
134     if ( !audio_sched->not_working() ) {
135         audio_sched -> update();
136         return true;
137     } else {
138         return false;
139     }
140 }
141
142
143 // add a sound effect, return true if successful
144 bool FGSoundMgr::add( FGSimpleSound *sound, const string& refname  ) {
145     sounds[refname] = sound;
146
147     return true;
148 }
149
150
151 // remove a sound effect, return true if successful
152 bool FGSoundMgr::remove( const string& refname ) {
153
154     sound_map_iterator it = sounds.find( refname );
155     if ( it != sounds.end() ) {
156         // first stop the sound from playing (so we don't bomb the
157         // audio scheduler)
158         FGSimpleSound *sample = it->second;
159
160         // cout << "Playing " << sample->get_sample()->getPlayCount()
161         //      << " instances!" << endl;
162
163         audio_sched->stopSample( sample->get_sample() );
164         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
165                                         NULL,
166                                         SL_PITCH_ENVELOPE );
167         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
168                                         NULL,
169                                         SL_VOLUME_ENVELOPE );
170
171 #if defined ( PLIB_1_2_X )
172         // if PLIB_1_2_X, we can't reliably remove sounds
173         // that are currently being played. :-( So, let's just not
174         // remove them and return false.  The effects of this are that
175         // the sound sample will continue to finish playing (or
176         // continue to loop forever.)  And the sound sample will
177         // remain registered in the plib audio system.  This is a
178         // memory leak, and eventually this could cause us to max out
179         // the total number of allowed sound samples in plib, but what
180         // are you going to do?  Hopefully the plib team will do a new
181         // stable relase with these problems fixed.
182
183         // cout << "plib broken audio, skipping actual remove" << endl;
184
185         return false;
186 #else
187         // must call audio_sched->update() after stopping the sound
188         // but before deleting it.
189         audio_sched -> update();
190         // cout << "Still playing " << sample->get_sample()->getPlayCount()
191         //      << " instances!" << endl;
192
193         delete sample;
194         sounds.erase( it );
195
196         return true;
197 #endif
198    } else {
199         return false;
200     }
201 }
202
203
204 // return true of the specified sound exists in the sound manager system
205 bool FGSoundMgr::exists( const string& refname ) {
206     sound_map_iterator it = sounds.find( refname );
207     if ( it != sounds.end() ) {
208         return true;
209    } else {
210         return false;
211     }
212 }
213
214
215 // return a pointer to the FGSimpleSound if the specified sound exists
216 // in the sound manager system, otherwise return NULL
217 FGSimpleSound *FGSoundMgr::find( const string& refname ) {
218     sound_map_iterator it = sounds.find( refname );
219     if ( it != sounds.end() ) {
220         return it->second;
221    } else {
222         return NULL;
223     }
224 }
225
226
227 // tell the scheduler to play the indexed sample in a continuous
228 // loop
229 bool FGSoundMgr::play_looped( const string& refname ) {
230     sound_map_iterator it = sounds.find( refname );
231     if ( it != sounds.end() ) {
232         FGSimpleSound *sample = it->second;
233         audio_sched->loopSample( sample->get_sample() );
234         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
235                                         sample->get_pitch_envelope(),
236                                         SL_PITCH_ENVELOPE );
237         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
238                                         sample->get_volume_envelope(),
239                                         SL_VOLUME_ENVELOPE );
240         
241         return true;
242     } else {
243         return false;
244     }
245 }
246
247
248 // tell the scheduler to play the indexed sample once
249 bool FGSoundMgr::play_once( const string& refname ) {
250     sound_map_iterator it = sounds.find( refname );
251     if ( it != sounds.end() ) {
252         FGSimpleSound *sample = it->second;
253         audio_sched->stopSample( sample->get_sample() );
254         audio_sched->playSample( sample->get_sample() );
255         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
256                                         sample->get_pitch_envelope(),
257                                         SL_PITCH_ENVELOPE );
258         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
259                                         sample->get_volume_envelope(),
260                                         SL_VOLUME_ENVELOPE );
261         
262         return true;
263     } else {
264         return false;
265     }
266 }
267
268
269 // return true of the specified sound is currently being played
270 bool FGSoundMgr::is_playing( const string& refname ) {
271     sound_map_iterator it = sounds.find( refname );
272     if ( it != sounds.end() ) {
273         FGSimpleSound *sample = it->second;
274         return (sample->get_sample()->getPlayCount() > 0 );
275         return true;
276     } else {
277         return false;
278     }
279 }
280
281
282 // immediate stop playing the sound
283 bool FGSoundMgr::stop( const string& refname ) {
284     sound_map_iterator it = sounds.find( refname );
285     if ( it != sounds.end() ) {
286         FGSimpleSound *sample = it->second;
287         audio_sched->stopSample( sample->get_sample() );
288         return true;
289     } else {
290         return false;
291     }
292 }