forked from mirror/openmw-tes3mp
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
|
||||
itself. Back-ends can use this as a means of sharing data and
|
||||
saving memory. */
|
||||
virtual SoundPtr clone() const = 0;
|
||||
virtual SoundPtr clone() = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~Sound() {}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include "../../stream/filters/buffer_stream.hpp"
|
||||
#include "../../tools/str_exception.hpp"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// ---- Helper functions and classes ----
|
||||
|
@ -69,17 +72,91 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
|
|||
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(bool doSetup)
|
||||
: didSetup(doSetup)
|
||||
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
|
||||
{
|
||||
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;
|
||||
canLoadFile = false;
|
||||
canLoadStream = false;
|
||||
canLoadSource = true;
|
||||
|
||||
ALCdevice *Device;
|
||||
ALCcontext *Context;
|
||||
|
||||
if(doSetup)
|
||||
{
|
||||
// Set up sound system
|
||||
|
@ -90,6 +167,9 @@ OpenAL_Factory::OpenAL_Factory(bool doSetup)
|
|||
fail("Failed to initialize context or device");
|
||||
|
||||
alcMakeContextCurrent(Context);
|
||||
|
||||
device = Device;
|
||||
context = Context;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,8 +179,8 @@ OpenAL_Factory::~OpenAL_Factory()
|
|||
if(didSetup)
|
||||
{
|
||||
alcMakeContextCurrent(NULL);
|
||||
if(Context) alcDestroyContext(Context);
|
||||
if(Device) alcCloseDevice(Device);
|
||||
if(context) alcDestroyContext((ALCcontext*)context);
|
||||
if(device) alcCloseDevice((ALCdevice*)device);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,6 +188,7 @@ OpenAL_Factory::~OpenAL_Factory()
|
|||
|
||||
void OpenAL_Sound::play()
|
||||
{
|
||||
setupBuffer();
|
||||
alSourcePlay(inst);
|
||||
checkALError("starting playback");
|
||||
}
|
||||
|
@ -164,14 +245,16 @@ void OpenAL_Sound::setRepeat(bool rep)
|
|||
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));
|
||||
}
|
||||
|
||||
// Constructor used for cloned sounds
|
||||
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
|
||||
: bufferID(buf), refCnt(ref)
|
||||
: bufferID(buf), refCnt(ref), streaming(false)
|
||||
{
|
||||
// Increase the reference count
|
||||
assert(ref != NULL);
|
||||
|
@ -185,12 +268,35 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref)
|
|||
}
|
||||
|
||||
// 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
|
||||
int fmt, rate;
|
||||
getALFormat(input, fmt, rate);
|
||||
|
||||
if(streaming)
|
||||
{
|
||||
// To be done
|
||||
}
|
||||
|
||||
// NON-STREAMING
|
||||
|
||||
// Set up the OpenAL buffer
|
||||
alGenBuffers(1, &bufferID);
|
||||
checkALError("generating buffer");
|
||||
|
@ -205,17 +311,16 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input)
|
|||
else
|
||||
{
|
||||
// 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
|
||||
alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate);
|
||||
}
|
||||
|
||||
checkALError("loading sound buffer");
|
||||
|
||||
// Create a source
|
||||
alGenSources(1, &inst);
|
||||
checkALError("creating source");
|
||||
// We're done with the input stream, release the pointer
|
||||
input.reset();
|
||||
|
||||
alSourcei(inst, AL_BUFFER, bufferID);
|
||||
checkALError("assigning buffer");
|
||||
|
||||
|
|
|
@ -3,57 +3,13 @@
|
|||
|
||||
#include "../output.hpp"
|
||||
|
||||
#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;
|
||||
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
|
||||
{
|
||||
ALCdevice *Device;
|
||||
ALCcontext *Context;
|
||||
void *device;
|
||||
void *context;
|
||||
bool didSetup;
|
||||
|
||||
public:
|
||||
|
@ -65,24 +21,12 @@ class OpenAL_Factory : public SoundFactory
|
|||
|
||||
SoundPtr load(const std::string &file) { assert(0); }
|
||||
SoundPtr load(Stream::StreamPtr input) { assert(0); }
|
||||
SoundPtr loadRaw(SampleSourcePtr input)
|
||||
{ return SoundPtr(new OpenAL_Sound(input)); }
|
||||
SoundPtr loadRaw(SampleSourcePtr input);
|
||||
|
||||
void update() {}
|
||||
void update();
|
||||
void 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);
|
||||
}
|
||||
float ux, float uy, float uz);
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
|
|
@ -17,11 +17,10 @@ using namespace Mangle::Stream;
|
|||
support), but that's more messy.
|
||||
|
||||
- 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
|
||||
nice shortcut over using those libraries - OTOH it's another
|
||||
dependency. But it also means we could scavenge the mpg123 source
|
||||
for these parts if we want them.
|
||||
dependency.
|
||||
|
||||
- we could implement seek(), tell() and size(), but they aren't
|
||||
really necessary. Furthermore, since the returned size is only a
|
||||
|
|
|
@ -25,15 +25,23 @@ int main()
|
|||
|
||||
cout << "Playing\n";
|
||||
|
||||
// This initializes OpenAL for us, and serves no other purpose.
|
||||
OpenAL_Factory mg;
|
||||
|
||||
OpenAL_Sound snd(source);
|
||||
SoundPtr snd = mg.loadRaw(source);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,11 @@ class BufferStream : public MemoryStream
|
|||
std::vector<char> buffer;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -37,7 +41,6 @@ class BufferStream : public MemoryStream
|
|||
{
|
||||
// We DON'T know how big the stream is. We'll have to read
|
||||
// it in increments.
|
||||
const unsigned int ADD = 32*1024;
|
||||
size_t len=0, newlen;
|
||||
|
||||
while(!input->eof())
|
||||
|
@ -52,7 +55,7 @@ class BufferStream : public MemoryStream
|
|||
|
||||
// If we read less than expected, we should be at the
|
||||
// end of the stream
|
||||
assert(read == ADD || input->eof());
|
||||
assert(read == ADD || (read < ADD && input->eof()));
|
||||
}
|
||||
|
||||
// Downsize to match the real length
|
||||
|
|
Loading…
Reference in a new issue