bsnes/ruby/audio/openal.cpp

177 lines
4.8 KiB
C++

#if defined(PLATFORM_MACOS)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
struct AudioOpenAL : AudioDriver {
AudioOpenAL& self = *this;
AudioOpenAL(Audio& super) : AudioDriver(super) {}
~AudioOpenAL() { terminate(); }
auto create() -> bool override {
super.setDevice(hasDevices().first());
super.setChannels(2);
super.setFrequency(48000);
super.setLatency(20);
return initialize();
}
auto driver() -> string override { return "OpenAL"; }
auto ready() -> bool override { return _ready; }
auto hasBlocking() -> bool override { return true; }
auto hasDevices() -> vector<string> override {
vector<string> devices;
if(const char* list = alcGetString(nullptr, ALC_DEVICE_SPECIFIER)) {
while(list && *list) {
devices.append(list);
list += strlen(list) + 1;
}
}
return devices;
}
auto hasChannels() -> vector<uint> override {
return {2};
}
auto hasFrequencies() -> vector<uint> override {
return {44100, 48000, 96000};
}
auto hasLatencies() -> vector<uint> override {
return {20, 40, 60, 80, 100};
}
auto setDevice(string device) -> bool override { return initialize(); }
auto setBlocking(bool blocking) -> bool override { return true; }
auto setFrequency(uint frequency) -> bool override { return initialize(); }
auto setLatency(uint latency) -> bool override { return updateLatency(); }
auto output(const double samples[]) -> void override {
_buffer[_bufferLength] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
_buffer[_bufferLength] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_bufferLength < _bufferSize) return;
ALuint alBuffer = 0;
int processed = 0;
while(true) {
alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
alSourceUnqueueBuffers(_source, 1, &alBuffer);
alDeleteBuffers(1, &alBuffer);
_queueLength--;
}
//wait for buffer playback to catch up to sample generation if not synchronizing
if(!self.blocking || _queueLength < 3) break;
}
if(_queueLength < 3) {
alGenBuffers(1, &alBuffer);
alBufferData(alBuffer, _format, _buffer, _bufferSize * 4, self.frequency);
alSourceQueueBuffers(_source, 1, &alBuffer);
_queueLength++;
}
ALint playing;
alGetSourcei(_source, AL_SOURCE_STATE, &playing);
if(playing != AL_PLAYING) alSourcePlay(_source);
_bufferLength = 0;
}
private:
auto initialize() -> bool {
terminate();
if(!hasDevices().find(self.device)) self.device = hasDevices().first();
_queueLength = 0;
updateLatency();
bool success = false;
if(_openAL = alcOpenDevice(self.device)) {
if(_context = alcCreateContext(_openAL, nullptr)) {
alcMakeContextCurrent(_context);
alGenSources(1, &_source);
//alSourcef (_source, AL_PITCH, 1.0);
//alSourcef (_source, AL_GAIN, 1.0);
//alSource3f(_source, AL_POSITION, 0.0, 0.0, 0.0);
//alSource3f(_source, AL_VELOCITY, 0.0, 0.0, 0.0);
//alSource3f(_source, AL_DIRECTION, 0.0, 0.0, 0.0);
//alSourcef (_source, AL_ROLLOFF_FACTOR, 0.0);
//alSourcei (_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 listenerOrientation[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
alListenerfv(AL_ORIENTATION, listenerOrientation);
success = true;
}
}
if(!success) return terminate(), false;
return _ready = true;
}
auto terminate() -> void {
_ready = false;
if(alIsSource(_source) == AL_TRUE) {
int playing = 0;
alGetSourcei(_source, AL_SOURCE_STATE, &playing);
if(playing == AL_PLAYING) {
alSourceStop(_source);
int queued = 0;
alGetSourcei(_source, AL_BUFFERS_QUEUED, &queued);
while(queued--) {
ALuint alBuffer = 0;
alSourceUnqueueBuffers(_source, 1, &alBuffer);
alDeleteBuffers(1, &alBuffer);
_queueLength--;
}
}
alDeleteSources(1, &_source);
_source = 0;
}
if(_context) {
alcMakeContextCurrent(nullptr);
alcDestroyContext(_context);
_context = nullptr;
}
if(_openAL) {
alcCloseDevice(_openAL);
_openAL = nullptr;
}
delete[] _buffer;
_buffer = nullptr;
}
auto updateLatency() -> bool {
delete[] _buffer;
_bufferSize = self.frequency * self.latency / 1000.0 + 0.5;
_buffer = new uint32_t[_bufferSize]();
return true;
}
bool _ready = false;
ALCdevice* _openAL = nullptr;
ALCcontext* _context = nullptr;
ALuint _source = 0;
ALenum _format = AL_FORMAT_STEREO16;
uint _queueLength = 0;
uint32_t* _buffer = nullptr;
uint _bufferLength = 0;
uint _bufferSize = 0;
};