From cb638cd44e71d0601734eac935484273f508b1a1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 28 Dec 2009 17:15:52 +0100 Subject: [PATCH 1/5] Started reworking the sound system. Added sources/ - WIP --- sound/source.h | 63 ++++++++ sound/sources/audiere_source.cpp | 139 ++++++++++++++++++ sound/sources/audiere_source.h | 51 +++++++ sound/sources/ffmpeg_source.cpp | 238 +++++++++++++++++++++++++++++++ sound/sources/ffmpeg_source.h | 47 ++++++ sound/sources/loadertemplate.h | 26 ++++ 6 files changed, 564 insertions(+) create mode 100644 sound/source.h create mode 100644 sound/sources/audiere_source.cpp create mode 100644 sound/sources/audiere_source.h create mode 100644 sound/sources/ffmpeg_source.cpp create mode 100644 sound/sources/ffmpeg_source.h create mode 100644 sound/sources/loadertemplate.h 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/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp new file mode 100644 index 0000000000..6ebc4f8813 --- /dev/null +++ b/sound/sources/audiere_source.cpp @@ -0,0 +1,139 @@ +#include "audiere_source.h" + +#include "../../stream/clients/audiere_file.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; + +// --- SampleSource --- + +void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + SampleFormat fmt; + sample->getFormat(*channels, *rate, fmt); + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); +} + +/* + Get data. Since Audiere operates with frames, not bytes, there's a + little conversion magic going on here. We need to make sure we're + 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. + */ +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 + if(pullSize) + { + // pullSize is how much was stored the last time, so skip that. + memcpy(data, pullOver+pullSize, PSIZE-pullSize); + length -= pullSize; + data += pullSize; + } + + // Determine the overshoot up front + pullSize = length % frameSize; + + // Number of whole frames + int frames = length / frameSize; + + // Read the data + int res = sample->read(frames, data); + + 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) + { + // 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; + } + + // 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/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp new file mode 100644 index 0000000000..d53b1ced35 --- /dev/null +++ b/sound/sources/ffmpeg_source.cpp @@ -0,0 +1,238 @@ +// NOT UPDATED - WIP + +#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) {} + +const char* FFM_Exception::what() const throw() +{ return msg.c_str(); } + +FFM_Exception::~FFM_Exception() throw() {} + +static void fail(const std::string &msg) +{ + throw FFM_Exception("FFMpeg exception: " + msg); +} + + +// --- Manager --- + +FFM_InputManager::FFM_InputManager() +{ + if(!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) +{ + std::string msg; + AVCodec *codec; + + empty = false; + + if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) + fail("Error loading audio file " + file); + + if(av_find_stream_info(FmtCtx) < 0) + { + msg = "Error in file stream " + file; + goto err; + } + + // Pick the first audio stream, if any + for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) + { + // Pick the first audio stream + if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + + if(StreamNum == FmtCtx->nb_streams) + fail("File " + file + " didn't contain any audio streams"); + + // Open the decoder + CodecCtx = FmtCtx->streams[StreamNum]->codec; + codec = avcodec_find_decoder(CodecCtx->codec_id); + + if(!codec || avcodec_open(CodecCtx, codec) < 0) + { + msg = "Error loading " + file + ": "; + if(codec) + msg += "coded error"; + else + msg += "no codec found"; + goto err; + } + + // No errors, we're done + return; + + // Handle errors + err: + av_close_input_file(FmtCtx); + fail(msg); +} + +FFM_InputStream::~FFM_InputStream() +{ + avcodec_close(CodecCtx); + av_close_input_file(FmtCtx); +} + +void FFM_InputStream::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) +{ + if(empty) return 0; + + uint32_t left = length; + uint8_t *outPtr = (uint8_t*)data; + + // First, copy over any stored data we might be sitting on + { + int s = storage.size(); + int copy = s; + if(s) + { + // Make sure there's room + if(copy > left) + copy = left; + + // Copy + memcpy(outPtr, &storage[0], copy); + outPtr += copy; + left -= copy; + + // Is there anything left in the storage? + s -= copy; + if(s) + { + assert(left == 0); + + // Move it to the start and resize + memmove(&storage[0], &storage[copy], s); + storage.resize(s); + } + } + } + + // Next, get more input data from stream, and decode it + while(left) + { + AVPacket packet; + + // Get the next packet, if any + if(av_read_frame(FmtCtx, &packet) < 0) + break; + + // We only allow one stream per file at the moment + assert(StreamNum == packet.stream_index); + + // Decode the packet + int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, + &len, packet.data, packet.size); + assert(tmp < 0 || tmp == packet.size); + + // We don't need the input packet any longer + av_free_packet(&packet); + + if(tmp < 0) + fail("Error decoding audio stream"); + + // Copy whatever data we got, and advance the pointer + if(len > 0) + { + // copy = how many bytes do we copy now + int copy = len; + if(copy > left) + copy = left; + + // len = how many bytes are left uncopied + len -= copy; + + // copy data + memcpy(outPtr, outBuf, copy); + + // left = how much space is left in the caller output + // buffer + left -= copy; + outPtr += copy; + assert(left >= 0); + + if(len > 0) + { + // There were uncopied bytes. Store them for later. + assert(left == 0); + storage.resize(len); + memcpy(&storage[0], outBuf, len); + } + } + } + + // End of loop. Return the number of bytes copied. + assert(left <= length); + + // If we're returning less than asked for, then we're done + if(left > 0) + empty = 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..bf32084fa2 --- /dev/null +++ b/sound/sources/ffmpeg_source.h @@ -0,0 +1,47 @@ +#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; + bool empty; + + 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 +typedef SSL_Template AudiereLoader; + +}} // namespaces +#endif diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h new file mode 100644 index 0000000000..95d0e798ca --- /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 X(file); + } + + SampleSource *load(Stream::Stream *input) + { + assert(canLoadStream); + return new X(input); + } +}; + +#endif From 6281685f732f799f25565be8e431f1fab7ee9730 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 10:34:40 +0100 Subject: [PATCH 2/5] Finished source/ --- sound/sources/ffmpeg_source.cpp | 56 +++++++++------------------------ sound/sources/ffmpeg_source.h | 10 ++++-- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index d53b1ced35..af785eaa35 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,14 +1,10 @@ -// NOT UPDATED - WIP - #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 @@ -35,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; @@ -97,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; @@ -105,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 @@ -122,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 @@ -209,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); @@ -229,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 index bf32084fa2..5317c11c02 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -20,7 +20,6 @@ class FFMpegSource : public SampleSource AVFormatContext *FmtCtx; AVCodecContext *CodecCtx; int StreamNum; - bool empty; std::vector storage; @@ -41,7 +40,14 @@ class FFMpegSource : public SampleSource #include "loadertemplate.h" /// A factory that loads FFMpegSources from file -typedef SSL_Template AudiereLoader; +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 From 4ee198d66c8b54527fa35e1eb55e127f33c1a8a4 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 11:56:14 +0100 Subject: [PATCH 3/5] Added SampleBuffer (experimental) --- sound/buffer.h | 57 ++++++++++++++++++++++++++++++++++ sound/sources/loadertemplate.h | 6 ++-- sound/sources/memsource.h | 49 +++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 sound/buffer.h create mode 100644 sound/sources/memsource.h 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/sources/loadertemplate.h b/sound/sources/loadertemplate.h index 95d0e798ca..0b668fffa6 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -1,7 +1,7 @@ #ifndef SSL_TEMPL_H #define SSL_TEMPL_H -template +template class SSL_Template : public SampleSourceLoader { SSL_Template() @@ -13,13 +13,13 @@ class SSL_Template : public SampleSourceLoader SampleSource *load(const std::string &file) { assert(canLoadFile); - return new X(file); + return new SourceT(file); } SampleSource *load(Stream::Stream *input) { assert(canLoadStream); - return new X(input); + return new SourceT(input); } }; 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 From fb88d9ef0e3ec4750cd2f8eca1c01a3db13f7377 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 13:12:48 +0100 Subject: [PATCH 4/5] Deleted a bunch of files, started on output --- sound/input.h | 83 -------- sound/{sound.h => output.h} | 107 +++------- sound/servers/.gitignore | 1 - sound/servers/audiere_imp.cpp | 95 --------- sound/servers/audiere_imp.h | 77 ------- sound/servers/input_audiere.cpp | 139 ------------- sound/servers/input_audiere.h | 59 ------ sound/servers/input_ffmpeg.cpp | 224 -------------------- sound/servers/input_ffmpeg.h | 75 ------- sound/servers/input_filter.h | 80 -------- sound/servers/openal_audiere.h | 29 --- sound/servers/openal_ffmpeg.h | 28 --- sound/servers/output_openal.cpp | 353 -------------------------------- sound/servers/output_openal.h | 125 ----------- 14 files changed, 34 insertions(+), 1441 deletions(-) delete mode 100644 sound/input.h rename sound/{sound.h => output.h} (52%) delete mode 100644 sound/servers/.gitignore delete mode 100644 sound/servers/audiere_imp.cpp delete mode 100644 sound/servers/audiere_imp.h delete mode 100644 sound/servers/input_audiere.cpp delete mode 100644 sound/servers/input_audiere.h delete mode 100644 sound/servers/input_ffmpeg.cpp delete mode 100644 sound/servers/input_ffmpeg.h delete mode 100644 sound/servers/input_filter.h delete mode 100644 sound/servers/openal_audiere.h delete mode 100644 sound/servers/openal_ffmpeg.h delete mode 100644 sound/servers/output_openal.cpp delete mode 100644 sound/servers/output_openal.h 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/.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.cpp b/sound/servers/input_audiere.cpp deleted file mode 100644 index c48f45013b..0000000000 --- a/sound/servers/input_audiere.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "input_audiere.h" -#include - -#include "../../stream/clients/audiere_file.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; - -// --- InputManager --- - -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) -{ - SampleFormat fmt; - sample->getFormat(*channels, *rate, fmt); - if(fmt == SF_U8) - *bits = 8; - else if(fmt == SF_S16) - *bits = 16; - else assert(0); -} - -/* - Get data. Since Audiere operates with frames, not bytes, there's a - little conversion magic going on here. We need to make sure we're - 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) -{ - char *data = (char*)_data; - - // Move the remains from the last operation first - if(pullSize) - { - // pullSize is how much was stored the last time, so skip that. - memcpy(data, pullOver+pullSize, PSIZE-pullSize); - length -= pullSize; - data += pullSize; - } - - // Determine the overshoot up front - pullSize = length % frameSize; - - // Number of whole frames - int frames = length / frameSize; - - // Read the data - int res = sample->read(frames, data); - - // Are we missing data? If resread(1, pullOver) != 0)) - { - // Now, move as much of it as we can fit into the output - // data - memcpy(data+length-pullSize, pullOver, pullSize); - } - else pullSize = 0; - - // Return the total number of bytes stored - return frameSize*res + pullSize; -} 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.cpp b/sound/servers/input_ffmpeg.cpp deleted file mode 100644 index f29ddfdb64..0000000000 --- a/sound/servers/input_ffmpeg.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#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; - -FFM_Exception::FFM_Exception(const std::string &m) - : msg(m) {} - -const char* FFM_Exception::what() const throw() -{ return msg.c_str(); } - -FFM_Exception::~FFM_Exception() throw() {} - -static void fail(const std::string &msg) -{ - throw FFM_Exception("FFMpeg exception: " + msg); -} - - -// --- Manager --- - -FFM_InputManager::FFM_InputManager() -{ - if(!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) -{ - std::string msg; - AVCodec *codec; - - empty = false; - - if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) - fail("Error loading audio file " + file); - - if(av_find_stream_info(FmtCtx) < 0) - { - msg = "Error in file stream " + file; - goto err; - } - - // Pick the first audio stream, if any - for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) - { - // Pick the first audio stream - if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) - break; - } - - if(StreamNum == FmtCtx->nb_streams) - fail("File " + file + " didn't contain any audio streams"); - - // Open the decoder - CodecCtx = FmtCtx->streams[StreamNum]->codec; - codec = avcodec_find_decoder(CodecCtx->codec_id); - - if(!codec || avcodec_open(CodecCtx, codec) < 0) - { - msg = "Error loading " + file + ": "; - if(codec) - msg += "coded error"; - else - msg += "no codec found"; - goto err; - } - - // No errors, we're done - return; - - // Handle errors - err: - av_close_input_file(FmtCtx); - fail(msg); -} - -FFM_InputStream::~FFM_InputStream() -{ - avcodec_close(CodecCtx); - av_close_input_file(FmtCtx); -} - -void FFM_InputStream::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) -{ - if(empty) return 0; - - uint32_t left = length; - uint8_t *outPtr = (uint8_t*)data; - - // First, copy over any stored data we might be sitting on - { - int s = storage.size(); - int copy = s; - if(s) - { - // Make sure there's room - if(copy > left) - copy = left; - - // Copy - memcpy(outPtr, &storage[0], copy); - outPtr += copy; - left -= copy; - - // Is there anything left in the storage? - s -= copy; - if(s) - { - assert(left == 0); - - // Move it to the start and resize - memmove(&storage[0], &storage[copy], s); - storage.resize(s); - } - } - } - - // Next, get more input data from stream, and decode it - while(left) - { - AVPacket packet; - - // Get the next packet, if any - if(av_read_frame(FmtCtx, &packet) < 0) - break; - - // We only allow one stream per file at the moment - assert(StreamNum == packet.stream_index); - - // Decode the packet - int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; - int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, - &len, packet.data, packet.size); - assert(tmp < 0 || tmp == packet.size); - - // We don't need the input packet any longer - av_free_packet(&packet); - - if(tmp < 0) - fail("Error decoding audio stream"); - - // Copy whatever data we got, and advance the pointer - if(len > 0) - { - // copy = how many bytes do we copy now - int copy = len; - if(copy > left) - copy = left; - - // len = how many bytes are left uncopied - len -= copy; - - // copy data - memcpy(outPtr, outBuf, copy); - - // left = how much space is left in the caller output - // buffer - left -= copy; - outPtr += copy; - assert(left >= 0); - - if(len > 0) - { - // There were uncopied bytes. Store them for later. - assert(left == 0); - storage.resize(len); - memcpy(&storage[0], outBuf, len); - } - } - } - - // End of loop. Return the number of bytes copied. - assert(left <= length); - - // If we're returning less than asked for, then we're done - if(left > 0) - empty = true; - - return length - left; -} - -void FFM_InputStream::drop() -{ delete this; } 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/input_filter.h b/sound/servers/input_filter.h deleted file mode 100644 index 455a60e14f..0000000000 --- a/sound/servers/input_filter.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef MANGLE_INPUT_FILTER_H -#define MANGLE_INPUT_FILTER_H - -#include "../sound.h" - -#include - -namespace Mangle { -namespace Sound { - -/** - @brief This filter class adds file loading capabilities to a - Sound::Manager class, by associating an InputManager with it. - - The class takes an existing Manager able to load streams, and - associates an InputManager with it. The combined class is able to - load files directly. - - Example: - \code - - // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot - // decode sound files on its own. - InputFilter mg(new OpenAL_Manager, new FFM_InputManager); - - // We can now load filenames directly. - mg.load("file1.mp3"); - \endcode -*/ -class InputFilter : public Manager -{ - protected: - Manager *snd; - InputManager *inp; - - public: - /// Empty constructor - InputFilter() {} - - /// Assign an input manager and a sound manager to this object - InputFilter(Manager *_snd, InputManager *_inp) - { set(_snd, _inp); } - - /// Assign an input manager and a sound manager to this object - void set(Manager *_snd, InputManager *_inp) - { - inp = _inp; - snd = _snd; - - // Set capabilities - needsUpdate = snd->needsUpdate; - has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; - canLoadStream = inp->canLoadStream; - - // Both these should be true, or the use of this class is pretty - // pointless - canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; - assert(canLoadSource && canLoadFile); - } - - virtual Sound *load(const std::string &file, bool stream=false) - { return load(inp->load(file), stream); } - - virtual Sound *load(Stream::Stream *input, bool stream=false) - { return load(inp->load(input), stream); } - - virtual Sound *load(InputSource *input, bool stream=false) - { return snd->load(input, stream); } - - virtual void update() { snd->update(); } - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) - { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } -}; - -}} -#endif diff --git a/sound/servers/openal_audiere.h b/sound/servers/openal_audiere.h deleted file mode 100644 index 65947b22fc..0000000000 --- a/sound/servers/openal_audiere.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H - -#include "input_filter.h" -#include "input_audiere.h" -#include "output_openal.h" - -namespace Mangle { -namespace Sound { - -/// A InputFilter that adds audiere decoding to OpenAL. Audiere has -/// it's own output, but OpenAL sports 3D and other advanced features. -class OpenAL_Audiere_Manager : public InputFilter -{ - public: - OpenAL_Audiere_Manager() - { - set(new OpenAL_Manager, - new AudiereInput); - } - ~OpenAL_Audiere_Manager() - { - delete snd; - delete inp; - } -}; - -}} -#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.cpp b/sound/servers/output_openal.cpp deleted file mode 100644 index 06a8edca80..0000000000 --- a/sound/servers/output_openal.cpp +++ /dev/null @@ -1,353 +0,0 @@ -#include "output_openal.h" -#include - -#include - -using namespace Mangle::Sound; - - -// ---- Helper functions and classes ---- - -class OpenAL_Exception : public std::exception -{ - std::string msg; - - public: - - OpenAL_Exception(const std::string &m) : msg(m) {} - ~OpenAL_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw OpenAL_Exception("OpenAL exception: " + msg); -} - -static void checkALError(const std::string &msg) -{ - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - fail("\"" + std::string(alGetString(err)) + "\" while " + msg); -} - -static void getALFormat(InputStream *inp, int &fmt, int &rate) -{ - int ch, bits; - inp->getInfo(&rate, &ch, &bits); - - fmt = 0; - - if(bits == 8) - { - if(ch == 1) fmt = AL_FORMAT_MONO8; - if(ch == 2) fmt = AL_FORMAT_STEREO8; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); - } - } - if(bits == 16) - { - if(ch == 1) fmt = AL_FORMAT_MONO16; - if(ch == 2) fmt = AL_FORMAT_STEREO16; - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); - } - } - - if(fmt == 0) - fail("Unsupported input format"); -} - - -// ---- Manager ---- - -OpenAL_Manager::OpenAL_Manager() - : Context(NULL), Device(NULL) -{ - 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() -{ - // Deinitialize sound system - alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); -} - -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() -{ - // Loop through all the streaming sounds and update them - LST::iterator it, next; - for(it = streaming.begin(); - it != streaming.end(); - it++) - { - (*it)->update(); - } -} - -void OpenAL_Manager::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); - checkALError("setting listener position"); -} - -OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) -{ - streaming.push_front(inst); - return streaming.begin(); -} - -void OpenAL_Manager::remove_stream(LST::iterator it) -{ - streaming.erase(it); -} - - -// ---- 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) -{ - assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); - - if(stream) - return new OpenAL_Stream_Instance(source->getStream(), owner); - - // Load the buffer if it hasn't been done already - if(bufferID == 0) - { - // Get an input stream and load the file from it - InputStream *inp = source->getStream(); - - std::vector buffer; - - // Add 32 kb at each increment - const int ADD = 32*1024; - - // Fill the buffer. We increase the buffer until it's large - // enough to fit all the data. - while(true) - { - // Increase the buffer - int oldlen = buffer.size(); - buffer.resize(oldlen+ADD); - - // Read the data - size_t len = inp->getData(&buffer[oldlen], ADD); - - // If we read less than requested, we're done. - if(len < ADD) - { - // Downsize the buffer to the right size - buffer.resize(oldlen+len); - break; - } - } - - // Get the format - int fmt, rate; - getALFormat(inp, fmt, rate); - - // We don't need the file anymore - inp->drop(); - source->drop(); - source = NULL; - - // Move the data into OpenAL - alGenBuffers(1, &bufferID); - alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); - checkALError("loading sound buffer"); - } // End of buffer loading - - // At this point, the file data has been loaded into the buffer - // in 'bufferID', and we should be ready to go. - assert(bufferID != 0); - - return new OpenAL_Simple_Instance(bufferID); -} - - -// ---- 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) -{ - // Create instance and associate buffer - alGenSources(1, &inst); - alSourcei(inst, AL_BUFFER, buf); -} - -OpenAL_Simple_Instance::~OpenAL_Simple_Instance() -{ - // Stop - alSourceStop(inst); - - // Return sound - alDeleteSources(1, &inst); -} - - -// ---- OpenAL_Stream_Instance ---- - -OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, - OpenAL_Manager *_owner) - : stream(_stream), owner(_owner) -{ - // Deduce the file format from the stream info - getALFormat(stream, fmt, rate); - - // Create the buffers and the sound instance - alGenBuffers(BUFS, bufs); - alGenSources(1, &inst); - - checkALError("initializing"); - - // Fill the buffers and que them - for(int i=0; iadd_stream(this); -} - -void OpenAL_Stream_Instance::queueBuffer(ALuint bId) -{ - char buf[SIZE]; - - // Get the data - int len = stream->getData(buf, SIZE); - if(len == 0) - return; - - // .. and stash it - alBufferData(bId, fmt, buf, len, rate); - alSourceQueueBuffers(inst, 1, &bId); -} - -OpenAL_Stream_Instance::~OpenAL_Stream_Instance() -{ - // Remove ourselves from streaming list - owner->remove_stream(lit); - - // Stop - alSourceStop(inst); - - // Kill the input stream - stream->drop(); - - // Return sound - alDeleteSources(1, &inst); - - // Delete buffers - alDeleteBuffers(BUFS, bufs); -} - -void OpenAL_Stream_Instance::update() -{ - ALint count; - alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); - - for(int i = 0;i < count;i++) - { - // Unque a finished buffer - ALuint bId; - alSourceUnqueueBuffers(inst, 1, &bId); - - // Queue a new buffer - queueBuffer(bId); - } -} 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 From c8256e3bb31f161937b47cadbcffe9b8f3dbf016 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 15:54:05 +0100 Subject: [PATCH 5/5] Worked on outputs, OpenAL. VERY wip --- sound/outputs/input_filter.h | 80 ++++++++++ sound/outputs/openal_audiere.h | 29 ++++ sound/outputs/openal_out.cpp | 283 +++++++++++++++++++++++++++++++++ sound/outputs/openal_out.h | 109 +++++++++++++ 4 files changed, 501 insertions(+) create mode 100644 sound/outputs/input_filter.h create mode 100644 sound/outputs/openal_audiere.h create mode 100644 sound/outputs/openal_out.cpp create mode 100644 sound/outputs/openal_out.h diff --git a/sound/outputs/input_filter.h b/sound/outputs/input_filter.h new file mode 100644 index 0000000000..455a60e14f --- /dev/null +++ b/sound/outputs/input_filter.h @@ -0,0 +1,80 @@ +#ifndef MANGLE_INPUT_FILTER_H +#define MANGLE_INPUT_FILTER_H + +#include "../sound.h" + +#include + +namespace Mangle { +namespace Sound { + +/** + @brief This filter class adds file loading capabilities to a + Sound::Manager class, by associating an InputManager with it. + + The class takes an existing Manager able to load streams, and + associates an InputManager with it. The combined class is able to + load files directly. + + Example: + \code + + // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot + // decode sound files on its own. + InputFilter mg(new OpenAL_Manager, new FFM_InputManager); + + // We can now load filenames directly. + mg.load("file1.mp3"); + \endcode +*/ +class InputFilter : public Manager +{ + protected: + Manager *snd; + InputManager *inp; + + public: + /// Empty constructor + InputFilter() {} + + /// Assign an input manager and a sound manager to this object + InputFilter(Manager *_snd, InputManager *_inp) + { set(_snd, _inp); } + + /// Assign an input manager and a sound manager to this object + void set(Manager *_snd, InputManager *_inp) + { + inp = _inp; + snd = _snd; + + // Set capabilities + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + canLoadStream = inp->canLoadStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } + + virtual Sound *load(const std::string &file, bool stream=false) + { return load(inp->load(file), stream); } + + virtual Sound *load(Stream::Stream *input, bool stream=false) + { return load(inp->load(input), stream); } + + virtual Sound *load(InputSource *input, bool stream=false) + { return snd->load(input, stream); } + + virtual void update() { snd->update(); } + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } +}; + +}} +#endif diff --git a/sound/outputs/openal_audiere.h b/sound/outputs/openal_audiere.h new file mode 100644 index 0000000000..65947b22fc --- /dev/null +++ b/sound/outputs/openal_audiere.h @@ -0,0 +1,29 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.h" +#include "input_audiere.h" +#include "output_openal.h" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds audiere decoding to OpenAL. Audiere has +/// it's own output, but OpenAL sports 3D and other advanced features. +class OpenAL_Audiere_Manager : public InputFilter +{ + public: + OpenAL_Audiere_Manager() + { + set(new OpenAL_Manager, + new AudiereInput); + } + ~OpenAL_Audiere_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp new file mode 100644 index 0000000000..5d3633e753 --- /dev/null +++ b/sound/outputs/openal_out.cpp @@ -0,0 +1,283 @@ +#include "openal_out.h" +#include + +// ALuint bufferID; + +using namespace Mangle::Sound; + +// ---- Helper functions and classes ---- + +class OpenAL_Exception : public std::exception +{ + std::string msg; + + public: + + OpenAL_Exception(const std::string &m) : msg(m) {} + ~OpenAL_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw OpenAL_Exception("OpenAL exception: " + msg); +} + +static void checkALError(const std::string &msg) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); +} + +static void getALFormat(InputStream *inp, int &fmt, int &rate) +{ + int ch, bits; + inp->getInfo(&rate, &ch, &bits); + + fmt = 0; + + if(bits == 8) + { + if(ch == 1) fmt = AL_FORMAT_MONO8; + if(ch == 2) fmt = AL_FORMAT_STEREO8; + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); + } + } + if(bits == 16) + { + if(ch == 1) fmt = AL_FORMAT_MONO16; + if(ch == 2) fmt = AL_FORMAT_STEREO16; + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); + } + } + + if(fmt == 0) + fail("Unsupported input format"); +} + +// ---- OpenAL_Sound ---- + +OpenAL_Sound::OpenAL_Sound(SampleSource *input) +{ + +} + +void OpenAL_Sound::play() +{ + alSourcePlay(inst); + checkALError("starting playback"); +} + +void OpenAL_Sound::stop() +{ + alSourceStop(inst); + checkALError("stopping"); +} + +void OpenAL_Sound::pause() +{ + alSourcePause(inst); + checkALError("pausing"); +} + +bool OpenAL_Sound::isPlaying() +{ + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; +} + +void OpenAL_Sound::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_Sound::setPos(float x, float y, float z) +{ + alSource3f(inst, AL_POSITION, x, y, z); + checkALError("setting position"); +} + + + + + + + + + + + + + + + + + + + + +Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +{ + assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + + if(stream) + return new OpenAL_Stream_Instance(source->getStream(), owner); + + // Load the buffer if it hasn't been done already + if(bufferID == 0) + { + // Get an input stream and load the file from it + InputStream *inp = source->getStream(); + + std::vector buffer; + + // Add 32 kb at each increment + const int ADD = 32*1024; + + // Fill the buffer. We increase the buffer until it's large + // enough to fit all the data. + while(true) + { + // Increase the buffer + int oldlen = buffer.size(); + buffer.resize(oldlen+ADD); + + // Read the data + size_t len = inp->getData(&buffer[oldlen], ADD); + + // If we read less than requested, we're done. + if(len < ADD) + { + // Downsize the buffer to the right size + buffer.resize(oldlen+len); + break; + } + } + + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); + + // We don't need the file anymore + inp->drop(); + source->drop(); + source = NULL; + + // Move the data into OpenAL + alGenBuffers(1, &bufferID); + alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); + checkALError("loading sound buffer"); + } // End of buffer loading + + // At this point, the file data has been loaded into the buffer + // in 'bufferID', and we should be ready to go. + assert(bufferID != 0); + + return new OpenAL_Simple_Instance(bufferID); +} + + +// ---- OpenAL_Simple_Instance ---- + +OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) +{ + // Create instance and associate buffer + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, buf); +} + +OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +{ + // Stop + alSourceStop(inst); + + // Return sound + alDeleteSources(1, &inst); +} + + +// ---- OpenAL_Stream_Instance ---- + +OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, + OpenAL_Manager *_owner) + : stream(_stream), owner(_owner) +{ + // Deduce the file format from the stream info + getALFormat(stream, fmt, rate); + + // Create the buffers and the sound instance + alGenBuffers(BUFS, bufs); + alGenSources(1, &inst); + + checkALError("initializing"); + + // Fill the buffers and que them + for(int i=0; iadd_stream(this); +} + +void OpenAL_Stream_Instance::queueBuffer(ALuint bId) +{ + char buf[SIZE]; + + // Get the data + int len = stream->getData(buf, SIZE); + if(len == 0) + return; + + // .. and stash it + alBufferData(bId, fmt, buf, len, rate); + alSourceQueueBuffers(inst, 1, &bId); +} + +OpenAL_Stream_Instance::~OpenAL_Stream_Instance() +{ + // Remove ourselves from streaming list + owner->remove_stream(lit); + + // Stop + alSourceStop(inst); + + // Kill the input stream + stream->drop(); + + // Return sound + alDeleteSources(1, &inst); + + // Delete buffers + alDeleteBuffers(BUFS, bufs); +} + +void OpenAL_Stream_Instance::update() +{ + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + + for(int i = 0;i < count;i++) + { + // Unque a finished buffer + ALuint bId; + alSourceUnqueueBuffers(inst, 1, &bId); + + // Queue a new buffer + queueBuffer(bId); + } +} 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