/* [<][>][^][v][top][bottom][index][help] */
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
// 2011 Free Software Foundation, Inc
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef SOUND_HANDLER_H
#define SOUND_HANDLER_H
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
#endif
#include "dsodefs.h" // for DSOEXPORT
#include "MediaHandler.h" // for inlined ctor
#include "SoundEnvelope.h" // for SoundEnvelopes typedef
#include "AuxStream.h" // for aux_stramer_ptr typedef
#include "WAVWriter.h" // for dtor visibility
#include <string>
#include <vector>
#include <memory>
#include <cassert>
#include <cstring>
#include <limits>
#include <set> // for composition
#include <boost/scoped_ptr.hpp>
namespace gnash {
namespace media {
class SoundInfo;
}
namespace sound {
class EmbedSound;
class InputStream;
}
class SimpleBuffer;
}
namespace gnash {
/// Gnash %sound handling subsystem (libsound)
//
/// This subsystem takes care of mixing audio
/// and communicating to the system mixer.
///
namespace sound {
/// %Sound mixer.
//
/// Stores the audio found by the parser and plays on demand.
/// Also allows plugging auxiliary streamers for ActionScript
/// driven %sound (Sound, NetStream).
///
/// The main interface this class exposes to hosting application
/// is fetching of sound samples, in couples of signed 16bit integers
/// (left channel, right channel) in PCM.
/// See fetchSamples.
///
/// Implementations of this class willing to allow
/// hosting application to use a thread for fetching
/// audio should make sure to take care about mutex
/// protecting the relevant datastores.
/// The default implementation uses NO locking.
///
/// @todo rename to gnash::sound::Mixer ?
///
class DSOEXPORT sound_handler
{
public:
/// Identifier of a streaming sound block
//
/// Use coupled with a soundId for fully qualified identifier
///
typedef unsigned long StreamBlockId;
/// Create a sound buffer slot, for on-demand playback.
//
/// @param data
/// The data to be stored. For soundstream this is NULL.
/// The data is in encoded format, with format specified
/// with the sinfo parameter, this is to allow on-demand
/// decoding (if the sound is never played, it's never decoded).
///
/// @param sinfo
/// A SoundInfo object contained in an auto_ptr, which contains
/// info about samplerate, samplecount, stereo and more.
/// The SoundObject must be not-NULL!
///
/// @return the id given by the soundhandler for later identification.
///
virtual int create_sound(
std::auto_ptr<SimpleBuffer> data,
std::auto_ptr<media::SoundInfo> sinfo
);
/// Append data to an existing sound buffer slot.
//
///
/// Gnash's parser calls this to fill up soundstreams data.
/// TODO: the current code uses memory reallocation to grow the sound,
/// which is suboptimal; instead, we should maintain sound sources as a
/// list of buffers, to avoid reallocations.
///
/// @param data
/// The sound data to be saved, allocated by new[].
/// Ownership is transferred.
/// TODO: use SimpleBuffer ?
///
/// @param dataBytes
/// Size of the data in bytes
///
/// @param sampleCount
/// Number of samples in the data
///
/// @param streamId
/// The soundhandlers id of the sound we want to add data to
///
/// @return an identifier for the new block for use in playSound
///
/// @throw SoundException on error
///
virtual StreamBlockId addSoundBlock(unsigned char* data,
unsigned int dataBytes,
unsigned int sampleCount,
int streamId);
/// \brief
/// Returns a pointer to the SoundInfo object for the sound
/// with the given id.
//
/// The SoundInfo object is still owned by the soundhandler.
///
/// @param soundHandle
/// The soundhandlers id of the sound we want some info about.
///
/// @return a pointer to the SoundInfo object for the sound with
/// the given id.
///
virtual media::SoundInfo* get_sound_info(int soundHandle);
/// Start playback of an event sound
//
/// All scheduled sounds will be played on next output flush.
///
/// @param id
/// Id of the sound buffer slot schedule playback of.
///
/// @param loops
/// loops == 0 means play the sound once (1 means play it twice, etc)
///
/// @param inPoint
/// Offset in output samples this instance should start
/// playing from. These are post-resampling samples (44100
/// for one second of samples).
///
/// @param outPoint
/// Offset in output samples this instance should stop
/// playing at. These are post-resampling samples (44100
/// for one second of samples).
/// Use std::numeric_limits<unsigned int>::max() for no limit
/// (default if missing)
///
/// @param env
/// Some eventsounds have some volume control mechanism called
/// envelopes.
/// They basically tells that from sample X the volume should be Y.
///
/// @param allowMultiple
/// If false, the sound will not be scheduled if there's another
/// instance of it already playing.
///
///
void startSound(int id, int loops,
const SoundEnvelopes* env,
bool allowMultiple, unsigned int inPoint=0,
unsigned int outPoint=std::numeric_limits<unsigned int>::max());
/// Start playback of a streaming sound, if not playing already
//
///
/// @param streamId
/// Id of the sound buffer slot schedule playback of.
/// It is assumed to refer to a straming sound
///
/// @param blockId
/// Identifier of the block to start decoding from.
///
void playStream(int id, StreamBlockId blockId);
/// Remove all scheduled request for playback of sound buffer slots
virtual void stop_all_sounds();
/// Gets the volume for a given sound buffer slot.
//
/// Only used by the AS Sound class
///
/// @param sound_handle
/// The sound_handlers id for the sound to be deleted
///
/// @return the sound volume level as an integer from 0 to 100,
/// where 0 is off and 100 is full volume. The default setting is 100.
///
virtual int get_volume(int sound_handle);
/// Get the volume to apply to mixed output
//
/// @return percent value. 100 is full, 0 is none.
/// Can be negative or over 100 too.
///
int getFinalVolume() { return _volume; }
/// Sets the volume for a given sound buffer slot.
//
/// Only used by the AS Sound class
///
/// @param sound_handle
/// The sound_handlers id for the sound to be deleted
///
/// @param volume
/// A number from 0 to 100 representing a volume level.
/// 100 is full volume and 0 is no volume.
/// The default setting is 100.
///
virtual void set_volume(int sound_handle, int volume);
/// Set the volume to apply to mixed output
//
/// @param v percent value. 100 is full, 0 is none.
/// Can be negative or over 100 too.
///
void setFinalVolume(int v) { _volume=v; }
/// Remove scheduled requests to play the specified sound buffer slot
//
/// Stop the specified sound if it's playing.
/// (Normally a full-featured sound API would take a
/// handle specifying the *instance* of a playing
/// sample, but SWF is not expressive that way.)
//
/// @param sound_handle
/// The sound_handlers id for the sound to be deleted
///
virtual void stop_sound(int sound_handle);
/// Discard a sound buffer slot
//
/// @param sound_handle
/// The sound_handlers id for the sound to be deleted
///
virtual void delete_sound(int sound_handle);
// Stop and delete all sounds
virtual void delete_all_sounds();
/// \brief
/// Discard all sound inputs (slots and aux streamers)
/// and clear scheduling
//
/// Gnash calls this on movie restart.
///
/// The function should stop all sounds and get ready
/// for a "parse from scratch" operation.
///
virtual void reset();
/// Call this to mute audio
virtual void mute();
/// Call this to unmute audio
virtual void unmute();
/// Returns whether or not sound is muted.
//
/// @return true if muted, false if not
///
virtual bool is_muted() const;
/// gnash calls this to pause audio
virtual void pause() { _paused=true; }
/// gnash calls this to unpause audio
virtual void unpause() { _paused=false; }
/// return true if audio is paused
bool isPaused() const { return _paused; }
/// Plug an external InputStream into the mixer
//
/// This is called by AS classes NetStream or Sound to attach callback, so
/// that audio from the classes will be played through the soundhandler.
///
/// This is actually only used by the SDL sound_handler.
/// It uses these "auxiliary" streamers to fetch decoded audio data
/// to mix and send to the output channel.
///
/// The "aux streamer" will be called with the 'udata' pointer as
/// first argument, then will be passed a pointer to buffer of samples
/// as second argument and the number of samples to fetch as third.
///
/// The callbacks should fill the given buffer if possible, and return
/// the number of samples actually fetched and an eof flag (last argument).
/// The callback will be detached if it sets the 'eof' output parameter
/// to true.
///
/// @param ptr
/// The pointer to the callback function
///
/// @param udata
/// User data pointer, passed as first argument to the registered
/// callback.
///
/// @return an InputStream pointer, for passing to unplugInputStream.
/// Callers do not own the InputStream, and should NOT realy
/// on it to be point to allocated memory! It is meant to be used
/// as an identifier for the newly created mixer channel.
///
/// @todo change to plugInputStream(InputStream* str),
/// implement in base class
///
/// @see unplugInputStream
///
virtual InputStream* attach_aux_streamer(aux_streamer_ptr ptr, void* udata);
/// Unplug an external InputStream from the mixer
//
/// This is called by AS classes NetStream or Sound to dettach callback,
/// so that audio from the classes no longer will be played through the
/// soundhandler.
//
/// @param id
/// The key identifying the auxiliary streamer, as returned
/// by attach_aux_streamer.
///
virtual void unplugInputStream(InputStream* id);
virtual ~sound_handler() {};
/// \brief
/// Gets the duration in milliseconds of an event sound connected
/// to an AS Sound obejct.
//
/// @param sound_handle
/// The id of the event sound
///
/// @return the duration of the sound in milliseconds
///
virtual unsigned int get_duration(int sound_handle);
/// \brief
/// Gets the playhead position in milliseconds of an event sound connected
/// to an AS Sound obejct.
//
/// @param sound_handle
/// The id of the event sound
///
/// @return the duration of the sound in milliseconds
///
virtual unsigned int tell(int sound_handle);
/// Special test-fuction. Reports how many times a sound has been started
//
/// @deprecated Use a TestingSoundHanlder !
///
size_t numSoundsStarted() const { return _soundsStarted; }
/// Special test-fuction. Reports how many times a sound has been stopped
//
/// @deprecated Use a TestingSoundHanlder !
///
size_t numSoundsStopped() const { return _soundsStopped; }
/// Fetch mixed samples
//
/// We run through all the plugged InputStreams fetching decoded
/// audio blocks and mixing them into the given output stream.
///
/// @param to
/// The buffer to write mixed samples to.
/// Buffer must be big enough to hold nSamples samples.
///
/// @param nSamples
/// The amount of samples to fetch.
/// NOTE: this number currently refers to "mono" samples
/// due to some bad design decision. It is so expected
/// that the user fetches 44100 * 2 samples which has to
/// be interpreted as series of left,right channel couples.
/// TODO: use actual number of samples so that it's expected
/// to fetch 44100 per second and let expose a function
/// to give interpretation of what comes back (how many
/// bytes per channel, which format).
///
virtual void fetchSamples(boost::int16_t* to, unsigned int nSamples);
/// Mix nSamples from inSamples to outSamples, with given volume
//
/// @param outSamples
/// The output samples buffer, to mix to.
/// Must be big enough to contain nSamples.
/// Can be larger.
///
/// @param inSamples
/// The input samples buffer, to mix in.
/// It is non-const as this method *is* allowed
/// to mess with content, for example to apply
/// volume.
/// TODO: why not applying volume upstream ?!
///
/// @param nSamples
/// The amount of samples to mix in.
///
/// @param volume
/// The volume to apply to input samples, as a fraction (0..1)
///
/// TODO: this interface says nothing about how many channels we're
/// going to mix. It should, to be a sane interface.
/// Maybe, a Mixer instance should be created, initialized
/// with number of channels, at each fetching.
///
virtual void mix(boost::int16_t* outSamples, boost::int16_t* inSamples,
unsigned int nSamples, float volume) = 0;
/// Request to dump audio to the given filename
//
/// Every call to this function starts recording
/// to a new file, closing any existing other dump.
///
void setAudioDump(const std::string& wavefile);
protected:
sound_handler(media::MediaHandler* m)
:
_soundsStarted(0),
_soundsStopped(0),
_paused(false),
_muted(false),
_volume(100),
_sounds(),
_inputStreams(),
_mediaHandler(m)
{
}
/// Plug an InputStream to the mixer
//
/// @param in
/// The InputStream to plug, ownership transferred
///
virtual void plugInputStream(std::auto_ptr<InputStream> in);
/// Unplug all input streams
virtual void unplugAllInputStreams();
/// Does the mixer have input streams ?
bool hasInputStreams() const;
private:
/// Special test-member. Stores count of started sounds.
size_t _soundsStarted;
/// Special test-member. Stores count of stopped sounds.
size_t _soundsStopped;
/// True if sound is paused
bool _paused;
/// True if sound is muted
bool _muted;
/// Final output volume
int _volume;
typedef std::vector<EmbedSound*> Sounds;
/// Vector containing all sounds.
//
/// Elements of the vector are owned by this class
///
Sounds _sounds;
/// Stop all instances of an embedded sound
void stopEmbedSoundInstances(EmbedSound& def);
typedef std::set< InputStream* > InputStreams;
/// Sound input streams.
//
/// Elements owned by this class.
///
InputStreams _inputStreams;
media::MediaHandler* _mediaHandler;
/// Unplug any completed input stream
void unplugCompletedInputStreams();
/// Schedule playing of a sound buffer slot
//
/// All scheduled sounds will be played on next output flush.
///
/// @param id
/// Id of the sound buffer slot schedule playback of.
///
/// @param loops
/// loops == 0 means play the sound once (1 means play it twice, etc)
///
/// @param inPoint
/// Offset in output samples this instance should start
/// playing from. These are post-resampling samples (44100
/// for one second of samples).
///
/// @param outPoint
/// Offset in output samples this instance should stop
/// playing at. These are post-resampling samples (44100
/// for one second of samples).
///
/// @param blockId
/// When starting a soundstream from a random frame, this tells which
/// block to start decoding from.
/// If non-zero, the sound will only start when no other instances of
/// it are already playing.
///
/// @param env
/// Some eventsounds have some volume control mechanism called
/// envelopes.
/// They basically tells that from sample X the volume should be Y.
///
/// @param allowMultiple
/// If false, the sound will not be scheduled if there's another
/// instance of it already playing.
///
void playSound(int id, int loops,
unsigned int inPoint,
unsigned int outPoint,
StreamBlockId blockId, const SoundEnvelopes* env,
bool allowMultiple);
/// Convert SWF-specified number of samples to output number of samples
//
/// SWF-specified number of samples are: delaySeek in DEFINESOUND,
/// latency in STREAMSOUNDHEAD and seekSamples in STREAMSOUNDBLOCK.
/// These refer to samples at the sampleRate of input.
///
/// As gnash will resample the sounds to match expected output
/// (44100 Hz, stereo 16bit) this function is handy to convert
/// for simpler use later.
///
/// It is non-static in the event we'll one day allow different
/// sound_handler instances to be configured with different output
/// sample rate (would need a lot more changes atm but let's keep
/// that in mind).
///
unsigned int swfToOutSamples(const media::SoundInfo& sinfo,
unsigned int swfSamples);
boost::scoped_ptr<WAVWriter> _wavWriter;
};
// TODO: move to appropriate specific sound handlers
#ifdef SOUND_SDL
/// @throw a SoundException if fails to initialize audio card.
DSOEXPORT sound_handler* create_sound_handler_sdl(media::MediaHandler* m);
#elif defined(SOUND_AHI)
/// @throw a SoundException if fails to initialize audio card.
DSOEXPORT sound_handler* create_sound_handler_aos4(media::MediaHandler* m);
#elif defined(SOUND_MKIT)
/// @throw a SoundException if fails to create node.
DSOEXPORT sound_handler* create_sound_handler_mkit(media::MediaHandler* m);
#endif
} // gnash.sound namespace
} // namespace gnash
#endif // SOUND_HANDLER_H
// Local Variables:
// mode: C++
// indent-tabs-mode: t
// End: