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