bsnes/ruby/audio/openal.cpp

211 lines
5.0 KiB
C++

/*
audio.openal (2007-12-26)
author: Nach
contributors: byuu, wertigon, _willow_
*/
#if defined(PLATFORM_MACOSX)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
namespace ruby {
class pAudioOpenAL {
public:
struct {
ALCdevice* handle;
ALCcontext* context;
ALuint source;
ALenum format;
unsigned latency;
unsigned queue_length;
} device;
struct {
uint32_t* data;
unsigned length;
unsigned size;
} buffer;
struct {
bool synchronize;
unsigned frequency;
unsigned latency;
} settings;
bool cap(const string& name) {
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
any get(const string& name) {
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return false;
}
bool set(const string& name, const any& value) {
if(name == Audio::Synchronize) {
settings.synchronize = any_cast<bool>(value);
return true;
}
if(name == Audio::Frequency) {
settings.frequency = any_cast<unsigned>(value);
return true;
}
if(name == Audio::Latency) {
if(settings.latency != any_cast<unsigned>(value)) {
settings.latency = any_cast<unsigned>(value);
update_latency();
}
return true;
}
return false;
}
void sample(uint16_t sl, uint16_t sr) {
buffer.data[buffer.length++] = sl + (sr << 16);
if(buffer.length < buffer.size) return;
ALuint albuffer = 0;
int processed = 0;
while(true) {
alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
//wait for buffer playback to catch up to sample generation if not synchronizing
if(settings.synchronize == false || device.queue_length < 3) break;
}
if(device.queue_length < 3) {
alGenBuffers(1, &albuffer);
alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency);
alSourceQueueBuffers(device.source, 1, &albuffer);
device.queue_length++;
}
ALint playing;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing != AL_PLAYING) alSourcePlay(device.source);
buffer.length = 0;
}
void clear() {
}
void update_latency() {
if(buffer.data) delete[] buffer.data;
buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5;
buffer.data = new uint32_t[buffer.size];
}
bool init() {
update_latency();
device.queue_length = 0;
bool success = false;
if(device.handle = alcOpenDevice(NULL)) {
if(device.context = alcCreateContext(device.handle, NULL)) {
alcMakeContextCurrent(device.context);
alGenSources(1, &device.source);
//alSourcef (device.source, AL_PITCH, 1.0);
//alSourcef (device.source, AL_GAIN, 1.0);
//alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0);
//alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0);
//alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0);
//alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE);
alListener3f(AL_POSITION, 0.0, 0.0, 0.0);
alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0);
ALfloat listener_orientation[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
alListenerfv(AL_ORIENTATION, listener_orientation);
success = true;
}
}
if(success == false) {
term();
return false;
}
return true;
}
void term() {
if(alIsSource(device.source) == AL_TRUE) {
int playing = 0;
alGetSourcei(device.source, AL_SOURCE_STATE, &playing);
if(playing == AL_PLAYING) {
alSourceStop(device.source);
int queued = 0;
alGetSourcei(device.source, AL_BUFFERS_QUEUED, &queued);
while(queued--) {
ALuint albuffer = 0;
alSourceUnqueueBuffers(device.source, 1, &albuffer);
alDeleteBuffers(1, &albuffer);
device.queue_length--;
}
}
alDeleteSources(1, &device.source);
device.source = 0;
}
if(device.context) {
alcMakeContextCurrent(NULL);
alcDestroyContext(device.context);
device.context = 0;
}
if(device.handle) {
alcCloseDevice(device.handle);
device.handle = 0;
}
if(buffer.data) {
delete[] buffer.data;
buffer.data = 0;
}
}
pAudioOpenAL() {
device.source = 0;
device.handle = 0;
device.context = 0;
device.format = AL_FORMAT_STEREO16;
device.queue_length = 0;
buffer.data = 0;
buffer.length = 0;
buffer.size = 0;
settings.synchronize = true;
settings.frequency = 22050;
settings.latency = 40;
}
~pAudioOpenAL() {
term();
}
};
DeclareAudio(OpenAL)
};