2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 // Free Software Foundation, Inc
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.
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
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
47 silentStream(void*, boost::int16_t* stream
, unsigned int len
, bool& atEOF
)
49 std::fill(stream
, stream
+ len
, 0);
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.
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
);
88 StreamingSoundData
* sounddata
= _streamingSounds
[handle
];
90 log_error(_("handle passed to fill_stream_data (%d) "
91 "was deleted"), handle
);
96 ensurePadding(*data
, _mediaHandler
);
98 return sounddata
->append(data
, sampleCount
, seekSamples
);
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.
112 stopEmbedSoundInstances(*sdef
);
113 assert(!sdef
->numPlayingInstances());
119 for (StreamingSounds::iterator i
= _streamingSounds
.begin(),
120 e
= _streamingSounds
.end(); i
!= e
; ++i
)
122 StreamingSoundData
* sdef
= *i
;
124 // Streaming sounds are never deleted.
127 stopEmbedSoundInstances(*sdef
);
128 assert(!sdef
->numPlayingInstances());
132 _streamingSounds
.clear();
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
);
146 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
147 log_debug("deleting sound :%d", handle
);
150 EmbedSound
* def
= _sounds
[handle
];
152 log_error(_("handle passed to delete_sound (%d) "
153 "already deleted"), handle
);
157 stopEmbedSoundInstances(*def
);
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
;
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
;
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
;
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
);
219 StreamingSoundData
* sounddata
= _streamingSounds
[handle
];
222 stopEmbedSoundInstances(*sounddata
);
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
);
234 EmbedSound
* sounddata
= _sounds
[handle
];
236 log_error(_("stop_sound(%d): sound was deleted"), handle
);
240 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
241 log_debug("stop_sound %d called", handle
);
244 stopEmbedSoundInstances(*sounddata
);
249 sound_handler::stopAllEventSounds()
251 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
252 log_debug("stopAllEventSounds called");
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
);
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();
278 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
279 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i
);
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();
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();
305 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
306 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i
);
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();
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. "),
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
335 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
336 log_debug("Unplugged InputStream %p", id
);
339 // Delete the InputStream (we own it..)
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
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
;
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());
401 sound_handler::create_sound(std::auto_ptr
<SimpleBuffer
> data
,
402 const media::SoundInfo
& sinfo
)
405 ensurePadding(*data
, _mediaHandler
);
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(),
423 // the vector takes ownership
424 _sounds
.push_back(sounddata
.release());
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();
443 sound_handler::playStream(int soundId
, StreamBlockId blockId
)
445 StreamingSoundData
& s
= *_streamingSounds
[soundId
];
446 if (s
.isPlaying() || s
.empty()) return;
449 std::auto_ptr
<InputStream
> is(
450 s
.createInstance(*_mediaHandler
, blockId
));
453 catch (const MediaException
& e
) {
454 log_error(_("Could not start streaming sound: %s"), e
.what());
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
);
471 EmbedSound
& sounddata
= *(_sounds
[handle
]);
472 const media::SoundInfo
& sinfo
= sounddata
.soundinfo
;
474 const int swfDelaySeek
= sinfo
.getDelaySeek();
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
491 // loop1 *****************
492 // loop2 ----------------*****************
493 // loop3 ----------------*****************
496 // loop1 *****************
497 // loop2 *****************
498 // loop3 *****************
500 LOG_ONCE(log_unimpl("MP3 delaySeek"));
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
;
512 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
513 log_debug("startSound %d called, SoundInfo format is %s",
514 handle
, sounddata
.soundinfo
.getFormat());
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");
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"));
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
,
542 plugInputStream(sound
);
544 catch (const MediaException
& e
) {
545 log_error(_("Could not start event sound: %s"), e
.what());
551 sound_handler::plugInputStream(std::auto_ptr
<InputStream
> newStreamer
)
553 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
554 InputStream
* newStream
= newStreamer
.get(); // for debugging
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 !
565 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
566 log_debug("Plugged InputStream %p", newStream
);
569 // Increment number of sound start request for the testing framework
574 sound_handler::unplugAllInputStreams()
576 for (InputStreams::iterator it
=_inputStreams
.begin(),
577 itE
=_inputStreams
.end();
582 _inputStreams
.clear();
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());
604 // Loop through the aux streamers sounds
605 for (InputStreams::iterator it
=_inputStreams
.begin(),
606 end
=_inputStreams
.end();
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());
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..
640 std::fill(to
, to
+nSamples
, 0);
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 ...
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);
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;
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();
681 return static_cast<StreamingSound
*>(i
)->currentBlock();
685 sound_handler::unplugCompletedInputStreams()
688 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
689 log_debug("Scanning %d input streams for completion", _inputStreams
.size());
692 for (InputStreams::iterator it
= _inputStreams
.begin(),
693 end
= _inputStreams
.end(); it
!= end
;) {
695 InputStream
* is
= *it
;
699 // InputStream EOF, detach
700 InputStreams::iterator it2
= it
;
701 ++it2
; // before we erase it
702 InputStreams::size_type erased
= _inputStreams
.erase(is
);
704 log_error(_("Expected 1 InputStream element, found %d"), erased
);
709 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
710 log_debug(" Input stream %p reached EOF, unplugging", is
);
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!
722 // Increment number of sound stop request for the testing framework
730 sound_handler::hasInputStreams() const
732 return !_inputStreams
.empty();
736 sound_handler::is_muted() const
738 // TODO: lock a mutex ?
743 sound_handler::mute()
745 // TODO: lock a mutex ?
750 sound_handler::unmute()
752 // TODO: lock a mutex ?
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();
765 sound_handler::attach_aux_streamer(aux_streamer_ptr ptr
, void* owner
)
770 std::auto_ptr
<InputStream
> newStreamer ( new AuxStream(ptr
, owner
) );
772 InputStream
* ret
= newStreamer
.get();
774 plugInputStream(newStreamer
);
779 sound_handler::~sound_handler()
782 unplugAllInputStreams();
785 } // gnash.sound namespace