Deleted a bunch of files, started on output

actorid
Nicolay Korslund 15 years ago
parent 4ee198d66c
commit fb88d9ef0e

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

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

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

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

@ -1,139 +0,0 @@
#include "input_audiere.h"
#include <assert.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;
// --- 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 res<length and we're at the end of the
// stream, then this doesn't apply.
if(res == frames && pullSize &&
// Read one more sample
(sample->read(1, pullOver) != 0))
{
// 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;
}

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

@ -1,224 +0,0 @@
#include "input_ffmpeg.h"
#include <assert.h>
using namespace Mangle::Sound;
// Static output buffer. Not thread safe, but supports multiple
// streams operated from the same thread.
static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
bool FFM_InputManager::init = false;
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; }

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

@ -1,80 +0,0 @@
#ifndef MANGLE_INPUT_FILTER_H
#define MANGLE_INPUT_FILTER_H
#include "../sound.h"
#include <assert.h>
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

@ -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

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

@ -1,353 +0,0 @@
#include "output_openal.h"
#include <assert.h>
#include <vector>
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<unsigned char> 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; i<BUFS; i++)
queueBuffer(bufs[i]);
checkALError("buffering initial data");
// Add ourselves to the stream list
lit = owner->add_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);
}
}

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