conditionally remove the manpage alias files if they are only copies.
[gnash.git] / libsound / sound_handler.cpp
blobed35c1f9b1ee393f01d601ebb28cda84b9d7ce78
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 // Free Software 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"
22 #include <boost/cstdint.hpp> // For C99 int types
23 #include <vector>
24 #include <cmath>
25 #include <boost/scoped_array.hpp>
27 #include "EmbedSound.h" // for use
28 #include "InputStream.h" // for use
29 #include "EmbedSoundInst.h" // for upcasting to InputStream
30 #include "log.h" // for use
31 #include "StreamingSound.h"
32 #include "StreamingSoundData.h"
33 #include "SimpleBuffer.h"
35 // Debug create_sound/delete_sound/playSound/stop_sound, loops
36 //#define GNASH_DEBUG_SOUNDS_MANAGEMENT
38 // Debug samples fetching
39 //#define GNASH_DEBUG_SAMPLES_FETCHING 1
41 namespace gnash {
42 namespace sound {
44 namespace {
46 unsigned int
47 silentStream(void*, boost::int16_t* stream, unsigned int len, bool& atEOF)
49 std::fill(stream, stream + len, 0);
50 atEOF=false;
51 return len;
54 template<typename T>
55 bool
56 validHandle(const T& container, int handle)
58 return handle >= 0 && static_cast<size_t>(handle) < container.size();
61 /// Ensure that each buffer has appropriate padding for the decoder.
63 /// Note: all callers passing a SimpleBuffer should already do this,
64 /// so this is a paranoid check.
65 void
66 ensurePadding(SimpleBuffer& data, media::MediaHandler* m)
68 const size_t padding = m ? m->getInputPaddingSize() : 0;
69 if (data.capacity() - data.size() < padding) {
70 log_error(_("Sound data creator didn't appropriately pad "
71 "buffer. We'll do so now, but will cost memory copies."));
72 data.reserve(data.size() + padding);
76 } // anonymous namespace
78 sound_handler::StreamBlockId
79 sound_handler::addSoundBlock(std::auto_ptr<SimpleBuffer> data,
80 size_t sampleCount, int seekSamples, int handle)
82 if (!validHandle(_streamingSounds, handle)) {
83 log_error(_("Invalid (%d) handle passed to fill_stream_data, "
84 "doing nothing"), handle);
85 return -1;
88 StreamingSoundData* sounddata = _streamingSounds[handle];
89 if (!sounddata) {
90 log_error(_("handle passed to fill_stream_data (%d) "
91 "was deleted"), handle);
92 return -1;
95 assert(data.get());
96 ensurePadding(*data, _mediaHandler);
98 return sounddata->append(data, sampleCount, seekSamples);
101 void
102 sound_handler::delete_all_sounds()
104 for (Sounds::iterator i = _sounds.begin(),
105 e = _sounds.end(); i != e; ++i)
107 EmbedSound* sdef = *i;
109 // The sound may have been deleted already.
110 if (!sdef) continue;
112 stopEmbedSoundInstances(*sdef);
113 assert(!sdef->numPlayingInstances());
115 delete sdef;
117 _sounds.clear();
119 for (StreamingSounds::iterator i = _streamingSounds.begin(),
120 e = _streamingSounds.end(); i != e; ++i)
122 StreamingSoundData* sdef = *i;
124 // Streaming sounds are never deleted.
125 assert(sdef);
127 stopEmbedSoundInstances(*sdef);
128 assert(!sdef->numPlayingInstances());
130 delete sdef;
132 _streamingSounds.clear();
136 void
137 sound_handler::delete_sound(int handle)
139 // Check if the sound exists
140 if (!validHandle(_sounds, handle)) {
141 log_error(_("Invalid (%d) handle passed to delete_sound, "
142 "doing nothing"), handle);
143 return;
146 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
147 log_debug("deleting sound :%d", handle);
148 #endif
150 EmbedSound* def = _sounds[handle];
151 if (!def) {
152 log_error(_("handle passed to delete_sound (%d) "
153 "already deleted"), handle);
154 return;
157 stopEmbedSoundInstances(*def);
158 delete def;
159 _sounds[handle] = 0;
163 void
164 sound_handler::stop_all_sounds()
166 for (Sounds::iterator i = _sounds.begin(),
167 e = _sounds.end(); i != e; ++i)
169 EmbedSound* sounddata = *i;
170 if ( ! sounddata ) continue; // could have been deleted already
171 stopEmbedSoundInstances(*sounddata);
174 for (StreamingSounds::iterator i = _streamingSounds.begin(),
175 e = _streamingSounds.end(); i != e; ++i)
177 StreamingSoundData* sounddata = *i;
178 if (!sounddata) continue;
179 stopEmbedSoundInstances(*sounddata);
184 sound_handler::get_volume(int handle) const
186 if (validHandle(_sounds, handle)) return _sounds[handle]->volume;
188 // Invalid handle.
189 return 0;
192 void
193 sound_handler::set_volume(int handle, int volume)
195 // Set volume for this sound.
196 // Should this only apply to the active sounds?
197 if (validHandle(_sounds, handle)) _sounds[handle]->volume = volume;
200 media::SoundInfo*
201 sound_handler::get_sound_info(int handle) const
203 // Check if the sound exists.
204 if (validHandle(_streamingSounds, handle)) {
205 return &_streamingSounds[handle]->soundinfo;
207 return 0;
210 void
211 sound_handler::stopStreamingSound(int handle)
213 // Check if the sound exists.
214 if (!validHandle(_streamingSounds, handle)) {
215 log_debug("stop_sound(%d): invalid sound id", handle);
216 return;
219 StreamingSoundData* sounddata = _streamingSounds[handle];
220 assert(sounddata);
222 stopEmbedSoundInstances(*sounddata);
225 void
226 sound_handler::stopEventSound(int handle)
228 // Check if the sound exists.
229 if (!validHandle(_sounds, handle)) {
230 log_debug("stop_sound(%d): invalid sound id", handle);
231 return;
234 EmbedSound* sounddata = _sounds[handle];
235 if (!sounddata) {
236 log_error(_("stop_sound(%d): sound was deleted"), handle);
237 return;
240 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
241 log_debug("stop_sound %d called", handle);
242 #endif
244 stopEmbedSoundInstances(*sounddata);
248 void
249 sound_handler::stopAllEventSounds()
251 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
252 log_debug("stopAllEventSounds called");
253 #endif
255 for (Sounds::iterator i=_sounds.begin(), e=_sounds.end(); i != e; ++i)
257 EmbedSound* sounddata = *i;
258 if (!sounddata) continue; // possible ?
260 stopEmbedSoundInstances(*sounddata);
265 void
266 sound_handler::stopEmbedSoundInstances(StreamingSoundData& def)
268 // Assert _mutex is locked ...
269 typedef std::vector<InputStream*> InputStreamVect;
270 InputStreamVect playing;
271 def.getPlayingInstances(playing);
273 // Now, for each playing InputStream, unplug it!
274 // NOTE: could be optimized...
275 for (InputStreamVect::iterator i=playing.begin(), e=playing.end();
276 i!=e; ++i)
278 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
279 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i);
280 #endif
282 // Explicitly calling the base class implementation
283 // is a (dirty?) way to avoid mutex-locking overrides
284 // in subclasses causing deadlocks.
285 sound_handler::unplugInputStream(*i);
288 def.clearInstances();
292 void
293 sound_handler::stopEmbedSoundInstances(EmbedSound& def)
295 // Assert _mutex is locked ...
296 typedef std::vector<InputStream*> InputStreamVect;
297 InputStreamVect playing;
298 def.getPlayingInstances(playing);
300 // Now, for each playing InputStream, unplug it!
301 // NOTE: could be optimized...
302 for (InputStreamVect::iterator i=playing.begin(), e=playing.end();
303 i!=e; ++i)
305 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
306 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i);
307 #endif
309 // Explicitly calling the base class implementation
310 // is a (dirty?) way to avoid mutex-locking overrides
311 // in subclasses causing deadlocks.
312 sound_handler::unplugInputStream(*i);
315 def.clearInstances();
318 void
319 sound_handler::unplugInputStream(InputStream* id)
321 // WARNING: erasing would break any iteration in the set
322 InputStreams::iterator it2=_inputStreams.find(id);
323 if (it2 == _inputStreams.end()) {
324 log_error(_("SDL_sound_handler::unplugInputStream: "
325 "Aux streamer %p not found. "),
326 id);
327 return; // we won't delete it, as it's likely deleted already
330 _inputStreams.erase(it2);
332 // Increment number of sound stop request for the testing framework
333 _soundsStopped++;
335 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
336 log_debug("Unplugged InputStream %p", id);
337 #endif
339 // Delete the InputStream (we own it..)
340 delete id;
343 unsigned int
344 sound_handler::tell(int handle) const
346 // Check if the sound exists.
347 if (!validHandle(_sounds, handle)) return 0;
349 const EmbedSound* sounddata = _sounds[handle];
351 // If there is no active sounds, return 0
352 if (!sounddata->isPlaying()) return 0;
354 // We use the first active sound of this.
355 const InputStream* asound = sounddata->firstPlayingInstance();
357 // Return the playhead position in milliseconds
358 unsigned int samplesPlayed = asound->samplesFetched();
360 unsigned int ret = samplesPlayed / 44100 * 1000;
361 ret += ((samplesPlayed % 44100) * 1000) / 44100;
362 ret = ret / 2; // 2 channels
363 return ret;
366 unsigned int
367 sound_handler::get_duration(int handle) const
369 // Check if the sound exists.
370 if (!validHandle(_sounds, handle)) return 0;
372 const EmbedSound* sounddata = _sounds[handle];
374 const boost::uint32_t sampleCount = sounddata->soundinfo.getSampleCount();
375 const boost::uint32_t sampleRate = sounddata->soundinfo.getSampleRate();
377 // Return the sound duration in milliseconds
378 if (sampleCount > 0 && sampleRate > 0) {
379 // TODO: should we cache this in the EmbedSound object ?
380 unsigned int ret = sampleCount / sampleRate * 1000;
381 ret += ((sampleCount % sampleRate) * 1000) / sampleRate;
382 return ret;
384 return 0;
388 sound_handler::createStreamingSound(const media::SoundInfo& sinfo)
390 std::auto_ptr<StreamingSoundData> sounddata(
391 new StreamingSoundData(sinfo, 100));
393 int sound_id = _streamingSounds.size();
394 // the vector takes ownership
395 _streamingSounds.push_back(sounddata.release());
397 return sound_id;
401 sound_handler::create_sound(std::auto_ptr<SimpleBuffer> data,
402 const media::SoundInfo& sinfo)
404 if (data.get()) {
405 ensurePadding(*data, _mediaHandler);
407 else {
408 log_debug("Event sound with no data!");
410 std::auto_ptr<EmbedSound> sounddata(new EmbedSound(data, sinfo, 100));
412 int sound_id = _sounds.size();
414 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
415 log_debug("create_sound: sound %d, format %s %s %dHz, %d samples (%d bytes)",
416 sound_id, sounddata->soundinfo.getFormat(),
417 sounddata->soundinfo.isStereo() ? "stereo" : "mono",
418 sounddata->soundinfo.getSampleRate(),
419 sounddata->soundinfo.getSampleCount(),
420 sounddata->size());
421 #endif
423 // the vector takes ownership
424 _sounds.push_back(sounddata.release());
426 return sound_id;
430 bool
431 sound_handler::isSoundPlaying(int handle) const
433 if (!validHandle(_sounds, handle)) return false;
435 EmbedSound& sounddata = *(_sounds[handle]);
437 // When this is called from a StreamSoundBlockTag,
438 // we only start if this sound isn't already playing.
439 return sounddata.isPlaying();
442 void
443 sound_handler::playStream(int soundId, StreamBlockId blockId)
445 StreamingSoundData& s = *_streamingSounds[soundId];
446 if (s.isPlaying() || s.empty()) return;
448 try {
449 std::auto_ptr<InputStream> is(
450 s.createInstance(*_mediaHandler, blockId));
451 plugInputStream(is);
453 catch (const MediaException& e) {
454 log_error(_("Could not start streaming sound: %s"), e.what());
458 void
459 sound_handler::startSound(int handle, int loops, const SoundEnvelopes* env,
460 bool allowMultiple, unsigned int inPoint,
461 unsigned int outPoint)
463 // Check if the sound exists
464 if (!validHandle(_sounds, handle)) {
465 log_error(_("Invalid (%d) sound_handle passed to startSound, "
466 "doing nothing"), handle);
467 return;
470 // Handle delaySeek
471 EmbedSound& sounddata = *(_sounds[handle]);
472 const media::SoundInfo& sinfo = sounddata.soundinfo;
474 const int swfDelaySeek = sinfo.getDelaySeek();
475 if (swfDelaySeek) {
476 // NOTE: differences between delaySeek and inPoint:
478 // - Sample count semantic:
479 // inPoint uses output sample rate (44100 for one second)
480 // while delaySeek uses source sample rate
481 // (SoundInfo.getSampleRate() for one second)
483 // - Loop-back semantic:
484 // An event sound always loops-back from inPoint with no gaps
485 // When delaySeek is specified it is still used as a start
486 // for the initial playback but when it comes to looping back
487 // it seems to play silence instead of samples for the amount
488 // skipped:
490 // [ delaySeekTime ]
491 // loop1 *****************
492 // loop2 ----------------*****************
493 // loop3 ----------------*****************
495 // [ inPoint ]
496 // loop1 *****************
497 // loop2 *****************
498 // loop3 *****************
500 LOG_ONCE(log_unimpl("MP3 delaySeek"));
501 #if 0
502 unsigned int outDelaySeek = swfToOutSamples(sinfo, swfDelaySeek);
504 log_debug("inPoint(%d) + delaySeek(%d -> %d) == %d",
505 inPoint, swfDelaySeek, outDelaySeek,
506 inPoint+outDelaySeek);
508 inPoint += outDelaySeek;
509 #endif
512 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
513 log_debug("startSound %d called, SoundInfo format is %s",
514 handle, sounddata.soundinfo.getFormat());
515 #endif
517 // When this is called from a StreamSoundBlockTag,
518 // we only start if this sound isn't already playing.
519 if (!allowMultiple && sounddata.isPlaying()) {
520 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
521 log_debug(" playSound: multiple instances not allowed, "
522 "and sound is already playing");
523 #endif
524 return;
527 // Make sure sound actually got some data
528 if (sounddata.empty()) {
529 // @@ should this be a log_error ? or even an assert ?
530 IF_VERBOSE_MALFORMED_SWF(
531 log_swferror(_("Trying to play sound with size 0"));
533 return;
536 try {
537 // Make an InputStream for this sound and plug it into
538 // the set of InputStream channels
539 std::auto_ptr<InputStream> sound(
540 sounddata.createInstance(*_mediaHandler, inPoint, outPoint,
541 env, loops));
542 plugInputStream(sound);
544 catch (const MediaException& e) {
545 log_error(_("Could not start event sound: %s"), e.what());
550 void
551 sound_handler::plugInputStream(std::auto_ptr<InputStream> newStreamer)
553 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
554 InputStream* newStream = newStreamer.get(); // for debugging
555 #endif
557 if (!_inputStreams.insert(newStreamer.release()).second) {
558 // this should never happen !
559 log_error(_("_inputStreams container still has a pointer "
560 "to deleted InputStream %p!"), newStreamer.get());
561 // FIXME: replace the old element with the new one !
562 abort();
565 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
566 log_debug("Plugged InputStream %p", newStream);
567 #endif
569 // Increment number of sound start request for the testing framework
570 ++_soundsStarted;
573 void
574 sound_handler::unplugAllInputStreams()
576 for (InputStreams::iterator it=_inputStreams.begin(),
577 itE=_inputStreams.end();
578 it != itE; ++it)
580 delete *it;
582 _inputStreams.clear();
585 void
586 sound_handler::fetchSamples(boost::int16_t* to, unsigned int nSamples)
588 if (isPaused()) return; // should we write wav file anyway ?
590 float finalVolumeFact = getFinalVolume()/100.0;
592 std::fill(to, to + nSamples, 0);
594 // call NetStream or Sound audio callbacks
595 if (!_inputStreams.empty()) {
597 // A buffer to fetch InputStream samples into
598 boost::scoped_array<boost::int16_t> buf(new boost::int16_t[nSamples]);
600 #ifdef GNASH_DEBUG_SAMPLES_FETCHING
601 log_debug("Fetching %d samples from each of %d input streams", nSamples, _inputStreams.size());
602 #endif
604 // Loop through the aux streamers sounds
605 for (InputStreams::iterator it=_inputStreams.begin(),
606 end=_inputStreams.end();
607 it != end; ++it)
609 InputStream* is = *it;
611 unsigned int wrote = is->fetchSamples(buf.get(), nSamples);
612 if (wrote < nSamples) {
613 // fill what wasn't written
614 std::fill(buf.get()+wrote, buf.get()+nSamples, 0);
617 #if GNASH_DEBUG_SAMPLES_FETCHING > 1
618 log_debug(" fetched %d/%d samples from input stream %p"
619 " (%d samples fetchehd in total)",
620 wrote, nSamples, is, is->samplesFetched());
621 #endif
623 mix(to, buf.get(), nSamples, finalVolumeFact);
626 unplugCompletedInputStreams();
629 // TODO: move this to base class !
630 if (_wavWriter.get()) {
631 _wavWriter->pushSamples(to, nSamples);
633 // now, mute all audio
634 std::fill(to, to+nSamples, 0);
637 // Now, after having "consumed" all sounds, blank out
638 // the buffer if muted..
639 if (is_muted()) {
640 std::fill(to, to+nSamples, 0);
644 void
645 sound_handler::setAudioDump(const std::string& wavefile)
647 bool wasDumping = (_wavWriter.get() != 0);
649 if (!wavefile.empty()) {
650 _wavWriter.reset(new WAVWriter(wavefile));
653 // TODO: just avoid pausing instead ...
654 if (!wasDumping) {
655 // add a silent stream to the audio pool so that our
656 // output file is homogenous; we actually want silent
657 // wave data when no sounds are playing on the stage
658 attach_aux_streamer(silentStream, (void*) this);
662 bool
663 sound_handler::streamingSound() const
665 if (_inputStreams.empty()) return false;
667 for (StreamingSounds::const_iterator it = _streamingSounds.begin(),
668 e = _streamingSounds.end(); it != e; ++it) {
669 if ((*it)->isPlaying()) return true;
671 return false;
674 int
675 sound_handler::getStreamBlock(int handle) const
677 if (!validHandle(_streamingSounds, handle)) return -1;
678 if (!_streamingSounds[handle]->isPlaying()) return -1;
679 InputStream* i = _streamingSounds[handle]->firstPlayingInstance();
680 if (!i) return -1;
681 return static_cast<StreamingSound*>(i)->currentBlock();
684 void
685 sound_handler::unplugCompletedInputStreams()
688 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
689 log_debug("Scanning %d input streams for completion", _inputStreams.size());
690 #endif
692 for (InputStreams::iterator it = _inputStreams.begin(),
693 end = _inputStreams.end(); it != end;) {
695 InputStream* is = *it;
697 // On EOF, detach
698 if (is->eof()) {
699 // InputStream EOF, detach
700 InputStreams::iterator it2 = it;
701 ++it2; // before we erase it
702 InputStreams::size_type erased = _inputStreams.erase(is);
703 if ( erased != 1 ) {
704 log_error(_("Expected 1 InputStream element, found %d"), erased);
705 abort();
707 it = it2;
709 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
710 log_debug(" Input stream %p reached EOF, unplugging", is);
711 #endif
713 // WARNING! deleting the InputStream here means
714 // a lot of things will happen from a
715 // separate thread. Instead, if we
716 // extend sound_handler interface to
717 // have an unplugCompletedInputStreams
718 // we may call it at heart-beating intervals
719 // and drop any threading paranoia!
720 delete is;
722 // Increment number of sound stop request for the testing framework
723 ++_soundsStopped;
725 else ++it;
729 bool
730 sound_handler::hasInputStreams() const
732 return !_inputStreams.empty();
735 bool
736 sound_handler::is_muted() const
738 // TODO: lock a mutex ?
739 return _muted;
742 void
743 sound_handler::mute()
745 // TODO: lock a mutex ?
746 _muted=true;
749 void
750 sound_handler::unmute()
752 // TODO: lock a mutex ?
753 _muted=false;
756 void
757 sound_handler::reset()
759 // Do not delete sounds on reset or there'd be nothing to play
760 // on restart. For a new SWF, we need a new sound_handler.
761 sound_handler::stop_all_sounds();
764 InputStream*
765 sound_handler::attach_aux_streamer(aux_streamer_ptr ptr, void* owner)
767 assert(owner);
768 assert(ptr);
770 std::auto_ptr<InputStream> newStreamer ( new AuxStream(ptr, owner) );
772 InputStream* ret = newStreamer.get();
774 plugInputStream(newStreamer);
776 return ret;
779 sound_handler::~sound_handler()
781 delete_all_sounds();
782 unplugAllInputStreams();
785 } // gnash.sound namespace
786 } // namespace gnash