Moved OpenAL_Sound to cpp file, added delayed buffer creation (prelude for streaming)

This commit is contained in:
Nicolay Korslund 2010-08-17 11:25:26 +02:00
parent c982f701ca
commit 932465442b
6 changed files with 145 additions and 86 deletions

View file

@ -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() {}

View file

@ -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");

View file

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

View file

@ -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.
*/ */

View file

@ -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);
} }

View file

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