Merged multiple WIPs into one big mess

Merge branch 'master' into stream_buffer
actorid
Nicolay Korslund 15 years ago
commit c7701ace82

@ -0,0 +1,57 @@
#ifndef MANGLE_SOUND_BUFFER_H
#define MANGLE_SOUND_BUFFER_H
#include "source.h"
#include "sources/memsource.h"
#include <vector>
#include <assert.h>
namespace Mangle {
namespace Sound {
/** A sample buffer is a factory that creates SampleSources from one
single sound source. It is helpful when you have many instances of
one sound and want to use one shared memory buffer.
This is just a helper class - you don't have to include it in your
program if you don't need it.
*/
class SampleBuffer
{
std::vector<uint8_t> buffer;
public:
/// Reads the source into a memory buffer. Not heavily optimized.
SampleBuffer(SampleSource *source)
{
size_t final = 0;
while(!source->eof())
{
const int add = 16*1024;
// Allocate more memory
size_t newSize = final + add;
buffer.resize(newSize);
// Fill in data
size_t read = source->read(&buffer[final], add);
// If we couldn't read enough data, we should be at the end
// of the stream
assert(read == add || source->eof());
final += read;
}
// Downsize the buffer to the actual length
buffer.resize(final);
}
/// Get a new source
SampleSource *get()
{ return new MemorySource(&buffer[0], buffer.size()); }
};
}}
#endif

@ -1,83 +0,0 @@
#ifndef MANGLE_SOUND_INPUT_H
#define MANGLE_SOUND_INPUT_H
#include <string>
#include <stdint.h>
#include "../stream/stream.h"
namespace Mangle {
namespace Sound {
/// An abstract interface for a read-once stream of audio data.
/** All instances of this is created through InputSource. Objects
should be manually deleted through a call to drop() when they are
no longer needed.
*/
class InputStream
{
public:
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0;
/// Get decoded sound data from the stream.
/** Stores 'length' bytes (or less) in the buffer pointed to by
'output'. Returns the number of bytes written. The function will
only return less than 'length' at the end of the stream. When
the stream is empty, all subsequent calls will return zero.
@param output where to store data
@param length number of bytes to get
@return number of bytes actually written
*/
virtual uint32_t getData(void *output, uint32_t length) = 0;
/// Kill this object
virtual void drop() = 0;
/// Virtual destructor
virtual ~InputStream() {}
};
/// Abstract interface representing one sound source.
/** A sound source may represent one sound file or buffer, and is a
factory for producing InputStream objects from that
sound. Instances of this class are created by an InputManager. All
instances should be deleted through drop() when they are no longer
needed.
*/
class InputSource
{
public:
/// Create a stream from this sound
virtual InputStream *getStream() = 0;
/// Kill this object
virtual void drop() = 0;
/// Virtual destructor
virtual ~InputSource() {}
};
/// Main interface to a sound decoder backend.
/** An input manager is a factory of InputSource objects.
*/
class InputManager
{
public:
/// If true, the stream version of load() works
bool canLoadStream;
/// Load a sound input source from file
virtual InputSource *load(const std::string &file) = 0;
/// Load a sound input source from stream (if canLoadStream is true)
virtual InputSource *load(Stream::Stream *input) = 0;
/// Virtual destructor
virtual ~InputManager() {}
};
}} // namespaces
#endif

