]> git.mxchange.org Git - flightgear.git/blob - src/Sound/soundmgr.cxx
*** empty log message ***
[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 //
38 // SimpleSound
39 //
40
41 // constructor
42 FGSimpleSound::FGSimpleSound( string file )
43   : requests(0),
44     volume(1.0),
45     pitch(1.0)
46 {
47     SGPath slfile( globals->get_fg_root() );
48     slfile.append( file );
49     sample = new slSample ( (char *)slfile.c_str() );
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 FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len )
57   : requests(0),
58     volume(1.0),
59     pitch(1.0)
60 {
61     sample = new slSample ( buffer, len );
62     pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
63     volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
64     pitch_envelope->setStep ( 0, 0.01, 1.0 );
65     volume_envelope->setStep ( 0, 0.01, 1.0 );
66 }
67
68 // destructor
69 FGSimpleSound::~FGSimpleSound() {
70     delete pitch_envelope;
71     delete volume_envelope;
72     delete sample;
73 }
74
75 void FGSimpleSound::play( slScheduler *sched, bool looped ) {
76     
77     requests++;
78     if (requests > 1)
79        return;
80
81     // sched->stopSample(sample);
82     if (looped)
83        sched->loopSample(sample);
84     else
85        sched->playSample(sample);
86     
87     sched->addSampleEnvelope(sample, 0, 0, pitch_envelope, SL_PITCH_ENVELOPE);
88     sched->addSampleEnvelope(sample, 0, 1, volume_envelope, SL_VOLUME_ENVELOPE);
89 }
90
91 void FGSimpleSound::stop( slScheduler *sched, bool quick ) {
92
93     if (quick)
94        requests = 0;
95     else
96        if (--requests < 0)
97           requests = 0;
98
99     if (requests > 0)
100        return;
101
102     sched->stopSample( sample );
103 }
104
105 //
106 // Sound Manager
107 //
108
109 // constructor
110 FGSoundMgr::FGSoundMgr() {
111     audio_sched = new slScheduler( 8000 );
112     if ( audio_sched->notWorking() ) {
113         SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
114     } else {
115         audio_sched -> setMaxConcurrent ( 6 ); 
116
117         audio_mixer = new smMixer;
118
119         SG_LOG( SG_GENERAL, SG_INFO,
120                 "Rate = " << audio_sched->getRate()
121                 << "  Bps = " << audio_sched->getBps()
122                 << "  Stereo = " << audio_sched->getStereo() );
123     }
124 }
125
126 // destructor
127
128 FGSoundMgr::~FGSoundMgr() {
129
130     //
131     // Remove the samples from the sample manager.
132     //
133     sample_map_iterator sample_current = samples.begin();
134     sample_map_iterator sample_end = samples.end();
135     for ( ; sample_current != sample_end; ++sample_current ) {
136         sample_ref *sr = sample_current->second;
137         delete sr->sample;
138         delete sr;
139     }
140
141     //
142     // Remove the sounds from the sound manager.
143     //
144     sound_map_iterator sound_current = sounds.begin();
145     sound_map_iterator sound_end = sounds.end();
146     for ( ; sound_current != sound_end; ++sound_current ) {
147         FGSimpleSound *s = sound_current->second;
148         delete s;
149     }
150
151     delete audio_sched;
152     delete audio_mixer;
153 }
154
155
156 // initialize the sound manager
157 void FGSoundMgr::init() {
158     last.stamp();
159     safety = FG_MAX_SOUND_SAFETY;
160
161     // audio_mixer -> setMasterVolume ( 80 ) ;  /* 80% of max volume. */
162     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
163
164
165     //
166     // Remove the samples from the sample manager.
167     //
168     sample_map_iterator sample_current = samples.begin();
169     sample_map_iterator sample_end = samples.end();
170     for ( ; sample_current != sample_end; ++sample_current ) {
171         sample_ref *sr = sample_current->second;
172         delete sr->sample;
173         delete sr;
174     }
175     samples.clear();
176  
177     //
178     // Remove the sounds from the sound manager.
179     //
180     sound_map_iterator sound_current = sounds.begin();
181     sound_map_iterator sound_end = sounds.end();
182     for ( ; sound_current != sound_end; ++sound_current ) {
183         FGSimpleSound *s = sound_current->second;
184         delete s->get_sample();
185         delete s;
186     }
187     sounds.clear();
188
189 }
190
191 void FGSoundMgr::bind ()
192 {
193   // no properties yet
194 }
195
196 void FGSoundMgr::unbind ()
197 {
198   // no properties yet
199 }
200
201
202 // run the audio scheduler
203 void FGSoundMgr::update(int dt) {
204     SGTimeStamp current;
205     current.stamp();
206
207     double elapsed = (double)(current - last) / 1000000.0;
208     last = current;
209
210     if ( elapsed > safety ) {
211         safety = elapsed;
212     } else {
213         safety = safety * 0.99 + elapsed * 0.01;
214     }
215     if ( safety > FG_MAX_SOUND_SAFETY ) {
216         safety = FG_MAX_SOUND_SAFETY;
217     }
218     // cout << "safety = " << safety << endl;
219     audio_sched -> setSafetyMargin ( FG_SOUND_SAFETY_MULT * safety ) ;
220
221     if ( !audio_sched->not_working() )
222         audio_sched -> update();
223 }
224
225
226 // add a sound effect, return true if successful
227 bool FGSoundMgr::add( FGSimpleSound *sound, const string& refname ) {
228
229     sample_map_iterator it = samples.find(refname);
230     if (it != samples.end())
231        return false;
232
233     sample_ref *sr = new sample_ref;
234
235     sr->n=1;
236     sr->sample = sound->get_sample();
237     samples[refname] = sr;
238
239     sounds[refname] = sound;
240
241     return true;
242 }
243
244 // add a sound from a file, return the sample if successful, else return NULL
245 FGSimpleSound *FGSoundMgr::add( const string& refname, const string &file ) {
246      FGSimpleSound *sound;
247
248     if (file == (string)"")
249        return NULL;
250
251     sample_map_iterator it = samples.find(file);
252     if (it == samples.end()) {
253        sound = new FGSimpleSound(file);
254        sounds[refname] = sound;
255
256        sample_ref *sr = new sample_ref;
257
258        sr->n=1;
259        sr->sample = sound->get_sample();
260        samples[file] = sr;
261
262     } else {
263        sample_ref *sr = it->second;
264
265        sr->n++;
266        sound =
267            new FGSimpleSound(sr->sample->getBuffer(), sr->sample->getLength());
268        sounds[refname] = sound;
269
270     }
271
272     return sound;
273 }
274
275 // remove a sound effect, return true if successful
276 bool FGSoundMgr::remove( const string& refname ) {
277
278     sound_map_iterator it = sounds.find( refname );
279     if ( it != sounds.end() ) {
280         // first stop the sound from playing (so we don't bomb the
281         // audio scheduler)
282         FGSimpleSound *sample = it->second;
283
284         // cout << "Playing " << sample->get_sample()->getPlayCount()
285         //      << " instances!" << endl;
286
287         audio_sched->stopSample( sample->get_sample() );
288         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, 
289                                         NULL,
290                                         SL_PITCH_ENVELOPE );
291         audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, 
292                                         NULL,
293                                         SL_VOLUME_ENVELOPE );
294
295         // must call audio_sched->update() after stopping the sound
296         // but before deleting it.
297         audio_sched -> update();
298         // cout << "Still playing " << sample->get_sample()->getPlayCount()
299         //      << " instances!" << endl;
300
301         //
302         // FIXME:
303         // Due to the change in the sound manager, samples live
304         // until the sound manager gets removed.
305         //
306         // delete sample;
307         sounds.erase( it );
308
309         return true;
310    } else {
311         return false;
312     }
313 }
314
315
316 // return true of the specified sound exists in the sound manager system
317 bool FGSoundMgr::exists( const string& refname ) {
318     sound_map_iterator it = sounds.find( refname );
319     if ( it != sounds.end() ) {
320         return true;
321    } else {
322         return false;
323     }
324 }
325
326
327 // return a pointer to the FGSimpleSound if the specified sound exists
328 // in the sound manager system, otherwise return NULL
329 FGSimpleSound *FGSoundMgr::find( const string& refname ) {
330     sound_map_iterator it = sounds.find( refname );
331     if ( it != sounds.end() ) {
332         return it->second;
333    } else {
334         return NULL;
335     }
336 }
337
338
339 // tell the scheduler to play the indexed sample in a continuous
340 // loop
341 bool FGSoundMgr::play_looped( const string& refname ) {
342    FGSimpleSound *sample;
343
344     if ((sample = find( refname )) == NULL)
345        return false;
346
347     sample->play(audio_sched, true);
348     return true;
349 }
350
351
352 // tell the scheduler to play the indexed sample once
353 bool FGSoundMgr::play_once( const string& refname ) {
354     FGSimpleSound *sample;
355
356     if ((sample = find( refname )) == NULL)
357        return false;
358
359     sample->play(audio_sched, false);
360     return true;
361 }
362
363
364 // return true of the specified sound is currently being played
365 bool FGSoundMgr::is_playing( const string& refname ) {
366     FGSimpleSound *sample;
367
368     if ((sample = find( refname )) == NULL)
369        return false;
370
371     return sample->is_playing();
372 }
373
374
375 // immediate stop playing the sound
376 bool FGSoundMgr::stop( const string& refname ) {
377     FGSimpleSound *sample;
378
379     if ((sample = find( refname )) == NULL)
380        return false;
381
382     audio_sched->stopSample( sample->get_sample() );
383     return true;
384 }