Worked on outputs, OpenAL. VERY wip

actorid
Nicolay Korslund 15 years ago
parent fb88d9ef0e
commit c8256e3bb3

@ -0,0 +1,80 @@
#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

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

@ -0,0 +1,283 @@
#include "openal_out.h"
#include <assert.h>
// 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<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_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);
}
}

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