mirror of https://github.com/OpenMW/openmw.git
Worked on outputs, OpenAL. VERY wip
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…
Reference in New Issue