diff --git a/sound/buffer.h b/sound/buffer.h new file mode 100644 index 0000000000..9cb27bd9cb --- /dev/null +++ b/sound/buffer.h @@ -0,0 +1,57 @@ +#ifndef MANGLE_SOUND_BUFFER_H +#define MANGLE_SOUND_BUFFER_H + +#include "source.h" +#include "sources/memsource.h" +#include +#include + +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 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 diff --git a/sound/input.h b/sound/input.h deleted file mode 100644 index f61a029ffe..0000000000 --- a/sound/input.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef MANGLE_SOUND_INPUT_H -#define MANGLE_SOUND_INPUT_H - -#include -#include - -#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 diff --git a/sound/sound.h b/sound/output.h similarity index 52% rename from sound/sound.h rename to sound/output.h index 0a51e9f939..4cdac7bed5 100644 --- a/sound/sound.h +++ b/sound/output.h @@ -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 -#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 diff --git a/sound/servers/input_filter.h b/sound/outputs/input_filter.h similarity index 100% rename from sound/servers/input_filter.h rename to sound/outputs/input_filter.h diff --git a/sound/servers/openal_audiere.h b/sound/outputs/openal_audiere.h similarity index 100% rename from sound/servers/openal_audiere.h rename to sound/outputs/openal_audiere.h diff --git a/sound/servers/output_openal.cpp b/sound/outputs/openal_out.cpp similarity index 70% rename from sound/servers/output_openal.cpp rename to sound/outputs/openal_out.cpp index 06a8edca80..5d3633e753 100644 --- a/sound/servers/output_openal.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,11 +1,10 @@ -#include "output_openal.h" +#include "openal_out.h" #include -#include +// 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_Manager::update() +void OpenAL_Sound::stop() { - // Loop through all the streaming sounds and update them - LST::iterator it, next; - for(it = streaming.begin(); - it != streaming.end(); - it++) - { - (*it)->update(); - } + alSourceStop(inst); + checkALError("stopping"); } -void OpenAL_Manager::setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) +void OpenAL_Sound::pause() { - 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"); + alSourcePause(inst); + checkALError("pausing"); } -OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) +bool OpenAL_Sound::isPlaying() { - streaming.push_front(inst); - return streaming.begin(); + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; } -void OpenAL_Manager::remove_stream(LST::iterator it) +void OpenAL_Sound::setVolume(float volume) { - streaming.erase(it); + if(volume > 1.0) volume = 1.0; + if(volume < 0.0) volume = 0.0; + alSourcef(inst, AL_GAIN, volume); + checkALError("setting volume"); +} + +void OpenAL_Sound::setPos(float x, float y, float z) +{ + 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) diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h new file mode 100644 index 0000000000..95d27845ca --- /dev/null +++ b/sound/outputs/openal_out.h @@ -0,0 +1,109 @@ +#ifndef MANGLE_SOUND_OPENAL_OUT_H +#define MANGLE_SOUND_OPENAL_OUT_H + +#include "../output.h" + +#include +#include +#include + +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 diff --git a/sound/servers/.gitignore b/sound/servers/.gitignore deleted file mode 100644 index 8b13789179..0000000000 --- a/sound/servers/.gitignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sound/servers/audiere_imp.cpp b/sound/servers/audiere_imp.cpp deleted file mode 100644 index ecdb0580ad..0000000000 --- a/sound/servers/audiere_imp.cpp +++ /dev/null @@ -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); } diff --git a/sound/servers/audiere_imp.h b/sound/servers/audiere_imp.h deleted file mode 100644 index 5ebb812bdb..0000000000 --- a/sound/servers/audiere_imp.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_H -#define MANGLE_SOUND_AUDIERE_H - -#include "../sound.h" - -#include -#include - -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 diff --git a/sound/servers/input_audiere.h b/sound/servers/input_audiere.h deleted file mode 100644 index e753b0174c..0000000000 --- a/sound/servers/input_audiere.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_INPUT_H -#define MANGLE_SOUND_AUDIERE_INPUT_H - -#include "../input.h" - -#include - -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 diff --git a/sound/servers/input_ffmpeg.h b/sound/servers/input_ffmpeg.h deleted file mode 100644 index b744a75856..0000000000 --- a/sound/servers/input_ffmpeg.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef MANGLE_SOUND_FFMPEG_H -#define MANGLE_SOUND_FFMPEG_H - -#include "../input.h" -#include -#include -#include - -extern "C" -{ -#include -#include -} - -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 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 diff --git a/sound/servers/openal_ffmpeg.h b/sound/servers/openal_ffmpeg.h deleted file mode 100644 index 179d3cb702..0000000000 --- a/sound/servers/openal_ffmpeg.h +++ /dev/null @@ -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 diff --git a/sound/servers/output_openal.h b/sound/servers/output_openal.h deleted file mode 100644 index 53226c32f3..0000000000 --- a/sound/servers/output_openal.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_SOUND_OPENAL_H -#define MANGLE_SOUND_OPENAL_H - -#include "../sound.h" - -#include -#include -#include - -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 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 diff --git a/sound/source.h b/sound/source.h new file mode 100644 index 0000000000..7361fb1182 --- /dev/null +++ b/sound/source.h @@ -0,0 +1,63 @@ +#ifndef MANGLE_SOUND_SOURCE_H +#define MANGLE_SOUND_SOURCE_H + +#include +#include +#include + +#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 diff --git a/sound/servers/input_audiere.cpp b/sound/sources/audiere_source.cpp similarity index 62% rename from sound/servers/input_audiere.cpp rename to sound/sources/audiere_source.cpp index c48f45013b..6ebc4f8813 100644 --- a/sound/servers/input_audiere.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,5 +1,4 @@ -#include "input_audiere.h" -#include +#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 resread(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); +} diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h new file mode 100644 index 0000000000..12ae81bce5 --- /dev/null +++ b/sound/sources/audiere_source.h @@ -0,0 +1,51 @@ +#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H +#define MANGLE_SOUND_AUDIERE_SOURCE_H + +#include "../source.h" + +#include + +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 AudiereLoader; + +}} // Namespace +#endif diff --git a/sound/servers/input_ffmpeg.cpp b/sound/sources/ffmpeg_source.cpp similarity index 79% rename from sound/servers/input_ffmpeg.cpp rename to sound/sources/ffmpeg_source.cpp index f29ddfdb64..af785eaa35 100644 --- a/sound/servers/input_ffmpeg.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,12 +1,22 @@ #include "input_ffmpeg.h" -#include 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; } diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h new file mode 100644 index 0000000000..5317c11c02 --- /dev/null +++ b/sound/sources/ffmpeg_source.h @@ -0,0 +1,53 @@ +#ifndef MANGLE_SOUND_FFMPEG_H +#define MANGLE_SOUND_FFMPEG_H + +#include "../input.h" +#include +#include +#include + +extern "C" +{ +#include +#include +} + +namespace Mangle { +namespace Sound { + +class FFMpegSource : public SampleSource +{ + AVFormatContext *FmtCtx; + AVCodecContext *CodecCtx; + int StreamNum; + + std::vector 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 +{ + 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 diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h new file mode 100644 index 0000000000..0b668fffa6 --- /dev/null +++ b/sound/sources/loadertemplate.h @@ -0,0 +1,26 @@ +#ifndef SSL_TEMPL_H +#define SSL_TEMPL_H + +template +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 diff --git a/sound/sources/memsource.h b/sound/sources/memsource.h new file mode 100644 index 0000000000..1e38d90524 --- /dev/null +++ b/sound/sources/memsource.h @@ -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