@ -1,21 +1,24 @@
#ifndef MANGLE_SOUND_SOUND_H
#define MANGLE_SOUND_SOUND_H
#ifndef MANGLE_SOUND_OUTPUT_H
#define MANGLE_SOUND_OUTPUT_H
#include <string>
#include "input.h"
#include "source.h"
#include "../stream/stream.h"
namespace Mangle {
namespace Sound {
/// Abstract interface for sound instances
/** This class represents one sound instance, which may be played,
stopped, paused and so on. Instances are created from the Sound
class. All instances must be terminated manually using the drop()
function when they are no longer in use.
/// Abstract interface for a single playable sound
/** This class represents one sound outlet, which may be played,
stopped, paused and so on.
Sound instances are created from the SoundFactory class. Sounds
may be connected to a SampleSource or read directly from a file,
and they may support 3d sounds, looping and other features
depending on the capabilities of the backend system.
*/
class Instance
class Sound
{
public:
/// Play or resume the sound
@ -36,63 +39,26 @@ class Instance
/// Set the position. May not have any effect on 2D sounds.
virtual void setPos(float x, float y, float z) = 0;
/// Kill the current object
virtual void drop() = 0;
/// Virtual destructor
virtual ~Instance() {}
};
/// Abstract interface for sound files or sources
/** This class acts as a factory for sound Instance objects.
Implementations may choose to store shared sound buffers or other
optimizations in subclasses of Sound. Objects of this class are
created through the Manager class. All objects of this class
should be terminated manually using the drop() function when they
are no longer in use.
*/
class Sound
{
public:
/**
@brief Create an instance of this sound
See also the capability flags in the Manager class.
@param is3d true if this the sound is to be 3d enabled
@param repeat true if the sound should loop
@return new Instance object
*/
virtual Instance *getInstance(bool is3d, bool repeat) = 0;
// Some prefab functions
/// Shortcut for creating 3D instances
Instance *get3D(bool loop=false)
{ return getInstance(true, loop); }
/// Shortcut for creating 2D instances
Instance *get2D(bool loop=false)
{ return getInstance(false, loop); }
/// Kill the current object
virtual void drop() = 0;
/// Virtual destructor
virtual ~Sound() {}
};
/// Abstract interface for the main sound manager class
/** The sound manager is used to load sound files and is a factory for
Sound objects. It is the main entry point to a given sound system
implementation.
/// Factory interface for creating Sound objects
/** The SoundFactory is the main entry point to a given sound output
system. It is used to create Sound objects, which may be connected
to a sound file or stream, and which may be individually played,
paused, and so on.
The class also contains a set of public bools which describe the
capabilities the particular system. These should be set by
implementations (base classes) in their respective constructors.
*/
class Manager
class SoundFactory
{
public:
/// Virtual destructor
virtual ~SoundFactory() {}
/** @brief If set to true, you should call update() regularly (every frame
or so) on this sound manager. If false, update() should not be
called.
@ -111,23 +77,21 @@ class Manager
*/
bool canRepeatStream;
/// true if we can load sounds directly from file
/// true if we can load sounds directly from file (containing encoded data)
bool canLoadFile;
/// true if we can load sounds from an InputSource
bool canLoadSource;
/// If true, we can lound sound files from a Stream
/// If true, we can lound sound files from a Stream (containing encoded data)
bool canLoadStream;
/// true if we can load sounds from a SampleSource (containing raw data)
bool canLoadSource;
/**
@brief Load a sound from an input source. Only valid if
@brief Load a sound from a sample source. Only valid if
canLoadSource is true.
This function loads a sound from a given stream as defined by
InputSource and InputStream. The InputSource and all streams
created from it will be dropped when drop() is called on the
owning sound / instance.
SampleSource.
@param input the input source
@param stream true if the file should be streamed.
@ -135,10 +99,10 @@ class Manager
large files, but they are not required to.
@return a new Sound object
*/
virtual Sound *load(InputSource *input, bool stream=false) = 0;
virtual Sound *load(SampleSource *input, bool stream=false) = 0;
/**
@brief Load a sound directly from file. Only valid if canLoadStream
@brief Load a sound file from stream. Only valid if canLoadStream
is true.
@param input audio file stream
@ -159,10 +123,10 @@ class Manager
/// Call this every frame if needsUpdate is true
/**
Update function that should be called regularly (about every
frame in a normal game setting.) Implementions may use this to
fill streaming buffers and similar. Implementations that do not
need this should set needsUpdate to false.
This should be called regularly (about every frame in a normal
game setting.) Implementions may use this for filling streaming
buffers and similar tasks. Implementations that do not need this
should set needsUpdate to false.
*/
virtual void update() = 0;
@ -177,9 +141,6 @@ class Manager
virtual void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz) = 0;
/// Virtual destructor
virtual ~Manager() {}
};
}} // Namespaces

