1 // soundmgr.cxx -- Sound effect management class
3 // Sound manager initially written by David Findlay
4 // <david_j_findlay@yahoo.com.au> 2001
6 // C++-ified by Curtis Olson, started March 2001.
8 // Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
28 #include <simgear/debug/logstream.hxx>
29 #include <simgear/misc/sg_path.hxx>
31 #include "soundmgr.hxx"
33 #define SOUND_SAFETY_MULT 3
34 #define MAX_SOUND_SAFETY ( 1.0 / SOUND_SAFETY_MULT )
41 SGSimpleSound::SGSimpleSound( const char *path, const char *file )
44 volume_envelope(NULL),
48 SGPath slfile( path );
50 slfile.append( file );
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 );
59 SGSimpleSound::SGSimpleSound( unsigned char *buffer, int len )
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 );
71 SGSimpleSound::~SGSimpleSound() {
72 delete pitch_envelope;
73 delete volume_envelope;
77 void SGSimpleSound::play( slScheduler *sched, bool looped ) {
79 // make sure sound isn't already playing
80 if ( sample->getPlayCount() > 0 ) {
81 sched->stopSample(sample);
86 sched->loopSample(sample);
88 sched->playSample(sample);
91 sched->addSampleEnvelope(sample, 0, 0, pitch_envelope, SL_PITCH_ENVELOPE);
92 sched->addSampleEnvelope(sample, 0, 1, volume_envelope, SL_VOLUME_ENVELOPE);
95 void SGSimpleSound::stop( slScheduler *sched, bool quick ) {
97 sched->stopSample( sample );
105 SGSoundMgr::SGSoundMgr() {
106 audio_sched = new slScheduler( 8000 );
107 if ( audio_sched->notWorking() ) {
108 SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
110 audio_sched -> setMaxConcurrent ( 6 );
112 audio_mixer = new smMixer;
114 SG_LOG( SG_GENERAL, SG_INFO,
115 "Rate = " << audio_sched->getRate()
116 << " Bps = " << audio_sched->getBps()
117 << " Stereo = " << audio_sched->getStereo() );
123 SGSoundMgr::~SGSoundMgr() {
126 // Remove the samples from the sample manager.
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;
133 audio_sched->stopSample(sr->sample);
139 // Remove the sounds from the sound manager.
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 SGSimpleSound *s = sound_current->second;
146 audio_sched->stopSample(s->get_sample());
147 delete s->get_sample();
156 // initialize the sound manager
157 void SGSoundMgr::init() {
158 safety = MAX_SOUND_SAFETY;
160 // audio_mixer -> setMasterVolume ( 80 ) ; /* 80% of max volume. */
161 audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ;
165 // Remove the samples from the sample manager.
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;
172 audio_sched->stopSample(sr->sample);
179 // Remove the sounds from the sound manager.
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 SGSimpleSound *s = sound_current->second;
186 audio_sched->stopSample(s->get_sample());
187 delete s->get_sample();
195 void SGSoundMgr::bind ()
201 void SGSoundMgr::unbind ()
207 // run the audio scheduler
208 void SGSoundMgr::update( double dt ) {
212 safety = safety * 0.99 + dt * 0.01;
214 if ( safety > MAX_SOUND_SAFETY ) {
215 safety = MAX_SOUND_SAFETY;
217 // cout << "safety = " << safety << endl;
218 audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ;
220 if ( !audio_sched->not_working() )
221 audio_sched -> update();
228 audio_sched->pauseSample(0, 0);
233 SGSoundMgr::resume ()
235 audio_sched->resumeSample(0, 0);
239 // add a sound effect, return true if successful
240 bool SGSoundMgr::add( SGSimpleSound *sound, const string& refname ) {
242 sound_map_iterator sound_it = sounds.find( refname );
243 if ( sound_it != sounds.end() ) {
244 // sound already exists
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 );
255 sample_ref *sr = new sample_ref;
258 sr->sample = sound->get_sample();
259 samples[refname] = sr;
261 sounds[refname] = sound;
267 // add a sound from a file, return the sample if successful, else return NULL
268 SGSimpleSound *SGSoundMgr::add( const string &refname,
269 const char *path, const char *file ) {
270 SGSimpleSound *sound;
272 SGPath slfile( path );
274 slfile.append( file );
276 if ( slfile.str().empty() )
279 sample_map_iterator it = samples.find(slfile.str());
280 if (it == samples.end()) {
282 sound = new SGSimpleSound(slfile.c_str());
283 sounds[refname] = sound;
285 sample_ref *sr = new sample_ref;
288 sr->sample = sound->get_sample();
289 samples[slfile.str()] = sr;
292 sample_ref *sr = it->second;
296 new SGSimpleSound(sr->sample->getBuffer(), sr->sample->getLength());
297 sounds[refname] = sound;
305 // remove a sound effect, return true if successful
306 bool SGSoundMgr::remove( const string &refname ) {
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
312 SGSimpleSound *sample = it->second;
314 // cout << "Playing " << sample->get_sample()->getPlayCount()
315 // << " instances!" << endl;
317 audio_sched->stopSample( sample->get_sample() );
318 audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0,
321 audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1,
323 SL_VOLUME_ENVELOPE );
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;
333 // Due to the change in the sound manager, samples live
334 // until the sound manager gets removed.
339 // cout << "sndmgr: removed -> " << refname << endl;
342 // cout << "sndmgr: failed remove -> " << refname << endl;
348 // return true of the specified sound exists in the sound manager system
349 bool SGSoundMgr::exists( const string &refname ) {
350 sound_map_iterator it = sounds.find( refname );
351 if ( it != sounds.end() ) {
359 // return a pointer to the SGSimpleSound if the specified sound exists
360 // in the sound manager system, otherwise return NULL
361 SGSimpleSound *SGSoundMgr::find( const string &refname ) {
362 sound_map_iterator it = sounds.find( refname );
363 if ( it != sounds.end() ) {
371 // tell the scheduler to play the indexed sample in a continuous
373 bool SGSoundMgr::play_looped( const string &refname ) {
374 SGSimpleSound *sample;
376 if ((sample = find( refname )) == NULL)
379 sample->play(audio_sched, true);
384 // tell the scheduler to play the indexed sample once
385 bool SGSoundMgr::play_once( const string& refname ) {
386 SGSimpleSound *sample;
388 if ((sample = find( refname )) == NULL)
391 sample->play(audio_sched, false);
396 // return true of the specified sound is currently being played
397 bool SGSoundMgr::is_playing( const string& refname ) {
398 SGSimpleSound *sample;
400 if ((sample = find( refname )) == NULL)
403 return (sample->get_sample()->getPlayCount() > 0 );
407 // immediate stop playing the sound
408 bool SGSoundMgr::stop( const string& refname ) {
409 SGSimpleSound *sample;
411 if ((sample = find( refname )) == NULL)
414 audio_sched->stopSample( sample->get_sample() );