/* [<][>][^][v][top][bottom][index][help] */
// EmbedSoundInst.h - instance of an embedded sound, for gnash
//
// 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_EMBEDSOUNDINST_H
#define SOUND_EMBEDSOUNDINST_H
#include "InputStream.h" // for inheritance
#include "AudioDecoder.h" // for dtor visibility
#include "SoundEnvelope.h" // for SoundEnvelopes typedef
#include "SimpleBuffer.h" // for composition (decoded data)
#include "EmbedSound.h" // for inlines
#include "sound_handler.h" // for StreamBlockId typedef
#include <memory>
#include <cassert>
#include <boost/cstdint.hpp> // For C99 int types
// Forward declarations
namespace gnash {
namespace sound {
class EmbedSound;
}
namespace media {
class MediaHandler;
}
}
namespace gnash {
namespace sound {
/// Instance of a defined %sound (EmbedSound)
//
/// This class contains a pointer to the EmbedSound used for playing
/// and a SimpleBuffer to use when decoding is needed.
///
/// When the SimpleBuffer is NULL we'll play the EmbedSound bytes directly
/// (we assume they are decoded already)
///
class EmbedSoundInst : public InputStream
{
public:
/// Create an embedded %sound instance
//
/// @param def
/// The definition of this sound (where immutable data is kept)
///
/// @param mh
/// The MediaHandler to use for on-demand decoding
///
/// @param blockId
/// Identifier of the encoded block to start decoding from.
/// @see gnash::swf::StreamSoundBlockTag
///
/// @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 numeric_limits<unsigned int>::max() for never
///
/// @param envelopes
/// SoundEnvelopes to apply to this sound. May be 0 for none.
///
/// @param loopCount
/// Number of times this instance should loop over the defined sound.
/// Note that every loop begins and ends at the range given by
/// inPoint and outPoint.
///
EmbedSoundInst(EmbedSound& def, media::MediaHandler& mh,
sound_handler::StreamBlockId blockId,
unsigned int inPoint,
unsigned int outPoint,
const SoundEnvelopes* envelopes,
unsigned int loopCount);
// See dox in sound_handler.h (InputStream)
unsigned int fetchSamples(boost::int16_t* to, unsigned int nSamples);
// See dox in sound_handler.h (InputStream)
unsigned int samplesFetched() const;
// See dox in sound_handler.h (InputStream)
bool eof() const;
/// Unregister self from the associated EmbedSound
//
/// WARNING: must be thread-safe!
///
~EmbedSoundInst();
private:
/// Current decoding position in the encoded stream
unsigned long decodingPosition;
/// Current playback position in the decoded stream
unsigned long playbackPosition;
/// Numbers of loops: -1 means loop forever, 0 means play once.
/// For every loop completed, it is decremented.
long loopCount;
/// Offset in bytes samples from start of the block
/// to begin playback from
unsigned long _inPoint;
/// Offset in bytes to end playback at
/// Never if numeric_limits<unsigned long>::max()
unsigned long _outPoint;
/// Sound envelopes for the current sound, which determine the volume level
/// from a given position. Only used with event sounds.
const SoundEnvelopes* envelopes;
/// Index of current envelope.
boost::uint32_t current_env;
/// Number of samples fetched so far.
unsigned long _samplesFetched;
/// Get the sound definition this object is an instance of
const EmbedSound& getSoundData() {
return _soundDef;
}
/// Append size bytes to this raw data
//
/// @param data
/// Data bytes, allocated with new[]. Ownership transferred.
///
/// @param size
/// Size of the 'data' buffer.
///
void appendDecodedData(boost::uint8_t* data, unsigned int size);
/// Set decoded data
//
/// @param data
/// Data bytes, allocated with new[]. Ownership transferred.
///
/// @param size
/// Size of the 'data' buffer.
///
void setDecodedData(boost::uint8_t* data, unsigned int size)
{
if ( ! _decodedData.get() )
{
_decodedData.reset( new SimpleBuffer() );
}
_decodedData->resize(0); // shouldn't release memory
_decodedData->append(data, size);
delete [] data; // ownership transferred...
}
size_t encodedDataSize() const
{
return _soundDef.size();
}
/// Apply envelope-volume adjustments
//
///
/// Modified envelopes cursor (current_env)
///
/// @param samples
/// The samples to apply envelopes to
///
/// @param nSamples
/// Number of samples in the samples array.
/// (nSamples*2 bytes).
///
/// @param firstSampleNum
/// Logical position of first sample in the array.
/// This number gives the sample position referred to
/// by SoundEnvelope objects.
///
/// @param env
/// SoundEnvelopes to apply.
///
///
void applyEnvelopes(boost::int16_t* samples, unsigned int nSamples,
unsigned int firstSampleNum,
const SoundEnvelopes& env);
/// AS-volume adjustment
//
/// @param samples
/// The 16bit samples to adjust volume of
///
/// @param nSamples
/// Number of samples in the array
///
/// @param volume
/// Volume factor
///
static void adjustVolume(boost::int16_t* samples,
unsigned int nSamples, float volume);
/// Returns the data pointer in the encoded datastream
/// for the given position. Boundaries are checked.
//
/// Uses _samplesFetched and playbackPosition
///
/// @todo make private
///
const boost::uint8_t* getEncodedData(unsigned long int pos);
/// Return number of already-decoded samples available
/// from playback position on
unsigned int decodedSamplesAhead() const
{
unsigned int dds = decodedDataSize();
if ( dds <= playbackPosition ) return 0;
unsigned int bytesAhead = dds - playbackPosition;
assert(!(bytesAhead%2));
if ( _outPoint < std::numeric_limits<unsigned long>::max() )
{
unsigned int toCustomEnd = _outPoint-playbackPosition;
if ( toCustomEnd < bytesAhead ) bytesAhead = toCustomEnd;
}
unsigned int samplesAhead = bytesAhead/2;
return samplesAhead;
}
bool reachedCustomEnd() const;
/// Return true if there's nothing more to decode
bool decodingCompleted() const
{
// example: 10 bytes of encoded data, decodingPosition 8 : more to decode
// example: 10 bytes of encoded data, decodingPosition 10 : nothing more to decode
return ( decodingPosition >= encodedDataSize() );
}
/// The decoder object used to convert the data into the playable format
std::auto_ptr<media::AudioDecoder> _decoder;
/// Create a decoder for this instance
//
/// If decoder creation fails an error will
/// be logged, and _decoder won't be set
///
void createDecoder(media::MediaHandler& mediaHandler);
/// Return full size of the decoded data buffer
size_t decodedDataSize() const
{
if ( _decodedData.get() )
{
return _decodedData->size();
}
else return 0;
}
/// \brief
/// Returns the data pointer in the decoded datastream
/// for the given byte-offset.
//
/// Boundaries are checked.
///
/// @param pos offsets in bytes. This should usually be
/// a multiple of two, since decoded data is
/// composed of signed 16bit PCM samples..
///
boost::int16_t* getDecodedData(unsigned long int pos);
/// The encoded data
//
/// It is non-const because we deregister ourselves
/// from its container of playing instances on destruction
///
EmbedSound& _soundDef;
/// The decoded buffer
//
/// If NULL, the _soundDef will be considered
/// decoded instead
///
std::auto_ptr<SimpleBuffer> _decodedData;
/// Decode next input block
//
/// It's assumed !decodingCompleted()
///
void decodeNextBlock();
};
} // gnash.sound namespace
} // namespace gnash
#endif // SOUND_EMBEDSOUNDINST_H