@ -1,11 +1,10 @@
#include "output_openal.h"
#include "openal_out.h"
#include <assert.h>
#include <vector>
// ALuint bufferID;
using namespace Mangle::Sound;
// ---- Helper functions and classes ----
class OpenAL_Exception : public std::exception
@ -64,97 +63,71 @@ static void getALFormat(InputStream *inp, int &fmt, int &rate)
fail("Unsupported input format");
}
// ---- OpenAL_Sound ----
// ---- Manager ----
OpenAL_Manager::OpenAL_Manager()
: Context(NULL), Device(NULL)
OpenAL_Sound::OpenAL_Sound(SampleSource *input)
{
needsUpdate = true;
has3D = true;
canRepeatStream = false;
canLoadFile = false;
canLoadSource = true;
canLoadStream = false;
// Set up sound system
Device = alcOpenDevice(NULL);
Context = alcCreateContext(Device, NULL);
if(!Device || !Context)
fail("Failed to initialize context or device");
alcMakeContextCurrent(Context);
}
OpenAL_Manager::~OpenAL_Manager()
void OpenAL_Sound::play()
{
// Deinitialize sound system
alcMakeContextCurrent(NULL);
if(Context) alcDestroyContext(Context);
if(Device) alcCloseDevice(Device);
alSourcePlay(inst);
checkALError("starting playback");
}
Sound *OpenAL_Manager::load(const std::string &file, bool stream)
{ assert(0 && "OpenAL cannot decode files"); }
Sound *OpenAL_Manager::load(Stream::Stream*,bool)
{ assert(0 && "OpenAL cannot decode streams"); }
Sound *OpenAL_Manager::load(InputSource *source, bool stream)
{ return new OpenAL_Sound(source, this, stream); }
void OpenAL_Sound::stop()
{
alSourceStop(inst);
checkALError("stopping");
}
void OpenAL_Manager::update()
void OpenAL_Sound::pause()
{
// Loop through all the streaming sounds and update them
LST::iterator it, next;
for(it = streaming.begin();
it != streaming.end();
it++)
{
(*it)->update();
}
alSourcePause(inst);
checkALError("pausing");
}
void OpenAL_Manager::setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
bool OpenAL_Sound::isPlaying()
{
ALfloat orient[6];
orient[0] = fx;
orient[1] = fy;
orient[2] = fz;
orient[3] = ux;
orient[4] = uy;
orient[5] = uz;
alListener3f(AL_POSITION, x, y, z);
alListenerfv(AL_ORIENTATION, orient);
checkALError("setting listener position");
ALint state;
alGetSourcei(inst, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst)
void OpenAL_Sound::setVolume(float volume)
{
streaming.push_front(inst);
return streaming.begin();
if(volume > 1.0) volume = 1.0;
if(volume < 0.0) volume = 0.0;
alSourcef(inst, AL_GAIN, volume);
checkALError("setting volume");
}
void OpenAL_Manager::remove_stream(LST::iterator it)
void OpenAL_Sound::setPos(float x, float y, float z)
{
streaming.erase(it);
alSource3f(inst, AL_POSITION, x, y, z);
checkALError("setting position");
}
// ---- Sound ----
OpenAL_Sound::~OpenAL_Sound()
{
// Kill the input source
if(source) source->drop();
// And any allocated buffers
if(bufferID)
alDeleteBuffers(1, &bufferID);
}
Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat)
{
@ -217,49 +190,6 @@ Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat)
}
// ---- OpenAL_Instance_Base ----
void OpenAL_Instance_Base::play()
{
alSourcePlay(inst);
checkALError("starting playback");
}
void OpenAL_Instance_Base::stop()
{
alSourceStop(inst);
checkALError("stopping");
}
void OpenAL_Instance_Base::pause()
{
alSourcePause(inst);
checkALError("pausing");
}
bool OpenAL_Instance_Base::isPlaying()
{
ALint state;
alGetSourcei(inst, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
void OpenAL_Instance_Base::setVolume(float volume)
{
if(volume > 1.0) volume = 1.0;
if(volume < 0.0) volume = 0.0;
alSourcef(inst, AL_GAIN, volume);
checkALError("setting volume");
}
void OpenAL_Instance_Base::setPos(float x, float y, float z)
{
alSource3f(inst, AL_POSITION, x, y, z);
checkALError("setting position");
}
// ---- OpenAL_Simple_Instance ----
OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf)

@ -0,0 +1,109 @@
#ifndef MANGLE_SOUND_OPENAL_OUT_H
#define MANGLE_SOUND_OPENAL_OUT_H
#include "../output.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <list>
namespace Mangle {
namespace Sound {
/// OpenAL sound output
class OpenAL_Sound : public Sound
{
protected:
ALuint inst;
public:
OpenAL_Sound(SampleSource *input);
~OpenAL_Sound();
/// Play or resume the sound
void play();
/// Stop the sound
void stop();
/// Pause the sound, may be resumed later
void pause();
/// Check if the sound is still playing
bool isPlaying();
/// Set the volume. The parameter must be between 0.0 and 1.0.
void setVolume(float);
/// Set the 3D position.
void setPos(float x, float y, float z);
};
class OpenALFactory : public SoundFactory
{
ALCdevice *Device;
ALCcontext *Context;
bool didSetup;
public:
/// Initialize object. Pass true (default) if you want the
/// constructor to set up the current ALCdevice and ALCcontext for
/// you.
OpenALFactory(bool doSetup = true)
: didSetup(doSetup)
{
needsUpdate = false;
has3D = true;
canRepeatStream = false;
canLoadFile = false;
canLoadStream = false;
canLoadSource = true;
if(doSetup)
{
// Set up sound system
Device = alcOpenDevice(NULL);
Context = alcCreateContext(Device, NULL);
if(!Device || !Context)
fail("Failed to initialize context or device");
alcMakeContextCurrent(Context);
}
}
~OpenALFactory()
{
// Deinitialize sound system
if(didSetup)
{
alcMakeContextCurrent(NULL);
if(Context) alcDestroyContext(Context);
if(Device) alcCloseDevice(Device);
}
}
Sound *load(const std::string &file, bool stream=false) { assert(0); }
Sound *load(Stream::Stream *input, bool stream=false) { assert(0); }
Sound *load(SampleSource* input, bool stream=false)
{ return new OpenAL_Sound(input); }
void update() {}
setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
{
ALfloat orient[6];
orient[0] = fx;
orient[1] = fy;
orient[2] = fz;
orient[3] = ux;
orient[4] = uy;
orient[5] = uz;
alListener3f(AL_POSITION, x, y, z);
alListenerfv(AL_ORIENTATION, orient);
}
};
}} // namespaces
#endif

@ -1,95 +0,0 @@
#include "audiere_imp.h"
// Exception handling
class Audiere_Exception : public std::exception
{
std::string msg;
public:
Audiere_Exception(const std::string &m) : msg(m) {}
~Audiere_Exception() throw() {}
virtual const char* what() const throw() { return msg.c_str(); }
};
static void fail(const std::string &msg)
{
throw Audiere_Exception("Audiere exception: " + msg);
}
using namespace audiere;
using namespace Mangle::Sound;
AudiereManager::AudiereManager()
{
needsUpdate = false;
has3D = false;
canRepeatStream = true;
canLoadFile = true;
canLoadSource = false;
canLoadStream = false;
device = OpenDevice("");
if(device == NULL)
fail("Failed to open device");
}
// --- Manager ---
Sound *AudiereManager::load(const std::string &file, bool stream)
{ return new AudiereSound(file, device, stream); }
// --- Sound ---
AudiereSound::AudiereSound(const std::string &file,
AudioDevicePtr _device,
bool _stream)
: device(_device), stream(_stream)
{
sample = OpenSampleSource(file.c_str());
if(!sample)
fail("Couldn't load file " + file);
buf = CreateSampleBuffer(sample);
}
Instance *AudiereSound::getInstance(bool is3d, bool repeat)
{
// Ignore is3d. Audiere doesn't implement 3d sound. We could make a
// hack software 3D implementation later, but it's not that
// important.
SampleSourcePtr sample = buf->openStream();
if(!sample)
fail("Failed to open sample stream");
OutputStreamPtr sound = OpenSound(device, sample, stream);
if(repeat)
sound->setRepeat(true);
return new AudiereInstance(sound);
}
// --- Instance ---
AudiereInstance::AudiereInstance(OutputStreamPtr _sound)
: sound(_sound) {}
void AudiereInstance::play()
{ sound->play(); }
void AudiereInstance::stop()
{ sound->stop(); }
void AudiereInstance::pause()
{ stop(); }
bool AudiereInstance::isPlaying()
{ return sound->isPlaying(); }
void AudiereInstance::setVolume(float vol)
{ sound->setVolume(vol); }

@ -1,77 +0,0 @@
#ifndef MANGLE_SOUND_AUDIERE_H
#define MANGLE_SOUND_AUDIERE_H
#include "../sound.h"
#include <assert.h>
#include <audiere.h>
namespace Mangle {
namespace Sound {
/// Implementation of Sound::Manager for Audiere
class AudiereManager : public Manager
{
audiere::AudioDevicePtr device;
public:
AudiereManager();
virtual Sound *load(const std::string &file, bool stream=false);
/// not implemented yet
virtual Sound *load(Stream::Stream *input, bool stream=false)
{ assert(0); }
/// disabled
virtual Sound *load(InputSource *input, bool stream=false)
{ assert(0); }
/// disabled
virtual void update() { assert(0); }
/// disabled
virtual void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
{ assert(0); };
};
/// Audiere Sound implementation
class AudiereSound : public Sound
{
audiere::AudioDevicePtr device;
audiere::SampleSourcePtr sample;
audiere::SampleBufferPtr buf;
bool stream;
public:
virtual Instance *getInstance(bool is3d, bool repeat);
virtual void drop()
{ delete this; }
AudiereSound(const std::string &file, audiere::AudioDevicePtr device,
bool stream);
};
/// Audiere Instance implementation
class AudiereInstance : public Instance
{
audiere::OutputStreamPtr sound;
public:
virtual void play();
virtual void stop();
virtual void pause();
virtual bool isPlaying();
virtual void setVolume(float);
/// disabled
virtual void setPos(float x, float y, float z)
{ assert(0); }
virtual void drop()
{ delete this; }
AudiereInstance(audiere::OutputStreamPtr);
};
}} // Namespace
#endif

@ -1,59 +0,0 @@
#ifndef MANGLE_SOUND_AUDIERE_INPUT_H
#define MANGLE_SOUND_AUDIERE_INPUT_H
#include "../input.h"
#include <audiere.h>
namespace Mangle {
namespace Sound {
/// Implementation of Sound::InputManager for Audiere
class AudiereInput : public InputManager
{
public:
AudiereInput();
/// Load a source from a file
InputSource *load(const std::string &file);
/// Load a source from a stream
virtual InputSource *load(Stream::Stream *input);
};
/// Audiere InputSource implementation
class AudiereSource : public InputSource
{
audiere::SampleBufferPtr buf;
public:
AudiereSource(const std::string &file);
AudiereSource(Stream::Stream *input);
InputStream *getStream();
void drop() { delete this; }
};
/// Audiere InputStream implementation
class AudiereStream : public InputStream
{
audiere::SampleSourcePtr sample;
int frameSize; // Size of one frame, in bytes
static const int PSIZE = 10;
// Temporary storage for unevenly read samples. See the comment for
// getData() in the .cpp file.
char pullOver[PSIZE];
// How much of the above buffer is in use
int pullSize;
public:
AudiereStream(audiere::SampleSourcePtr _sample);
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
uint32_t getData(void *data, uint32_t length);
void drop() { delete this; }
};
}} // Namespace
#endif

@ -1,75 +0,0 @@
#ifndef MANGLE_SOUND_FFMPEG_H
#define MANGLE_SOUND_FFMPEG_H
#include "../input.h"
#include <exception>
#include <vector>
#include <assert.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
namespace Mangle {
namespace Sound {
/// FFmpeg exception
class FFM_Exception : public std::exception
{
std::string msg;
public:
FFM_Exception(const std::string &m);
~FFM_Exception() throw();
virtual const char* what() const throw();
};
/// FFMpeg implementation of InputManager
class FFM_InputManager : public InputManager
{
static bool init;
public:
FFM_InputManager();
virtual InputSource *load(const std::string &file);
/// not supported
virtual InputSource *load(Stream::Stream *input) { assert(0); }
};
/// FFMpeg implementation of InputSource
class FFM_InputSource : public InputSource
{
std::string name;
public:
FFM_InputSource(const std::string &file);
virtual InputStream *getStream();
virtual void drop();
};
/// FFMpeg implementation of InputStream
class FFM_InputStream : public InputStream
{
AVFormatContext *FmtCtx;
AVCodecContext *CodecCtx;
int StreamNum;
bool empty;
std::vector<uint8_t> storage;
public:
FFM_InputStream(const std::string &file);
~FFM_InputStream();
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
virtual uint32_t getData(void *data, uint32_t length);
virtual void drop();
};
}} // namespaces
#endif

@ -1,28 +0,0 @@
#ifndef MANGLE_FFMPEG_OPENAL_H
#define MANGLE_FFMPEG_OPENAL_H
#include "input_filter.h"
#include "input_ffmpeg.h"
#include "output_openal.h"
namespace Mangle {
namespace Sound {
/// An InputFilter that adds FFmpeg decoding to OpenAL
class OpenAL_FFM_Manager : public InputFilter
{
public:
OpenAL_FFM_Manager()
{
set(new OpenAL_Manager,
new FFM_InputManager);
}
~OpenAL_FFM_Manager()
{
delete snd;
delete inp;
}
};
}}
#endif

@ -1,125 +0,0 @@
#ifndef MANGLE_SOUND_OPENAL_H
#define MANGLE_SOUND_OPENAL_H
#include "../sound.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <list>
namespace Mangle {
namespace Sound {
class OpenAL_Stream_Instance;
/// OpenAL implementation of Manager
class OpenAL_Manager : public Manager
{
public:
// List of all streaming sounds - these need to be updated regularly
typedef std::list<OpenAL_Stream_Instance*> LST;
OpenAL_Manager();
virtual ~OpenAL_Manager();
LST::iterator add_stream(OpenAL_Stream_Instance*);
void remove_stream(LST::iterator);
virtual Sound *load(const std::string &file, bool stream=false);
virtual Sound *load(Stream::Stream *input, bool stream=false);
virtual Sound *load(InputSource* input, bool stream=false);
virtual void update();
virtual void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz);
private:
ALCdevice *Device;
ALCcontext *Context;
LST streaming;
};
/// OpenAL implementation of Sound
class OpenAL_Sound : public Sound
{
InputSource *source;
OpenAL_Manager *owner;
bool stream;
// Used for non-streaming files, contains the entire sound buffer if
// non-zero
ALuint bufferID;
public:
OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str)
: source(src), owner(own), stream(str), bufferID(0) {}
~OpenAL_Sound();
virtual Instance *getInstance(bool is3d, bool repeat);
void drop() { delete this; }
};
/// Shared parent class that holds an OpenAL sound instance. Just used
/// for shared functionality, has no setup or cleanup code.
class OpenAL_Instance_Base : public Instance
{
protected:
ALuint inst;
public:
void drop() { delete this; }
virtual void play();
virtual void stop();
virtual void pause();
virtual bool isPlaying();
virtual void setVolume(float);
virtual void setPos(float x, float y, float z);
};
/// Non-streaming OpenAL-implementation of Instance. Uses a shared
/// sound buffer in OpenAL_Sound.
class OpenAL_Simple_Instance : public OpenAL_Instance_Base
{
public:
OpenAL_Simple_Instance(ALuint buf);
~OpenAL_Simple_Instance();
};
/// Streaming OpenAL-implementation of Instance.
class OpenAL_Stream_Instance : public OpenAL_Instance_Base
{
// Since OpenAL streams have to be updated manually each frame, we
// need to have a sufficiently large buffer so that we don't run out
// of data in the mean time. Each instance will take around 512 Kb
// of memory, independent of how large the file is.
static const int BUFS = 4;
static const int SIZE = 128*1024;
// Buffers
ALuint bufs[BUFS];
// Sound format settings
int rate, fmt;
// Source of data
InputStream *stream;
OpenAL_Manager *owner;
// List iterator, used for removing ourselves from the streaming
// list when we're deleted.
OpenAL_Manager::LST::iterator lit;
// Load and queue a new buffer
void queueBuffer(ALuint buffer);
public:
OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*);
~OpenAL_Stream_Instance();
void update();
};
}} // namespaces
#endif

