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