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