@ -0,0 +1,63 @@
#ifndef MANGLE_SOUND_SOURCE_H
#define MANGLE_SOUND_SOURCE_H
#include <string>
#include <stdint.h>
#include <assert.h>
#include "../stream/stream.h"
namespace Mangle {
namespace Sound {
/// A stream containing raw sound data and information about the format
class SampleSource : public Stream::Stream
{
protected:
bool isEof;
public:
SampleSource()
{
// These are usually not needed for sound data
isSeekable = false;
hasPosition = false;
hasSize = false;
isEof = false;
}
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0;
bool eof() const { return isEof; }
// Disabled functions
void seek(size_t pos) const { assert(0); }
size_t tell() const { assert(0); }
size_t size() const { assert(0); }
};
/// A factory interface for loading SampleSources from file or stream
class SampleSourceLoader
{
public:
/// If true, the stream version of load() works
bool canLoadStream;
/// If true, the file version of load() works
bool canLoadFile;
/// Load a sound input source from file (if canLoadFile is true)
virtual SampleSource *load(const std::string &file) = 0;
/// Load a sound input source from stream (if canLoadStream is true)
virtual SampleSource *load(Stream::Stream *input) = 0;
/// Virtual destructor
virtual ~SampleSourceLoader() {}
};
}} // namespaces
#endif

