Do not log an error about missing export symbols when export symbols ARE FOUND!
[gnash.git] / libsound / sound_handler.cpp
blobae7d84f868b8806981f12fca1d807d3c67ac0f99
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "sound_handler.h"
21 #include "EmbedSound.h" // for use
22 #include "InputStream.h" // for use
23 #include "EmbedSoundInst.h" // for upcasting to InputStream
24 #include "log.h" // for use
25 #include "WallClockTimer.h" // for debugging
27 #include <boost/cstdint.hpp> // For C99 int types
28 #include <vector> // for use
29 #include <cmath> // for floor (debugging)
31 // Debug create_sound/delete_sound/playSound/stop_sound, loops
32 //#define GNASH_DEBUG_SOUNDS_MANAGEMENT
34 // Debug samples fetching
35 //#define GNASH_DEBUG_SAMPLES_FETCHING 1
37 namespace {
39 unsigned int silentStream(void*, boost::int16_t* stream, unsigned int len, bool& atEOF)
41 std::fill(stream, stream+len, 0);
42 atEOF=false;
43 return len;
49 namespace gnash {
50 namespace sound {
52 sound_handler::StreamBlockId
53 sound_handler::addSoundBlock(unsigned char* data,
54 unsigned int data_bytes, unsigned int /*sample_count*/,
55 int handleId)
57 // @@ does a negative handle_id have any meaning ?
58 // should we change it to unsigned instead ?
59 if (handleId < 0 || (unsigned int) handleId+1 > _sounds.size())
61 log_error("Invalid (%d) sound_handle passed to fill_stream_data, "
62 "doing nothing", handleId);
63 delete [] data;
64 return -1;
67 EmbedSound* sounddata = _sounds[handleId];
68 if ( ! sounddata )
70 log_error("sound_handle passed to fill_stream_data (%d) "
71 "was deleted", handleId);
72 return -1;
75 // Handling of the sound data
76 size_t start_size = sounddata->size();
77 sounddata->append(reinterpret_cast<boost::uint8_t*>(data), data_bytes);
79 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
80 log_debug("fill_stream_data: sound %d, %d samples (%d bytes) appended at offset %d",
81 handleId, data_bytes/2, data_bytes, start_size);
82 #endif
84 return start_size;
87 void
88 sound_handler::delete_all_sounds()
90 for (Sounds::iterator i = _sounds.begin(),
91 e = _sounds.end(); i != e; ++i)
93 EmbedSound* sdef = *i;
95 // The sound may have been deleted already.
96 if (!sdef) continue;
98 stopEmbedSoundInstances(*sdef);
99 assert(!sdef->numPlayingInstances());
101 delete sdef;
103 _sounds.clear();
106 void
107 sound_handler::delete_sound(int sound_handle)
109 // Check if the sound exists
110 if (sound_handle < 0 || static_cast<unsigned int>(sound_handle) >= _sounds.size())
112 log_error("Invalid (%d) sound_handle passed to delete_sound, "
113 "doing nothing", sound_handle);
114 return;
117 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
118 log_debug ("deleting sound :%d", sound_handle);
119 #endif
121 EmbedSound* def = _sounds[sound_handle];
122 if ( ! def )
124 log_error("sound_handle passed to delete_sound (%d) "
125 "already deleted", sound_handle);
126 return;
129 stopEmbedSoundInstances(*def);
130 delete def;
131 _sounds[sound_handle] = 0;
135 void
136 sound_handler::stop_all_sounds()
138 for (Sounds::iterator i = _sounds.begin(),
139 e = _sounds.end(); i != e; ++i)
141 EmbedSound* sounddata = *i;
142 if ( ! sounddata ) continue; // could have been deleted already
143 stopEmbedSoundInstances(*sounddata);
148 sound_handler::get_volume(int soundHandle)
150 int ret;
151 // Check if the sound exists.
152 if (soundHandle >= 0 && static_cast<unsigned int>(soundHandle) < _sounds.size())
154 ret = _sounds[soundHandle]->volume;
155 } else {
156 ret = 0; // Invalid handle
158 return ret;
161 void
162 sound_handler::set_volume(int sound_handle, int volume)
164 // Check if the sound exists.
165 if (sound_handle < 0 || static_cast<unsigned int>(sound_handle) >= _sounds.size())
167 // Invalid handle.
168 } else {
170 // Set volume for this sound. Should this only apply to the active sounds?
171 _sounds[sound_handle]->volume = volume;
177 media::SoundInfo*
178 sound_handler::get_sound_info(int sound_handle)
180 // Check if the sound exists.
181 if (sound_handle >= 0 && static_cast<unsigned int>(sound_handle) < _sounds.size())
183 return _sounds[sound_handle]->soundinfo.get();
184 } else {
185 return NULL;
189 void
190 sound_handler::stop_sound(int sound_handle)
192 // Check if the sound exists.
193 if (sound_handle < 0 || (unsigned int) sound_handle >= _sounds.size())
195 log_debug("stop_sound(%d): invalid sound id", sound_handle);
196 // Invalid handle.
197 return;
201 EmbedSound* sounddata = _sounds[sound_handle];
202 if ( ! sounddata )
204 log_error("stop_sound(%d): sound was deleted", sound_handle);
205 return;
208 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
209 log_debug("stop_sound %d called", sound_handle);
210 #endif
212 stopEmbedSoundInstances(*sounddata);
216 void
217 sound_handler::stopEmbedSoundInstances(EmbedSound& def)
219 // Assert _mutex is locked ...
221 typedef std::vector<InputStream*> InputStreamVect;
222 InputStreamVect playing;
223 def.getPlayingInstances(playing);
225 // Now, for each playing InputStream, unplug it!
226 // NOTE: could be optimized...
227 for (InputStreamVect::iterator i=playing.begin(), e=playing.end();
228 i!=e; ++i)
230 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
231 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i);
232 #endif
234 unplugInputStream(*i);
237 def.clearInstances();
240 void
241 sound_handler::unplugInputStream(InputStream* id)
243 // WARNING: erasing would break any iteration in the set
244 InputStreams::iterator it2=_inputStreams.find(id);
245 if ( it2 == _inputStreams.end() )
247 log_error("SDL_sound_handler::unplugInputStream: "
248 "Aux streamer %p not found. ",
249 id);
250 return; // we won't delete it, as it's likely deleted already
253 _inputStreams.erase(it2);
255 // Increment number of sound stop request for the testing framework
256 _soundsStopped++;
258 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
259 log_debug("Unplugged InputStream %p", id);
260 #endif
262 // Delete the InputStream (we own it..)
263 delete id;
266 unsigned int
267 sound_handler::tell(int sound_handle)
269 // Check if the sound exists.
270 if (sound_handle < 0 || (unsigned int) sound_handle >= _sounds.size())
272 // Invalid handle.
273 return 0;
276 EmbedSound* sounddata = _sounds[sound_handle];
278 // If there is no active sounds, return 0
279 if ( ! sounddata->isPlaying() ) return 0;
281 // We use the first active sound of this.
282 InputStream* asound = sounddata->firstPlayingInstance();
284 // Return the playhead position in milliseconds
285 unsigned int samplesPlayed = asound->samplesFetched();
287 unsigned int ret = samplesPlayed / 44100 * 1000;
288 ret += ((samplesPlayed % 44100) * 1000) / 44100;
289 ret = ret / 2; // 2 channels
290 return ret;
293 unsigned int
294 sound_handler::get_duration(int sound_handle)
296 // Check if the sound exists.
297 if (sound_handle < 0 || (unsigned int) sound_handle >= _sounds.size())
299 // Invalid handle.
300 return 0;
303 EmbedSound* sounddata = _sounds[sound_handle];
305 boost::uint32_t sampleCount = sounddata->soundinfo->getSampleCount();
306 boost::uint32_t sampleRate = sounddata->soundinfo->getSampleRate();
308 // Return the sound duration in milliseconds
309 if (sampleCount > 0 && sampleRate > 0) {
310 // TODO: should we cache this in the EmbedSound object ?
311 unsigned int ret = sampleCount / sampleRate * 1000;
312 ret += ((sampleCount % sampleRate) * 1000) / sampleRate;
313 //if (sounddata->soundinfo->isStereo()) ret = ret / 2;
314 return ret;
315 } else {
316 return 0;
321 sound_handler::create_sound(std::auto_ptr<SimpleBuffer> data,
322 std::auto_ptr<media::SoundInfo> sinfo)
324 assert(sinfo.get());
326 std::auto_ptr<EmbedSound> sounddata(
327 new EmbedSound(data, sinfo, 100,
328 _mediaHandler ? _mediaHandler->getInputPaddingSize() : 0));
330 int sound_id = _sounds.size();
332 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
333 log_debug("create_sound: sound %d, format %s %s %dHz, %d samples (%d bytes)",
334 sound_id, sounddata->soundinfo->getFormat(),
335 sounddata->soundinfo->isStereo() ? "stereo" : "mono",
336 sounddata->soundinfo->getSampleRate(),
337 sounddata->soundinfo->getSampleCount(),
338 sounddata->size());
339 #endif
341 // the vector takes ownership
342 _sounds.push_back(sounddata.release());
344 return sound_id;
348 /*static private*/
349 unsigned int
350 sound_handler::swfToOutSamples(const media::SoundInfo& sinfo,
351 unsigned int swfSamples)
353 // swf samples refers to pre-resampled state so we need to
354 // take that into account.
357 static const unsigned int outSampleRate = 44100;
359 unsigned int outSamples = swfSamples *
360 (outSampleRate/sinfo.getSampleRate());
362 // NOTE: this was tested with inputs:
363 // - isStereo?0 is16bit()?1 sampleRate?11025
364 // - isStereo?0 is16bit()?1 sampleRate?22050
365 // - isStereo?1 is16bit()?1 sampleRate?22050
366 // - isStereo?0 is16bit()?1 sampleRate?44100
367 // - isStereo?1 is16bit()?1 sampleRate?44100
369 // TODO: test with other sample sizes !
371 #if 1
372 log_debug("NOTE: isStereo?%d is16bit()?%d sampleRate?%d",
373 sinfo.isStereo(), sinfo.is16bit(), sinfo.getSampleRate());
374 #endif
377 return outSamples;
382 /* private */
383 void
384 sound_handler::playSound(int sound_handle,
385 int loopCount, unsigned int inPoint, unsigned int outPoint,
386 StreamBlockId blockId, const SoundEnvelopes* envelopes,
387 bool allowMultiples)
389 assert (sound_handle >= 0 && static_cast<unsigned int>(sound_handle) < _sounds.size());
391 EmbedSound& sounddata = *(_sounds[sound_handle]);
393 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
394 log_debug("playSound %d called, SoundInfo format is %s",
395 sound_handle, sounddata.soundinfo->getFormat());
396 #endif
398 // When this is called from a StreamSoundBlockTag,
399 // we only start if this sound isn't already playing.
400 if ( ! allowMultiples && sounddata.isPlaying() )
402 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
403 log_debug(" playSound: multiple instances not allowed, "
404 "and sound %d is already playing", sound_handle);
405 #endif
406 // log_debug("Stream sound block play request, "
407 // "but an instance of the stream is "
408 // "already playing, so we do nothing");
409 return;
412 // Make sure sound actually got some data
413 if ( sounddata.empty() )
415 // @@ should this be a log_error ? or even an assert ?
416 IF_VERBOSE_MALFORMED_SWF(
417 log_swferror(_("Trying to play sound with size 0"));
419 return;
422 // Make a "EmbedSoundInst" for this sound and plug it into
423 // the set of InputStream channels
425 std::auto_ptr<InputStream> sound ( sounddata.createInstance(
427 // MediaHandler to use for decoding
428 *_mediaHandler,
430 // Sound block identifier
431 blockId,
433 // Samples range
434 inPoint, outPoint,
436 // Volume envelopes to use for this instance
437 envelopes,
439 // Loop count
440 loopCount
442 ) );
444 plugInputStream(sound);
447 /*public*/
448 void
449 sound_handler::playStream(int soundId, StreamBlockId blockId)
451 unsigned int inPoint=0;
452 unsigned int outPoint=std::numeric_limits<unsigned int>::max();
454 playSound(soundId, 0, inPoint, outPoint, blockId, 0, false);
457 /*public*/
458 void
459 sound_handler::startSound(int soundId, int loops,
460 const SoundEnvelopes* env,
461 bool allowMultiple, unsigned int inPoint,
462 unsigned int outPoint)
464 // Check if the sound exists
465 if (soundId < 0 ||
466 static_cast<unsigned int>(soundId) >= _sounds.size())
468 log_error("Invalid (%d) sound_handle passed to startSound, "
469 "doing nothing", soundId);
470 return;
474 // Handle delaySeek
476 EmbedSound& sounddata = *(_sounds[soundId]);
477 const media::SoundInfo& sinfo = *(sounddata.soundinfo);
479 int swfDelaySeek = sinfo.getDelaySeek();
480 if ( swfDelaySeek )
482 // NOTE: differences between delaySeek and inPoint:
484 // - Sample count semantic:
485 // inPoint uses output sample rate (44100 for one second)
486 // while delaySeek uses source sample rate
487 // (SoundInfo.getSampleRate() for one second)
489 // - Loop-back semantic:
490 // An event sound always loops-back from inPoint with no gaps
491 // When delaySeek is specified it is still used as a start
492 // for the initial playback but when it comes to looping back
493 // it seems to play silence instead of samples for the amount
494 // skipped:
496 // [ delaySeekTime ]
497 // loop1 *****************
498 // loop2 ----------------*****************
499 // loop3 ----------------*****************
501 // [ inPoint ]
502 // loop1 *****************
503 // loop2 *****************
504 // loop3 *****************
506 LOG_ONCE(log_unimpl("MP3 delaySeek"));
507 #if 0
508 unsigned int outDelaySeek = swfToOutSamples(sinfo, swfDelaySeek);
510 log_debug("inPoint(%d) + delaySeek(%d -> %d) == %d",
511 inPoint, swfDelaySeek, outDelaySeek,
512 inPoint+outDelaySeek);
514 inPoint += outDelaySeek;
515 #endif
518 playSound(soundId, loops, inPoint, outPoint, 0, env, allowMultiple);
521 void
522 sound_handler::plugInputStream(std::auto_ptr<InputStream> newStreamer)
524 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
525 InputStream* newStream = newStreamer.get(); // for debugging
526 #endif
528 if ( ! _inputStreams.insert(newStreamer.release()).second )
530 // this should never happen !
531 log_error("_inputStreams container still has a pointer "
532 "to deleted InputStream %p!", newStreamer.get());
533 // FIXME: replace the old element with the new one !
534 abort();
537 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
538 log_debug("Plugged InputStream %p", newStream);
539 #endif
541 // Increment number of sound start request for the testing framework
542 ++_soundsStarted;
545 void
546 sound_handler::unplugAllInputStreams()
548 for (InputStreams::iterator it=_inputStreams.begin(),
549 itE=_inputStreams.end();
550 it != itE; ++it)
552 delete *it;
554 _inputStreams.clear();
557 void
558 sound_handler::fetchSamples (boost::int16_t* to, unsigned int nSamples)
560 if ( isPaused() ) return; // should we write wav file anyway ?
562 float finalVolumeFact = getFinalVolume()/100.0;
564 std::fill(to, to+nSamples, 0);
566 // call NetStream or Sound audio callbacks
567 if ( !_inputStreams.empty() )
569 // A buffer to fetch InputStream samples into
570 boost::scoped_array<boost::int16_t> buf ( new boost::int16_t[nSamples] );
572 #ifdef GNASH_DEBUG_SAMPLES_FETCHING
573 log_debug("Fetching %d samples from each of %d input streams", nSamples, _inputStreams.size());
574 #endif
576 // Loop through the aux streamers sounds
577 for (InputStreams::iterator it=_inputStreams.begin(),
578 end=_inputStreams.end();
579 it != end; ++it)
581 InputStream* is = *it;
583 unsigned int wrote = is->fetchSamples(buf.get(), nSamples);
584 if ( wrote < nSamples )
586 // fill what wasn't written
587 std::fill(buf.get()+wrote, buf.get()+nSamples, 0);
590 #if GNASH_DEBUG_SAMPLES_FETCHING > 1
591 log_debug(" fetched %d/%d samples from input stream %p"
592 " (%d samples fetchehd in total)",
593 wrote, nSamples, is, is->samplesFetched());
594 #endif
596 mix(to, buf.get(), nSamples, finalVolumeFact);
599 unplugCompletedInputStreams();
602 // TODO: move this to base class !
603 if (_wavWriter.get())
605 _wavWriter->pushSamples(to, nSamples);
607 // now, mute all audio
608 std::fill(to, to+nSamples, 0);
611 // Now, after having "consumed" all sounds, blank out
612 // the buffer if muted..
613 if ( is_muted() )
615 std::fill(to, to+nSamples, 0);
619 /*public*/
620 void
621 sound_handler::setAudioDump(const std::string& wavefile)
623 bool wasDumping = (_wavWriter.get() != 0);
625 if (!wavefile.empty()) {
626 _wavWriter.reset(new WAVWriter(wavefile));
629 // TODO: just avoid pausing instead ...
630 if ( ! wasDumping ) {
631 // add a silent stream to the audio pool so that our
632 // output file is homogenous; we actually want silent
633 // wave data when no sounds are playing on the stage
634 attach_aux_streamer(silentStream, (void*) this);
638 /*private*/
639 void
640 sound_handler::unplugCompletedInputStreams()
642 InputStreams::iterator it = _inputStreams.begin();
643 InputStreams::iterator end = _inputStreams.end();
645 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
646 log_debug("Scanning %d input streams for completion", _inputStreams.size());
647 #endif
649 while (it != end)
651 InputStream* is = *it;
653 // On EOF, detach
654 if (is->eof())
656 // InputStream EOF, detach
657 InputStreams::iterator it2=it;
658 ++it2; // before we erase it
659 InputStreams::size_type erased = _inputStreams.erase(is);
660 if ( erased != 1 ) {
661 log_error("Expected 1 InputStream element, found %d", erased);
662 abort();
664 it = it2;
666 //log_debug("fetchSamples: marking stopped InputStream %p (on EOF)", is);
667 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
668 log_debug(" Input stream %p reached EOF, unplugging", is);
669 #endif
671 // WARNING! deleting the InputStream here means
672 // a lot of things will happen from a
673 // separate thread. Instead, if we
674 // extend sound_handler interface to
675 // have an unplugCompletedInputStreams
676 // we may call it at heart-beating intervals
677 // and drop any threading paranoia!
678 delete is;
680 // Increment number of sound stop request for the testing framework
681 _soundsStopped++;
683 else
685 ++it;
690 bool
691 sound_handler::hasInputStreams() const
693 return !_inputStreams.empty();
696 bool
697 sound_handler::is_muted() const
699 // TODO: lock a mutex ?
700 return _muted;
703 void
704 sound_handler::mute()
706 // TODO: lock a mutex ?
707 _muted=true;
710 void
711 sound_handler::unmute()
713 // TODO: lock a mutex ?
714 _muted=false;
717 void
718 sound_handler::reset()
720 // Do not delete sounds on reset or there'd be nothing to play
721 // on restart. For a new SWF, we need a new sound_handler.
722 sound_handler::stop_all_sounds();
725 InputStream*
726 sound_handler::attach_aux_streamer(aux_streamer_ptr ptr, void* owner)
728 assert(owner);
729 assert(ptr);
731 std::auto_ptr<InputStream> newStreamer ( new AuxStream(ptr, owner) );
733 InputStream* ret = newStreamer.get();
735 plugInputStream(newStreamer);
737 return ret;
740 } // gnash.sound namespace
741 } // namespace gnash