2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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"
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
39 unsigned int silentStream(void*, boost::int16_t* stream
, unsigned int len
, bool& atEOF
)
41 std::fill(stream
, stream
+len
, 0);
52 sound_handler::StreamBlockId
53 sound_handler::addSoundBlock(unsigned char* data
,
54 unsigned int data_bytes
, unsigned int /*sample_count*/,
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
);
67 EmbedSound
* sounddata
= _sounds
[handleId
];
70 log_error("sound_handle passed to fill_stream_data (%d) "
71 "was deleted", handleId
);
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
);
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.
98 stopEmbedSoundInstances(*sdef
);
99 assert(!sdef
->numPlayingInstances());
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
);
117 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
118 log_debug ("deleting sound :%d", sound_handle
);
121 EmbedSound
* def
= _sounds
[sound_handle
];
124 log_error("sound_handle passed to delete_sound (%d) "
125 "already deleted", sound_handle
);
129 stopEmbedSoundInstances(*def
);
131 _sounds
[sound_handle
] = 0;
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
)
151 // Check if the sound exists.
152 if (soundHandle
>= 0 && static_cast<unsigned int>(soundHandle
) < _sounds
.size())
154 ret
= _sounds
[soundHandle
]->volume
;
156 ret
= 0; // Invalid handle
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())
170 // Set volume for this sound. Should this only apply to the active sounds?
171 _sounds
[sound_handle
]->volume
= volume
;
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();
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
);
201 EmbedSound
* sounddata
= _sounds
[sound_handle
];
204 log_error("stop_sound(%d): sound was deleted", sound_handle
);
208 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
209 log_debug("stop_sound %d called", sound_handle
);
212 stopEmbedSoundInstances(*sounddata
);
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();
230 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
231 log_debug(" unplugging input stream %p from stopEmbedSoundInstances", *i
);
234 unplugInputStream(*i
);
237 def
.clearInstances();
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. ",
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
258 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
259 log_debug("Unplugged InputStream %p", id
);
262 // Delete the InputStream (we own it..)
267 sound_handler::tell(int sound_handle
)
269 // Check if the sound exists.
270 if (sound_handle
< 0 || (unsigned int) sound_handle
>= _sounds
.size())
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
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())
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;
321 sound_handler::create_sound(std::auto_ptr
<SimpleBuffer
> data
,
322 std::auto_ptr
<media::SoundInfo
> sinfo
)
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(),
341 // the vector takes ownership
342 _sounds
.push_back(sounddata
.release());
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 !
372 log_debug("NOTE: isStereo?%d is16bit()?%d sampleRate?%d",
373 sinfo
.isStereo(), sinfo
.is16bit(), sinfo
.getSampleRate());
384 sound_handler::playSound(int sound_handle
,
385 int loopCount
, unsigned int inPoint
, unsigned int outPoint
,
386 StreamBlockId blockId
, const SoundEnvelopes
* envelopes
,
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());
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
);
406 // log_debug("Stream sound block play request, "
407 // "but an instance of the stream is "
408 // "already playing, so we do nothing");
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"));
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
430 // Sound block identifier
436 // Volume envelopes to use for this instance
444 plugInputStream(sound
);
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);
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
466 static_cast<unsigned int>(soundId
) >= _sounds
.size())
468 log_error("Invalid (%d) sound_handle passed to startSound, "
469 "doing nothing", soundId
);
476 EmbedSound
& sounddata
= *(_sounds
[soundId
]);
477 const media::SoundInfo
& sinfo
= *(sounddata
.soundinfo
);
479 int swfDelaySeek
= sinfo
.getDelaySeek();
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
497 // loop1 *****************
498 // loop2 ----------------*****************
499 // loop3 ----------------*****************
502 // loop1 *****************
503 // loop2 *****************
504 // loop3 *****************
506 LOG_ONCE(log_unimpl("MP3 delaySeek"));
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
;
518 playSound(soundId
, loops
, inPoint
, outPoint
, 0, env
, allowMultiple
);
522 sound_handler::plugInputStream(std::auto_ptr
<InputStream
> newStreamer
)
524 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
525 InputStream
* newStream
= newStreamer
.get(); // for debugging
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 !
537 #ifdef GNASH_DEBUG_SOUNDS_MANAGEMENT
538 log_debug("Plugged InputStream %p", newStream
);
541 // Increment number of sound start request for the testing framework
546 sound_handler::unplugAllInputStreams()
548 for (InputStreams::iterator it
=_inputStreams
.begin(),
549 itE
=_inputStreams
.end();
554 _inputStreams
.clear();
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());
576 // Loop through the aux streamers sounds
577 for (InputStreams::iterator it
=_inputStreams
.begin(),
578 end
=_inputStreams
.end();
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());
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..
615 std::fill(to
, to
+nSamples
, 0);
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);
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());
651 InputStream
* is
= *it
;
656 // InputStream EOF, detach
657 InputStreams::iterator it2
=it
;
658 ++it2
; // before we erase it
659 InputStreams::size_type erased
= _inputStreams
.erase(is
);
661 log_error("Expected 1 InputStream element, found %d", erased
);
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
);
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!
680 // Increment number of sound stop request for the testing framework
691 sound_handler::hasInputStreams() const
693 return !_inputStreams
.empty();
697 sound_handler::is_muted() const
699 // TODO: lock a mutex ?
704 sound_handler::mute()
706 // TODO: lock a mutex ?
711 sound_handler::unmute()
713 // TODO: lock a mutex ?
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();
726 sound_handler::attach_aux_streamer(aux_streamer_ptr ptr
, void* owner
)
731 std::auto_ptr
<InputStream
> newStreamer ( new AuxStream(ptr
, owner
) );
733 InputStream
* ret
= newStreamer
.get();
735 plugInputStream(newStreamer
);
740 } // gnash.sound namespace