@ -1,5 +1,4 @@
#include "input_audiere.h"
#include <assert.h>
#include "audiere_source.h"
#include "../../stream/clients/audiere_file.h"
@ -23,65 +22,9 @@ static void fail(const std::string &msg)
using namespace audiere;
using namespace Mangle::Sound;
// --- InputManager ---
// --- SampleSource ---
AudiereInput::AudiereInput()
{
canLoadStream = true;
}
InputSource *AudiereInput::load(const std::string &file)
{ return new AudiereSource(file); }
InputSource *AudiereInput::load(Stream::Stream *input)
{ return new AudiereSource(input); }
// --- InputSource ---
AudiereSource::AudiereSource(const std::string &file)
{
SampleSourcePtr sample = OpenSampleSource(file.c_str());
if(!sample)
fail("Couldn't load file " + file);
buf = CreateSampleBuffer(sample);
}
AudiereSource::AudiereSource(Stream::Stream *input)
{
SampleSourcePtr sample = OpenSampleSource
(new Stream::AudiereFile(input));
if(!sample)
fail("Couldn't load stream");
buf = CreateSampleBuffer(sample);
}
InputStream *AudiereSource::getStream()
{
return new AudiereStream(buf->openStream());
}
// --- InputStream ---
AudiereStream::AudiereStream(SampleSourcePtr _sample)
: sample(_sample), pullSize(0)
{
assert(sample);
SampleFormat fmt;
int channels, rate;
sample->getFormat(channels, rate, fmt);
// Calculate the size of one frame
frameSize = GetSampleSize(fmt) * channels;
// Make sure that our pullover hack will work. Increase this size if
// this doesn't work in all cases.
assert(frameSize <= PSIZE);
}
void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
{
SampleFormat fmt;
sample->getFormat(*channels, *rate, fmt);
@ -98,10 +41,11 @@ void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
reading a whole number of frames - if not, we need to store the
remainding part of the last frame and remember it for the next read
operation.
*/
uint32_t AudiereStream::getData(void *_data, uint32_t length)
size_t AudiereSource::read(void *_data, size_t length)
{
if(isEof) return 0;
char *data = (char*)_data;
// Move the remains from the last operation first
@ -122,18 +66,74 @@ uint32_t AudiereStream::getData(void *_data, uint32_t length)
// Read the data
int res = sample->read(frames, data);
// Are we missing data? If res<length and we're at the end of the
// stream, then this doesn't apply.
if(res == frames && pullSize &&
// Read one more sample
(sample->read(1, pullOver) != 0))
if(res < frames)
isEof = true;
// Are we missing data? If we're at the end of the stream, then this
// doesn't apply.
if(!isEof && pullSize)
{
// Now, move as much of it as we can fit into the output
// data
memcpy(data+length-pullSize, pullOver, pullSize);
// Read one more sample
if(sample->read(1, pullOver) != 0)
{
// Then, move as much of it as we can fit into the output
// data
memcpy(data+length-pullSize, pullOver, pullSize);
}
else
// Failed reading, we're out of data
isEof = true;
}
else pullSize = 0;
// If we're at the end of the stream, then no data remains to be
// pulled over
if(isEof)
pullSize = 0;
// Return the total number of bytes stored
return frameSize*res + pullSize;
}
// --- Constructors ---
AudiereSource::AudiereSource(const std::string &file)
{
sample = OpenSampleSource(file.c_str());
if(!sample)
fail("Couldn't load file " + file);
getFormat();
}
AudiereSource::AudiereSource(Stream::Stream *input)
{
// Use our Stream::AudiereFile implementation to convert a Mangle
// 'Stream' to an Audiere 'File'
sample = OpenSampleSource(new Stream::AudiereFile(input));
if(!sample)
fail("Couldn't load stream");
getFormat();
}
AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
: sample(src)
{ assert(sample); getFormat(); }
// Common function called from all constructors
AudiereSource::getFormat()
{
assert(sample);
SampleFormat fmt;
int channels, rate;
sample->getFormat(channels, rate, fmt);
// Calculate the size of one frame
frameSize = GetSampleSize(fmt) * channels;
// Make sure that our pullover hack will work. Increase this size if
// this doesn't work in all cases.
assert(frameSize <= PSIZE);
}

@ -0,0 +1,51 @@
#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
#define MANGLE_SOUND_AUDIERE_SOURCE_H
#include "../source.h"
#include <audiere.h>
namespace Mangle {
namespace Sound {
/// A sample source that decodes files using Audiere
class AudiereSource : public SampleSource
{
audiere::SampleSourcePtr sample;
// Number of bytes we cache between reads. This should correspond to
// the maximum possible value of frameSize.
static const int PSIZE = 10;
// Size of one frame, in bytes
int frameSize;
// Temporary storage for unevenly read samples. See the comment for
// read() in the .cpp file.
char pullOver[PSIZE];
// How much of the above buffer is in use
int pullSize;
void getFormat();
public:
/// Decode the given sound file
AudiereSource(const std::string &file);
/// Decode the given sound stream
AudiereSource(Stream::Stream *src);
/// Read directly from an existing audiere::SampleSource
AudiereSource(audiere::SampleSourcePtr src);
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.h"
/// A factory that loads AudiereSources from file and stream
typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
}} // Namespace
#endif

@ -1,12 +1,22 @@
#include "input_ffmpeg.h"
#include <assert.h>
using namespace Mangle::Sound;
// Static output buffer. Not thread safe, but supports multiple
// streams operated from the same thread.
static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
bool FFM_InputManager::init = false;
/// FFmpeg exception.
class FFM_Exception : public std::exception
{
std::string msg;
public:
FFM_Exception(const std::string &m);
~FFM_Exception() throw();
virtual const char* what() const throw();
};
FFM_Exception::FFM_Exception(const std::string &m)
: msg(m) {}
@ -21,44 +31,23 @@ static void fail(const std::string &msg)
throw FFM_Exception("FFMpeg exception: " + msg);
}
// --- Loader ---
// --- Manager ---
static bool init = false;
FFM_InputManager::FFM_InputManager()
FFMpegLoader::FFMpegLoader(bool setup)
{
if(!init)
if(setup && !init)
{
av_register_all();
av_log_set_level(AV_LOG_ERROR);
init = true;
}
canLoadStream = false;
}
InputSource *FFM_InputManager::load(const std::string &file)
{ return new FFM_InputSource(file); }
// --- Source ---
FFM_InputSource::FFM_InputSource(const std::string &file)
{
// FFmpeg doesn't handle several instances from one source. So we
// just store the filename.
name = file;
}
InputStream *FFM_InputSource::getStream()
{ return new FFM_InputStream(name); }
void FFM_InputSource::drop()
{ delete this; }
// --- Stream ---
FFM_InputStream::FFM_InputStream(const std::string &file)
FFMpegSource::FFMpegSource(const std::string &file)
{
std::string msg;
AVCodec *codec;
@ -83,7 +72,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file)
}
if(StreamNum == FmtCtx->nb_streams)
fail("File " + file + " didn't contain any audio streams");
fail("File '" + file + "' didn't contain any audio streams");
// Open the decoder
CodecCtx = FmtCtx->streams[StreamNum]->codec;
@ -91,7 +80,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file)
if(!codec || avcodec_open(CodecCtx, codec) < 0)
{
msg = "Error loading " + file + ": ";
msg = "Error loading '" + file + "': ";
if(codec)
msg += "coded error";
else
@ -108,24 +97,24 @@ FFM_InputStream::FFM_InputStream(const std::string &file)
fail(msg);
}
FFM_InputStream::~FFM_InputStream()
FFMpegSource::~FFMpegSource()
{
avcodec_close(CodecCtx);
av_close_input_file(FmtCtx);
}
void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
void FFMpegSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
{
if(rate) *rate = CodecCtx->sample_rate;
if(channels) *channels = CodecCtx->channels;
if(bits) *bits = 16;
}
uint32_t FFM_InputStream::getData(void *data, uint32_t length)
size_t FFMpegSource::read(void *data, size_t length)
{
if(empty) return 0;
if(isEof) return 0;
uint32_t left = length;
size_t left = length;
uint8_t *outPtr = (uint8_t*)data;
// First, copy over any stored data we might be sitting on
@ -195,7 +184,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length)
memcpy(outPtr, outBuf, copy);
// left = how much space is left in the caller output
// buffer
// buffer. This loop repeats as long left is > 0
left -= copy;
outPtr += copy;
assert(left >= 0);
@ -215,10 +204,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length)
// If we're returning less than asked for, then we're done
if(left > 0)
empty = true;
isEof = true;
return length - left;
}
void FFM_InputStream::drop()
{ delete this; }

