Make automated FSCommand invocation tests show player-side output.
[gnash.git] / libsound / sound_handler.cpp
blob744bcccf3dd76b02c0e4e50590aef4c3bcf40549
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
3 // 2015, 2016 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 <cstdint> // For C99 int types
23 #include <vector>
24 #include <cmath>
26 #include "EmbedSound.h" // for use
27 #include "InputStream.h" // for use
28 #include "EmbedSoundInst.h" // for upcasting to InputStream
29 #include "log.h" // for use
30 #include "StreamingSound.h"
31 #include "StreamingSoundData.h"
32 #include "SimpleBuffer.h"
33 #include "MediaHandler.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*, std::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 /* The volume ranges from 0 - 128 */
77 #define MIX_MAXVOLUME 128
78 #define ADJUST_VOLUME(s, v) (s = (s*v)/MIX_MAXVOLUME)
80 void
81 mixAudio(std::uint8_t *dst, const std::uint8_t *src, std::uint32_t len, int volume)
83 if ( volume == 0 ) return;
85 union {
86 int16_t i;
87 char c[2];
88 } isbig;
89 isbig.i = 1;
91 int lsb = ( isbig.c[0] == 1 );
93 std::int16_t src1, src2;
94 int dst_sample;
95 const int max_audioval = ((1<<(16-1))-1);
96 const int min_audioval = -(1<<(16-1));
98 if ( lsb )
100 // AUDIO_S16LSB
101 len /= 2;
102 while ( len-- ) {
103 src1 = ((src[1])<<8|src[0]);
104 ADJUST_VOLUME(src1, volume);
105 src2 = ((dst[1])<<8|dst[0]);
106 src += 2;
107 dst_sample = src1+src2;
108 if ( dst_sample > max_audioval ) {
109 dst_sample = max_audioval;
110 } else
111 if ( dst_sample < min_audioval ) {
112 dst_sample = min_audioval;
114 dst[0] = dst_sample&0xFF;
115 dst_sample >>= 8;
116 dst[1] = dst_sample&0xFF;
117 dst += 2;
120 else
122 // AUDIO_S16MSB
123 len /= 2;
124 while ( len-- ) {
125 src1 = ((src[0])<<8|src[1]);
126 ADJUST_VOLUME(src1, volume);
127 src2 = ((dst[0])<<8|dst[1]);
128 src += 2;
129 dst_sample = src1+src2;
130 if ( dst_sample > max_audioval ) {
131 dst_sample = max_audioval;
132 } else
133 if ( dst_sample < min_audioval ) {
134 dst_sample = min_audioval;
136 dst[1] = dst_sample&0xFF;
137 dst_sample >>= 8;
138 dst[0] = dst_sample&0xFF;
139 dst += 2;
144 } // anonymous namespace
146 sound_handler::StreamBlockId
147 sound_handler::addSoundBlock(SimpleBuffer data,
148 size_t sampleCount, int seekSamples, int handle)
150 if (!validHandle(_streamingSounds, handle)) {
151 log_error(_("Invalid (%d) handle passed to fill_stream_data, "
152 "doing nothing"), handle);
153 return -1;
156 StreamingSoundData* sounddata = _streamingSounds[handle];
157 if (!sounddata) {
158 log_error(_("handle passed to fill_stream_data (%d) "
159 "was deleted"), handle);
160 return -1;
163 ensurePadding(data, _mediaHandler);
165 return sounddata->append(std::move(data), sampleCount, seekSamples);
168 void
169 sound_handler::delete_all_sounds()
171 for (EmbedSound* sdef : _sounds)
173 // The sound may have been deleted already.
174 if (!sdef) continue;
176 stopEmbedSoundInstances(*sdef);
177 assert(!sdef->numPlayingInstances());
179 delete sdef;
181 _sounds.clear();
183 for (StreamingSoundData* sdef : _streamingSounds)
185 // Streaming sounds are never deleted.
186 assert(sdef);
188 stopEmbedSoundInstances(*sdef);
189 assert(!sdef->numPlayingInstances());
191 delete sdef;
193 _streamingSounds.clear();
197 void
198 sound_handler::delete_sound(int handle)
200 // Check if the sound exists
201 if (!validHandle(_sounds, handle)) {
202 log_error(_("Invalid (%d) handle passed to delete_sound, "
203 "doing nothing"), handle);
204 return;
207 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
208 log_debug("deleting sound :%d", handle);
209 #endif
211 EmbedSound* def = _sounds[handle];
212 if (!def) {
213 log_error(_("handle passed to delete_sound (%d) "
214 "already deleted"), handle);
215 return;
218 stopEmbedSoundInstances(*def);
219 delete def;
220 _sounds[handle] = nullptr;
224 void
225 sound_handler::stop_all_sounds()
227 for (EmbedSound* sounddata : _sounds)
229 if ( ! sounddata ) continue; // could have been deleted already
230 stopEmbedSoundInstances(*sounddata);
233 for (StreamingSoundData* sounddata : _streamingSounds)
235 if (!sounddata) continue;
236 stopEmbedSoundInstances(*sounddata);
241 sound_handler::get_volume(int handle) const
243 if (validHandle(_sounds, handle)) return _sounds[handle]->volume;
245 // Invalid handle.
246 return 0;
249 void
250 sound_handler::set_volume(int handle, int volume)
252 // Set volume for this sound.
253 // Should this only apply to the active sounds?
254 if (validHandle(_sounds, handle)) _sounds[handle]->volume = volume;
257 media::SoundInfo*
258 sound_handler::get_sound_info(int handle) const
260 // Check if the sound exists.
261 if (validHandle(_streamingSounds, handle)) {
262 return &_streamingSounds[handle]->soundinfo;
264 return nullptr;
267 void
268 sound_handler::stopStreamingSound(int handle)
270 // Check if the sound exists.
271 if (!validHandle(_streamingSounds, handle)) {
272 log_debug("stop_sound(%d): invalid sound id", handle);
273 return;
276 StreamingSoundData* sounddata = _streamingSounds[handle];
277 assert(sounddata);
279 stopEmbedSoundInstances(*sounddata);
282 void
283 sound_handler::stopEventSound(int handle)
285 // Check if the sound exists.
286 if (!validHandle(_sounds, handle)) {
287 log_debug("stop_sound(%d): invalid sound id", handle);
288 return;
291 EmbedSound* sounddata = _sounds[handle];
292 if (!sounddata) {
293 log_error(_("stop_sound(%d): sound was deleted"), handle);
294 return;
297 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
298 log_debug("stop_sound %d called", handle);
299 #endif
301 stopEmbedSoundInstances(*sounddata);
305 void
306 sound_handler::stopAllEventSounds()
308 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
309 log_debug("stopAllEventSounds called");
310 #endif
312 for (EmbedSound* sounddata : _sounds)
314 if (!sounddata) continue; // possible ?
316 stopEmbedSoundInstances(*sounddata);
321 void
322 sound_handler::stopEmbedSoundInstances(StreamingSoundData& def)
324 // Assert _mutex is locked ...
325 typedef std::vector<InputStream*> InputStreamVect;
326 InputStreamVect playing;
327 def.getPlayingInstances(playing);
329 // Now, for each playing InputStream, unplug it!
330 // NOTE: could be optimized...
331 for (InputStream* stream : playing)
333 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
334 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", stream);
335 #endif
337 // Explicitly calling the base class implementation
338 // is a (dirty?) way to avoid mutex-locking overrides
339 // in subclasses causing deadlocks.
340 sound_handler::unplugInputStream(stream);
343 def.clearInstances();
347 void
348 sound_handler::stopEmbedSoundInstances(EmbedSound& def)
350 // Assert _mutex is locked ...
351 typedef std::vector<InputStream*> InputStreamVect;
352 InputStreamVect playing;
353 def.getPlayingInstances(playing);
355 // Now, for each playing InputStream, unplug it!
356 // NOTE: could be optimized...
357 for (InputStream* stream : playing)
359 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
360 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", stream);
361 #endif
363 // Explicitly calling the base class implementation
364 // is a (dirty?) way to avoid mutex-locking overrides
365 // in subclasses causing deadlocks.
366 sound_handler::unplugInputStream(stream);
369 def.clearInstances();
372 void
373 sound_handler::unplugInputStream(InputStream* id)
375 // WARNING: erasing would break any iteration in the set
376 InputStreams::iterator it2=_inputStreams.find(id);
377 if (it2 == _inputStreams.end()) {
378 log_error(_("sound_handler::unplugInputStream: "
379 "Aux streamer %p not found. "),
380 id);
381 return; // we won't delete it, as it's likely deleted already
384 _inputStreams.erase(it2);
386 // Increment number of sound stop request for the testing framework
387 _soundsStopped++;
389 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
390 log_debug("Unplugged InputStream %p", id);
391 #endif
393 // Delete the InputStream (we own it..)
394 delete id;
397 unsigned int
398 sound_handler::tell(int handle) const
400 // Check if the sound exists.
401 if (!validHandle(_sounds, handle)) return 0;
403 const EmbedSound* sounddata = _sounds[handle];
405 // If there is no active sounds, return 0
406 if (!sounddata->isPlaying()) return 0;
408 // We use the first active sound of this.
409 const InputStream* asound = sounddata->firstPlayingInstance();
411 // Return the playhead position in milliseconds
412 unsigned int samplesPlayed = asound->samplesFetched();
414 unsigned int ret = samplesPlayed / 44100 * 1000;
415 ret += ((samplesPlayed % 44100) * 1000) / 44100;
416 ret = ret / 2; // 2 channels
417 return ret;
420 unsigned int
421 sound_handler::get_duration(int handle) const
423 // Check if the sound exists.
424 if (!validHandle(_sounds, handle)) return 0;
426 const EmbedSound* sounddata = _sounds[handle];
428 const std::uint32_t sampleCount = sounddata->soundinfo.getSampleCount();
429 const std::uint32_t sampleRate = sounddata->soundinfo.getSampleRate();
431 // Return the sound duration in milliseconds
432 if (sampleCount > 0 && sampleRate > 0) {
433 // TODO: should we cache this in the EmbedSound object ?
434 unsigned int ret = sampleCount / sampleRate * 1000;
435 ret += ((sampleCount % sampleRate) * 1000) / sampleRate;
436 return ret;
438 return 0;
442 sound_handler::createStreamingSound(const media::SoundInfo& sinfo)
444 std::unique_ptr<StreamingSoundData> sounddata(
445 new StreamingSoundData(sinfo, 100));
447 int sound_id = _streamingSounds.size();
448 // the vector takes ownership
449 _streamingSounds.push_back(sounddata.release());
451 return sound_id;
455 sound_handler::create_sound(std::unique_ptr<SimpleBuffer> data,
456 const media::SoundInfo& sinfo)
458 if (data.get()) {
459 ensurePadding(*data, _mediaHandler);
461 else {
462 log_debug("Event sound with no data!");
464 std::unique_ptr<EmbedSound> sounddata(new EmbedSound(std::move(data), sinfo, 100));
466 int sound_id = _sounds.size();
468 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
469 log_debug("create_sound: sound %d, format %s %s %dHz, %d samples (%d bytes)",
470 sound_id, sounddata->soundinfo.getFormat(),
471 sounddata->soundinfo.isStereo() ? "stereo" : "mono",
472 sounddata->soundinfo.getSampleRate(),
473 sounddata->soundinfo.getSampleCount(),
474 sounddata->size());
475 #endif
477 // the vector takes ownership
478 _sounds.push_back(sounddata.release());
480 return sound_id;
484 bool
485 sound_handler::isSoundPlaying(int handle) const
487 if (!validHandle(_sounds, handle)) return false;
489 EmbedSound& sounddata = *(_sounds[handle]);
491 // When this is called from a StreamSoundBlockTag,
492 // we only start if this sound isn't already playing.
493 return sounddata.isPlaying();
496 void
497 sound_handler::playStream(int soundId, StreamBlockId blockId)
499 StreamingSoundData& s = *_streamingSounds[soundId];
500 if (s.isPlaying() || s.empty()) return;
502 try {
503 if (!_mediaHandler) {
504 throw MediaException("No media handler available");
507 std::unique_ptr<InputStream> is(
508 s.createInstance(*_mediaHandler, blockId));
509 plugInputStream(std::move(is));
511 catch (const MediaException& e) {
512 log_error(_("Could not start streaming sound: %s"), e.what());
516 void
517 sound_handler::startSound(int handle, int loops, const SoundEnvelopes* env,
518 bool allowMultiple, unsigned int inPoint,
519 unsigned int outPoint)
521 // Check if the sound exists
522 if (!validHandle(_sounds, handle)) {
523 log_error(_("Invalid (%d) sound_handle passed to startSound, "
524 "doing nothing"), handle);
525 return;
528 // Handle delaySeek
529 EmbedSound& sounddata = *(_sounds[handle]);
530 const media::SoundInfo& sinfo = sounddata.soundinfo;
532 const int swfDelaySeek = sinfo.getDelaySeek();
533 if (swfDelaySeek) {
534 // NOTE: differences between delaySeek and inPoint:
536 // - Sample count semantic:
537 // inPoint uses output sample rate (44100 for one second)
538 // while delaySeek uses source sample rate
539 // (SoundInfo.getSampleRate() for one second)
541 // - Loop-back semantic:
542 // An event sound always loops-back from inPoint with no gaps
543 // When delaySeek is specified it is still used as a start
544 // for the initial playback but when it comes to looping back
545 // it seems to play silence instead of samples for the amount
546 // skipped:
548 // [ delaySeekTime ]
549 // loop1 *****************
550 // loop2 ----------------*****************
551 // loop3 ----------------*****************
553 // [ inPoint ]
554 // loop1 *****************
555 // loop2 *****************
556 // loop3 *****************
558 LOG_ONCE(log_unimpl("MP3 delaySeek"));
559 #if 0
560 unsigned int outDelaySeek = swfToOutSamples(sinfo, swfDelaySeek);
562 log_debug("inPoint(%d) + delaySeek(%d -> %d) == %d",
563 inPoint, swfDelaySeek, outDelaySeek,
564 inPoint+outDelaySeek);
566 inPoint += outDelaySeek;
567 #endif
570 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
571 log_debug("startSound %d called, SoundInfo format is %s",
572 handle, sounddata.soundinfo.getFormat());
573 #endif
575 // When this is called from a StreamSoundBlockTag,
576 // we only start if this sound isn't already playing.
577 if (!allowMultiple && sounddata.isPlaying()) {
578 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
579 log_debug(" playSound: multiple instances not allowed, "
580 "and sound is already playing");
581 #endif
582 return;
585 // Make sure sound actually got some data
586 if (sounddata.empty()) {
587 // @@ should this be a log_error ? or even an assert ?
588 IF_VERBOSE_MALFORMED_SWF(
589 log_swferror(_("Trying to play sound with size 0"));
591 return;
594 try {
595 if (!_mediaHandler) {
596 throw MediaException("No media handler available");
599 // Make an InputStream for this sound and plug it into
600 // the set of InputStream channels
601 std::unique_ptr<InputStream> sound(
602 sounddata.createInstance(*_mediaHandler, inPoint, outPoint,
603 env, loops));
604 plugInputStream(std::move(sound));
606 catch (const MediaException& e) {
607 log_error(_("Could not start event sound: %s"), e.what());
612 void
613 sound_handler::plugInputStream(std::unique_ptr<InputStream> newStreamer)
615 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
616 InputStream* newStream = newStreamer.get(); // for debugging
617 #endif
619 if (!_inputStreams.insert(newStreamer.release()).second) {
620 // this should never happen !
621 log_error(_("_inputStreams container still has a pointer "
622 "to deleted InputStream %p!"), newStreamer.get());
623 // FIXME: replace the old element with the new one !
624 abort();
627 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
628 log_debug("Plugged InputStream %p", newStream);
629 #endif
631 // Increment number of sound start request for the testing framework
632 ++_soundsStarted;
635 void
636 sound_handler::unplugAllInputStreams()
638 for (const InputStream* stream : _inputStreams)
640 delete stream;
642 _inputStreams.clear();
645 void
646 sound_handler::fetchSamples(std::int16_t* to, unsigned int nSamples)
648 if (isPaused()) return; // should we write wav file anyway ?
650 float finalVolumeFact = getFinalVolume()/100.0;
652 std::fill(to, to + nSamples, 0);
654 // call NetStream or Sound audio callbacks
655 if (!_inputStreams.empty()) {
657 // A buffer to fetch InputStream samples into
658 std::unique_ptr<std::int16_t[]> buf(new std::int16_t[nSamples]);
660 #ifdef GNASH_DEBUG_SAMPLES_FETCHING
661 log_debug("Fetching %d samples from each of %d input streams", nSamples, _inputStreams.size());
662 #endif
664 // Loop through the aux streamers sounds
665 for (InputStream* is : _inputStreams)
667 unsigned int wrote = is->fetchSamples(buf.get(), nSamples);
668 if (wrote < nSamples) {
669 // fill what wasn't written
670 std::fill(buf.get()+wrote, buf.get()+nSamples, 0);
673 #if GNASH_DEBUG_SAMPLES_FETCHING > 1
674 log_debug(" fetched %d/%d samples from input stream %p"
675 " (%d samples fetchehd in total)",
676 wrote, nSamples, is, is->samplesFetched());
677 #endif
679 mix(to, buf.get(), nSamples, finalVolumeFact);
682 unplugCompletedInputStreams();
685 // TODO: move this to base class !
686 if (_wavWriter.get()) {
687 _wavWriter->pushSamples(to, nSamples);
689 // now, mute all audio
690 std::fill(to, to+nSamples, 0);
693 // Now, after having "consumed" all sounds, blank out
694 // the buffer if muted..
695 if (is_muted()) {
696 std::fill(to, to+nSamples, 0);
700 void
701 sound_handler::setAudioDump(const std::string& wavefile)
703 bool wasDumping = (_wavWriter.get() != nullptr);
705 if (!wavefile.empty()) {
706 _wavWriter.reset(new WAVWriter(wavefile));
709 // TODO: just avoid pausing instead ...
710 if (!wasDumping) {
711 // add a silent stream to the audio pool so that our
712 // output file is homogenous; we actually want silent
713 // wave data when no sounds are playing on the stage
714 attach_aux_streamer(silentStream, (void*) this);
718 bool
719 sound_handler::streamingSound() const
721 if (_inputStreams.empty()) return false;
723 for (StreamingSoundData* const stream : _streamingSounds) {
724 if (stream->isPlaying()) return true;
726 return false;
729 int
730 sound_handler::getStreamBlock(int handle) const
732 if (!validHandle(_streamingSounds, handle)) return -1;
733 if (!_streamingSounds[handle]->isPlaying()) return -1;
734 InputStream* i = _streamingSounds[handle]->firstPlayingInstance();
735 if (!i) return -1;
736 return static_cast<StreamingSound*>(i)->currentBlock();
739 void
740 sound_handler::unplugCompletedInputStreams()
743 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
744 log_debug("Scanning %d input streams for completion", _inputStreams.size());
745 #endif
747 for (InputStreams::iterator it = _inputStreams.begin(),
748 end = _inputStreams.end(); it != end;) {
750 InputStream* is = *it;
752 // On EOF, detach
753 if (is->eof()) {
754 // InputStream EOF, detach
755 InputStreams::iterator it2 = it;
756 ++it2; // before we erase it
757 InputStreams::size_type erased = _inputStreams.erase(is);
758 if ( erased != 1 ) {
759 log_error(_("Expected 1 InputStream element, found %d"), erased);
760 abort();
762 it = it2;
764 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
765 log_debug(" Input stream %p reached EOF, unplugging", is);
766 #endif
768 // WARNING! deleting the InputStream here means
769 // a lot of things will happen from a
770 // separate thread. Instead, if we
771 // extend sound_handler interface to
772 // have an unplugCompletedInputStreams
773 // we may call it at heart-beating intervals
774 // and drop any threading paranoia!
775 delete is;
777 // Increment number of sound stop request for the testing framework
778 ++_soundsStopped;
780 else ++it;
784 bool
785 sound_handler::hasInputStreams() const
787 return !_inputStreams.empty();
790 bool
791 sound_handler::is_muted() const
793 // TODO: lock a mutex ?
794 return _muted.load();
797 void
798 sound_handler::mute()
800 // TODO: lock a mutex ?
801 _muted=true;
804 void
805 sound_handler::unmute()
807 // TODO: lock a mutex ?
808 _muted=false;
811 void
812 sound_handler::reset()
814 // Do not delete sounds on reset or there'd be nothing to play
815 // on restart. For a new SWF, we need a new sound_handler.
816 sound_handler::stop_all_sounds();
819 InputStream*
820 sound_handler::attach_aux_streamer(aux_streamer_ptr ptr, void* owner)
822 assert(owner);
823 assert(ptr);
825 std::unique_ptr<InputStream> newStreamer ( new AuxStream(ptr, owner) );
827 InputStream* ret = newStreamer.get();
829 plugInputStream(std::move(newStreamer));
831 return ret;
834 sound_handler::~sound_handler()
836 delete_all_sounds();
837 unplugAllInputStreams();
840 void
841 sound_handler::mix(std::int16_t* outSamples, std::int16_t* inSamples, unsigned int nSamples, float volume)
843 unsigned int nBytes = nSamples*2;
845 std::uint8_t *out = reinterpret_cast<std::uint8_t*>(outSamples);
846 std::uint8_t* in = reinterpret_cast<std::uint8_t*>(inSamples);
848 mixAudio(out, in, nBytes, static_cast<int>(MIX_MAXVOLUME*volume));
851 } // gnash.sound namespace
852 } // namespace gnash