forked from teamnwah/openmw-tes3coop
Moved OpenAL_Sound to cpp file, added delayed buffer creation (prelude for streaming)
This commit is contained in:
parent
c982f701ca
commit
932465442b
6 changed files with 145 additions and 86 deletions
|
@ -72,7 +72,7 @@ class Sound
|
||||||
/** Playback status is not cloned, only the sound data
|
/** Playback status is not cloned, only the sound data
|
||||||
itself. Back-ends can use this as a means of sharing data and
|
itself. Back-ends can use this as a means of sharing data and
|
||||||
saving memory. */
|
saving memory. */
|
||||||
virtual SoundPtr clone() const = 0;
|
virtual SoundPtr clone() = 0;
|
||||||
|
|
||||||
/// Virtual destructor
|
/// Virtual destructor
|
||||||
virtual ~Sound() {}
|
virtual ~Sound() {}
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#include "../../stream/filters/buffer_stream.hpp"
|
#include "../../stream/filters/buffer_stream.hpp"
|
||||||
#include "../../tools/str_exception.hpp"
|
#include "../../tools/str_exception.hpp"
|
||||||
|
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
|
||||||
using namespace Mangle::Sound;
|
using namespace Mangle::Sound;
|
||||||
|
|
||||||
// ---- Helper functions and classes ----
|
// ---- Helper functions and classes ----
|
||||||
|
@ -69,17 +72,91 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
|
||||||
fail("Unsupported input format");
|
fail("Unsupported input format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// OpenAL sound output
|
||||||
|
class OpenAL_Sound : public Sound
|
||||||
|
{
|
||||||
|
ALuint inst;
|
||||||
|
ALuint bufferID;
|
||||||
|
|
||||||
|
// Poor mans reference counting. Might improve this later. When
|
||||||
|
// NULL, the buffer has not been set up yet.
|
||||||
|
int *refCnt;
|
||||||
|
|
||||||
|
bool streaming;
|
||||||
|
|
||||||
|
// Input stream
|
||||||
|
SampleSourcePtr input;
|
||||||
|
|
||||||
|
void setupBuffer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Read samples from the given input buffer
|
||||||
|
OpenAL_Sound(SampleSourcePtr input);
|
||||||
|
|
||||||
|
/// Play an existing buffer, with a given ref counter. Used
|
||||||
|
/// internally for cloning.
|
||||||
|
OpenAL_Sound(ALuint buf, int *ref);
|
||||||
|
|
||||||
|
~OpenAL_Sound();
|
||||||
|
|
||||||
|
void play();
|
||||||
|
void stop();
|
||||||
|
void pause();
|
||||||
|
bool isPlaying() const;
|
||||||
|
void setVolume(float);
|
||||||
|
void setPos(float x, float y, float z);
|
||||||
|
void setPitch(float);
|
||||||
|
void setRepeat(bool);
|
||||||
|
void setStreaming(bool s) { streaming = s; }
|
||||||
|
SoundPtr clone();
|
||||||
|
|
||||||
|
// a = AL_REFERENCE_DISTANCE
|
||||||
|
// b = AL_MAX_DISTANCE
|
||||||
|
// c = ignored
|
||||||
|
void setRange(float a, float b=0.0, float c=0.0);
|
||||||
|
|
||||||
|
/// Not implemented
|
||||||
|
void setPan(float) {}
|
||||||
|
};
|
||||||
|
|
||||||
// ---- OpenAL_Factory ----
|
// ---- OpenAL_Factory ----
|
||||||
|
|
||||||
OpenAL_Factory::OpenAL_Factory(bool doSetup)
|
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
|
||||||
: didSetup(doSetup)
|
|
||||||
{
|
{
|
||||||
needsUpdate = false;
|
return SoundPtr(new OpenAL_Sound(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenAL_Factory::update()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenAL_Factory::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenAL_Factory::OpenAL_Factory(bool doSetup)
|
||||||
|
: device(NULL), context(NULL), didSetup(doSetup)
|
||||||
|
{
|
||||||
|
needsUpdate = true;
|
||||||
has3D = true;
|
has3D = true;
|
||||||
canLoadFile = false;
|
canLoadFile = false;
|
||||||
canLoadStream = false;
|
canLoadStream = false;
|
||||||
canLoadSource = true;
|
canLoadSource = true;
|
||||||
|
|
||||||
|
ALCdevice *Device;
|
||||||
|
ALCcontext *Context;
|
||||||
|
|
||||||
if(doSetup)
|
if(doSetup)
|
||||||
{
|
{
|
||||||
// Set up sound system
|
// Set up sound system
|
||||||
|
@ -90,6 +167,9 @@ OpenAL_Factory::OpenAL_Factory(bool doSetup)
|
||||||
fail("Failed to initialize context or device");
|
fail("Failed to initialize context or device");
|
||||||
|
|
||||||
alcMakeContextCurrent(Context);
|
alcMakeContextCurrent(Context);
|
||||||
|
|
||||||
|
device = Device;
|
||||||
|
context = Context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +179,8 @@ OpenAL_Factory::~OpenAL_Factory()
|
||||||
if(didSetup)
|
if(didSetup)
|
||||||
{
|
{
|
||||||
alcMakeContextCurrent(NULL);
|
alcMakeContextCurrent(NULL);
|
||||||
if(Context) alcDestroyContext(Context);
|
if(context) alcDestroyContext((ALCcontext*)context);
|
||||||
if(Device) alcCloseDevice(Device);
|
if(device) alcCloseDevice((ALCdevice*)device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +188,7 @@ OpenAL_Factory::~OpenAL_Factory()
|
||||||
|
|
||||||
void OpenAL_Sound::play()
|
void OpenAL_Sound::play()
|
||||||
{
|
{
|
||||||
|
setupBuffer();
|
||||||
alSourcePlay(inst);
|
alSourcePlay(inst);
|
||||||
checkALError("starting playback");
|
checkALError("starting playback");
|
||||||
}
|
}
|
||||||
|
@ -164,14 +245,16 @@ void OpenAL_Sound::setRepeat(bool rep)
|
||||||
alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE);
|
alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundPtr OpenAL_Sound::clone() const
|
SoundPtr OpenAL_Sound::clone()
|
||||||
{
|
{
|
||||||
|
setupBuffer();
|
||||||
|
assert(!streaming && "cloning streamed sounds not supported");
|
||||||
return SoundPtr(new OpenAL_Sound(bufferID, refCnt));
|
return SoundPtr(new OpenAL_Sound(bufferID, refCnt));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor used for cloned sounds
|
// Constructor used for cloned sounds
|
||||||
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
|
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
|
||||||
: bufferID(buf), refCnt(ref)
|
: bufferID(buf), refCnt(ref), streaming(false)
|
||||||
{
|
{
|
||||||
// Increase the reference count
|
// Increase the reference count
|
||||||
assert(ref != NULL);
|
assert(ref != NULL);
|
||||||
|
@ -185,12 +268,35 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor used for original (non-cloned) sounds
|
// Constructor used for original (non-cloned) sounds
|
||||||
OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input)
|
OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input)
|
||||||
|
: bufferID(0), refCnt(NULL), streaming(false), input(_input)
|
||||||
{
|
{
|
||||||
|
// Create a source
|
||||||
|
alGenSources(1, &inst);
|
||||||
|
checkALError("creating source");
|
||||||
|
|
||||||
|
// By default, the sound starts out in a buffer-less mode. We don't
|
||||||
|
// create a buffer until the sound is played. This gives the user
|
||||||
|
// the chance to call setStreaming(true) first.
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenAL_Sound::setupBuffer()
|
||||||
|
{
|
||||||
|
if(refCnt != NULL) return;
|
||||||
|
|
||||||
|
assert(input);
|
||||||
|
|
||||||
// Get the format
|
// Get the format
|
||||||
int fmt, rate;
|
int fmt, rate;
|
||||||
getALFormat(input, fmt, rate);
|
getALFormat(input, fmt, rate);
|
||||||
|
|
||||||
|
if(streaming)
|
||||||
|
{
|
||||||
|
// To be done
|
||||||
|
}
|
||||||
|
|
||||||
|
// NON-STREAMING
|
||||||
|
|
||||||
// Set up the OpenAL buffer
|
// Set up the OpenAL buffer
|
||||||
alGenBuffers(1, &bufferID);
|
alGenBuffers(1, &bufferID);
|
||||||
checkALError("generating buffer");
|
checkALError("generating buffer");
|
||||||
|
@ -205,17 +311,16 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Read the entire stream into a temporary buffer first
|
// Read the entire stream into a temporary buffer first
|
||||||
Mangle::Stream::BufferStream buf(input);
|
Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024);
|
||||||
|
|
||||||
// Then copy that into OpenAL
|
// Then copy that into OpenAL
|
||||||
alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate);
|
alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkALError("loading sound buffer");
|
checkALError("loading sound buffer");
|
||||||
|
|
||||||
// Create a source
|
// We're done with the input stream, release the pointer
|
||||||
alGenSources(1, &inst);
|
input.reset();
|
||||||
checkALError("creating source");
|
|
||||||
alSourcei(inst, AL_BUFFER, bufferID);
|
alSourcei(inst, AL_BUFFER, bufferID);
|
||||||
checkALError("assigning buffer");
|
checkALError("assigning buffer");
|
||||||
|
|
||||||
|
|
|
@ -3,57 +3,13 @@
|
||||||
|
|
||||||
#include "../output.hpp"
|
#include "../output.hpp"
|
||||||
|
|
||||||
#include <AL/al.h>
|
|
||||||
#include <AL/alc.h>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
namespace Mangle {
|
namespace Mangle {
|
||||||
namespace Sound {
|
namespace Sound {
|
||||||
|
|
||||||
/// OpenAL sound output
|
|
||||||
class OpenAL_Sound : public Sound
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
ALuint inst;
|
|
||||||
ALuint bufferID;
|
|
||||||
|
|
||||||
// Poor mans reference counting. Might improve this later.
|
|
||||||
int *refCnt;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Read samples from the given input buffer
|
|
||||||
OpenAL_Sound(SampleSourcePtr input);
|
|
||||||
|
|
||||||
/// Play an existing buffer, with a given ref counter. Used
|
|
||||||
/// internally for cloning.
|
|
||||||
OpenAL_Sound(ALuint buf, int *ref);
|
|
||||||
|
|
||||||
~OpenAL_Sound();
|
|
||||||
|
|
||||||
void play();
|
|
||||||
void stop();
|
|
||||||
void pause();
|
|
||||||
bool isPlaying() const;
|
|
||||||
void setVolume(float);
|
|
||||||
void setPos(float x, float y, float z);
|
|
||||||
void setPitch(float);
|
|
||||||
void setRepeat(bool);
|
|
||||||
void setStreaming(bool) {} // Not implemented yet
|
|
||||||
SoundPtr clone() const;
|
|
||||||
|
|
||||||
// a = AL_REFERENCE_DISTANCE
|
|
||||||
// b = AL_MAX_DISTANCE
|
|
||||||
// c = ignored
|
|
||||||
void setRange(float a, float b=0.0, float c=0.0);
|
|
||||||
|
|
||||||
/// Not implemented
|
|
||||||
void setPan(float) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class OpenAL_Factory : public SoundFactory
|
class OpenAL_Factory : public SoundFactory
|
||||||
{
|
{
|
||||||
ALCdevice *Device;
|
void *device;
|
||||||
ALCcontext *Context;
|
void *context;
|
||||||
bool didSetup;
|
bool didSetup;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -65,24 +21,12 @@ class OpenAL_Factory : public SoundFactory
|
||||||
|
|
||||||
SoundPtr load(const std::string &file) { assert(0); }
|
SoundPtr load(const std::string &file) { assert(0); }
|
||||||
SoundPtr load(Stream::StreamPtr input) { assert(0); }
|
SoundPtr load(Stream::StreamPtr input) { assert(0); }
|
||||||
SoundPtr loadRaw(SampleSourcePtr input)
|
SoundPtr loadRaw(SampleSourcePtr input);
|
||||||
{ return SoundPtr(new OpenAL_Sound(input)); }
|
|
||||||
|
|
||||||
void update() {}
|
void update();
|
||||||
void setListenerPos(float x, float y, float z,
|
void setListenerPos(float x, float y, float z,
|
||||||
float fx, float fy, float fz,
|
float fx, float fy, float fz,
|
||||||
float ux, float uy, float uz)
|
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
|
}} // namespaces
|
||||||
|
|
|
@ -17,14 +17,13 @@ using namespace Mangle::Stream;
|
||||||
support), but that's more messy.
|
support), but that's more messy.
|
||||||
|
|
||||||
- the library also supports output, via various other sources,
|
- the library also supports output, via various other sources,
|
||||||
including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this
|
including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this
|
||||||
library as a pure output library (if that is possible) would be a
|
library as a pure output library (if that is possible) would be a
|
||||||
nice shortcut over using those libraries - OTOH it's another
|
nice shortcut over using those libraries - OTOH it's another
|
||||||
dependency. But it also means we could scavenge the mpg123 source
|
dependency.
|
||||||
for these parts if we want them.
|
|
||||||
|
|
||||||
- we could implement seek(), tell() and size(), but they aren't
|
- we could implement seek(), tell() and size(), but they aren't
|
||||||
really necessary. Further more, since the returned size is only a
|
really necessary. Furthermore, since the returned size is only a
|
||||||
guess, it is not safe to rely on it.
|
guess, it is not safe to rely on it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,23 @@ int main()
|
||||||
|
|
||||||
cout << "Playing\n";
|
cout << "Playing\n";
|
||||||
|
|
||||||
// This initializes OpenAL for us, and serves no other purpose.
|
|
||||||
OpenAL_Factory mg;
|
OpenAL_Factory mg;
|
||||||
|
|
||||||
OpenAL_Sound snd(source);
|
SoundPtr snd = mg.loadRaw(source);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
snd.play();
|
// Try setting all kinds of stuff before playing. OpenAL_Sound
|
||||||
|
// uses delayed buffer loading, but these should still work
|
||||||
|
// without a buffer.
|
||||||
|
snd->stop();
|
||||||
|
snd->pause();
|
||||||
|
snd->setVolume(0.8);
|
||||||
|
snd->setPitch(0.9);
|
||||||
|
|
||||||
while(snd.isPlaying())
|
snd->play();
|
||||||
|
|
||||||
|
while(snd->isPlaying())
|
||||||
{
|
{
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,11 @@ class BufferStream : public MemoryStream
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BufferStream(StreamPtr input)
|
/*
|
||||||
|
input = stream to copy
|
||||||
|
ADD = each read increment (for streams without size())
|
||||||
|
*/
|
||||||
|
BufferStream(StreamPtr input, size_t ADD = 32*1024)
|
||||||
{
|
{
|
||||||
assert(input);
|
assert(input);
|
||||||
|
|
||||||
|
@ -37,7 +41,6 @@ class BufferStream : public MemoryStream
|
||||||
{
|
{
|
||||||
// We DON'T know how big the stream is. We'll have to read
|
// We DON'T know how big the stream is. We'll have to read
|
||||||
// it in increments.
|
// it in increments.
|
||||||
const unsigned int ADD = 32*1024;
|
|
||||||
size_t len=0, newlen;
|
size_t len=0, newlen;
|
||||||
|
|
||||||
while(!input->eof())
|
while(!input->eof())
|
||||||
|
@ -52,7 +55,7 @@ class BufferStream : public MemoryStream
|
||||||
|
|
||||||
// If we read less than expected, we should be at the
|
// If we read less than expected, we should be at the
|
||||||
// end of the stream
|
// end of the stream
|
||||||
assert(read == ADD || input->eof());
|
assert(read == ADD || (read < ADD && input->eof()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downsize to match the real length
|
// Downsize to match the real length
|
||||||
|
|
Loading…
Reference in a new issue