@ -0,0 +1,53 @@
#ifndef MANGLE_SOUND_FFMPEG_H
#define MANGLE_SOUND_FFMPEG_H
#include "../input.h"
#include <exception>
#include <vector>
#include <assert.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
namespace Mangle {
namespace Sound {
class FFMpegSource : public SampleSource
{
AVFormatContext *FmtCtx;
AVCodecContext *CodecCtx;
int StreamNum;
std::vector<uint8_t> storage;
public:
/// Decode the given sound file
FFMpegSource(const std::string &file);
/// Decode the given sound stream (not supported by FFmpeg)
FFMpegSource(Stream::Stream *src) { assert(0); }
~FFMpegSource();
// Overrides
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.h"
/// A factory that loads FFMpegSources from file
class FFMpegLoader : public SSL_Template<AudiereSource,false,true>
{
public:
/// Sets up the libavcodec library. If you want to do your own
/// setup, send a setup=false parameter.
FFMpegLoader(bool setup=true);
};
}} // namespaces
#endif

@ -0,0 +1,26 @@
#ifndef SSL_TEMPL_H
#define SSL_TEMPL_H
template <class SourceT, bool stream, bool file>
class SSL_Template : public SampleSourceLoader
{
SSL_Template()
{
canLoadStream = stream;
canLoadFile = file;
}
SampleSource *load(const std::string &file)
{
assert(canLoadFile);
return new SourceT(file);
}
SampleSource *load(Stream::Stream *input)
{
assert(canLoadStream);
return new SourceT(input);
}
};
#endif

@ -0,0 +1,49 @@
#ifndef MANGLE_SOUND_MEMSOURCE_H
#define MANGLE_SOUND_MEMSOURCE_H
#include "../source.h"
namespace Mangle {
namespace Sound {
/// A sample source reading directly from a memory buffer
class MemorySource : public SampleSource
{
char *buf;
size_t len;
size_t pos;
int32_t rate, channels, bits;
public:
MemorySource(void *_buf, size_t _len, int32_t _rate, int32_t _channels, int32_t _bits)
: len(_len), pos(0), rate(_rate), channels(_channels), bits(_bits)
{ buf = (char*)_buf; }
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) const
{
if(_rate) *_rate = rate;
if(_channels) *_channels = channels;
if(_bits) *_bits = bits;
}
bool eof() const { return pos == len; }
size_t read(void *out, size_t count)
{
assert(len >= pos);
if(count > (len-pos))
count = len-pos;
if(count) memcpy(out, buf+pos, count);
pos += count;
return count;
}
};
}} // namespaces
#endif
Loading…
Cancel
Save