mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-19 08:41:23 +02:00
Update to 20180730 release.
byuu says: These WIPs-within-WIPs are getting more and more broken ... this isn't going the way I wanted. But ... this time around, I've revamped the entire ruby API again, to solve a bunch of tough problems that have always made using ruby really clunky. But there are *so many* ruby drivers that it's going to take a long time to work through them all. This WIP is only going to run bsnes, and only on FreeBSD, and only with some drivers. hiro's Application::initialize() now calls hiro::initialize(), which you define inside of your hiro apps. This lets you call Application::setName(...) before anything else in hiro runs. This is essential on Xorg to set program icons, for instance. With the ruby rewrite and the change to hiro, I can get away from the need to make everything in bsnes/higan pointers to objects, and can now just declare them as regular objects.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include <nall/windows/registry.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
struct AudioASIO : Audio {
|
||||
|
282
ruby/audio/audio.cpp
Normal file
282
ruby/audio/audio.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
#if defined(AUDIO_ALSA)
|
||||
#include <ruby/audio/alsa.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_AO)
|
||||
#include <ruby/audio/ao.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_ASIO)
|
||||
#include <ruby/audio/asio.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
#include <ruby/audio/directsound.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OPENAL)
|
||||
#include <ruby/audio/openal.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OSS)
|
||||
#include <ruby/audio/oss.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIO)
|
||||
#include <ruby/audio/pulseaudio.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
#include <ruby/audio/pulseaudiosimple.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WASAPI)
|
||||
#include <ruby/audio/wasapi.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
#include <ruby/audio/xaudio2.cpp>
|
||||
#endif
|
||||
|
||||
namespace ruby {
|
||||
|
||||
auto Audio::setExclusive(bool exclusive) -> bool {
|
||||
if(driver->exclusive == exclusive) return true;
|
||||
if(!driver->hasExclusive()) return false;
|
||||
if(!driver->setExclusive(driver->exclusive = exclusive)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setContext(uintptr context) -> bool {
|
||||
if(driver->context == context) return true;
|
||||
if(!driver->hasContext()) return false;
|
||||
if(!driver->setContext(driver->context = context)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setDevice(string device) -> bool {
|
||||
if(driver->device == device) return true;
|
||||
if(!driver->hasDevice(device)) return false;
|
||||
if(!driver->setDevice(driver->device = device)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setBlocking(bool blocking) -> bool {
|
||||
if(driver->blocking == blocking) return true;
|
||||
if(!driver->hasBlocking()) return false;
|
||||
if(!driver->setBlocking(driver->blocking = blocking)) return false;
|
||||
for(auto& resampler : resamplers) resampler.reset(driver->frequency);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setDynamic(bool dynamic) -> bool {
|
||||
if(driver->dynamic == dynamic) return true;
|
||||
if(!driver->hasDynamic()) return false;
|
||||
if(!driver->setDynamic(driver->dynamic = dynamic)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setChannels(uint channels) -> bool {
|
||||
if(driver->channels == channels) return true;
|
||||
if(!driver->hasChannels(channels)) return false;
|
||||
if(!driver->setChannels(driver->channels = channels)) return false;
|
||||
resamplers.reset();
|
||||
resamplers.resize(channels);
|
||||
for(auto& resampler : resamplers) resampler.reset(driver->frequency);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setFrequency(uint frequency) -> bool {
|
||||
if(driver->frequency == frequency) return true;
|
||||
if(!driver->hasFrequency(frequency)) return false;
|
||||
if(!driver->setFrequency(driver->frequency = frequency)) return false;
|
||||
for(auto& resampler : resamplers) resampler.reset(driver->frequency);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Audio::setLatency(uint latency) -> bool {
|
||||
if(driver->latency == latency) return true;
|
||||
if(!driver->hasLatency(latency)) return false;
|
||||
if(!driver->setLatency(driver->latency = latency)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto Audio::clear() -> void {
|
||||
for(auto& resampler : resamplers) resampler.reset(driver->frequency);
|
||||
return driver->clear();
|
||||
}
|
||||
|
||||
auto Audio::level() -> double {
|
||||
return driver->level();
|
||||
}
|
||||
|
||||
auto Audio::output(const double samples[]) -> void {
|
||||
if(!driver->dynamic) return driver->output(samples);
|
||||
|
||||
auto maxDelta = 0.005;
|
||||
double fillLevel = driver->level();
|
||||
double dynamicFrequency = ((1.0 - maxDelta) + 2.0 * fillLevel * maxDelta) * driver->frequency;
|
||||
for(auto& resampler : resamplers) {
|
||||
resampler.setInputFrequency(dynamicFrequency);
|
||||
resampler.write(*samples++);
|
||||
}
|
||||
|
||||
while(resamplers.first().pending()) {
|
||||
double samples[driver->channels];
|
||||
for(uint n : range(driver->channels)) samples[n] = resamplers[n].read();
|
||||
driver->output(samples);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto Audio::create(string driver) -> bool {
|
||||
reset();
|
||||
if(!driver) driver = optimalDriver();
|
||||
|
||||
#if defined(AUDIO_ALSA)
|
||||
if(driver == "ALSA") self.driver = new AudioALSA(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_AO)
|
||||
if(driver == "libao") self.driver = new AudioAO(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_ASIO)
|
||||
if(driver == "ASIO") self.driver = new AudioASIO(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
if(driver == "DirectSound") self.driver = new AudioDirectSound(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OPENAL)
|
||||
if(driver == "OpenAL") self.driver = new AudioOpenAL(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OSS)
|
||||
if(driver == "OSS") self.driver = new AudioOSS(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIO)
|
||||
if(driver == "PulseAudio") self.driver = new AudioPulseAudio(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
if(driver == "PulseAudioSimple") self.driver = new AudioPulseAudioSimple(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WASAPI)
|
||||
if(driver == "WASAPI") self.driver = new AudioWASAPI(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
if(driver == "XAudio2") self.driver = new AudioXAudio2(*this);
|
||||
#endif
|
||||
|
||||
if(!self.driver) self.driver = new AudioDriver(*this);
|
||||
|
||||
return self.driver->create();
|
||||
}
|
||||
|
||||
auto Audio::hasDrivers() -> vector<string> {
|
||||
return {
|
||||
|
||||
#if defined(AUDIO_ASIO)
|
||||
"ASIO",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WASAPI)
|
||||
"WASAPI",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
"XAudio2",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
"DirectSound",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_ALSA)
|
||||
"ALSA",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OSS)
|
||||
"OSS",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_OPENAL)
|
||||
"OpenAL",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIO)
|
||||
"PulseAudio",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
"PulseAudioSimple",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_AO)
|
||||
"libao",
|
||||
#endif
|
||||
|
||||
"None"};
|
||||
}
|
||||
|
||||
auto Audio::optimalDriver() -> string {
|
||||
#if defined(AUDIO_ASIO)
|
||||
return "ASIO";
|
||||
#elif defined(AUDIO_WASAPI)
|
||||
return "WASAPI";
|
||||
#elif defined(AUDIO_XAUDIO2)
|
||||
return "XAudio2";
|
||||
#elif defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
return "OSS";
|
||||
#elif defined(AUDIO_OPENAL)
|
||||
return "OpenAL";
|
||||
#elif defined(AUDIO_PULSEAUDIO)
|
||||
return "PulseAudio";
|
||||
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
return "PulseAudioSimple";
|
||||
#elif defined(AUDIO_AO)
|
||||
return "libao";
|
||||
#else
|
||||
return "None";
|
||||
#endif
|
||||
}
|
||||
|
||||
auto Audio::safestDriver() -> string {
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound";
|
||||
#elif defined(AUDIO_WASAPI)
|
||||
return "WASAPI";
|
||||
#elif defined(AUDIO_XAUDIO2)
|
||||
return "XAudio2";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
return "OSS";
|
||||
#elif defined(AUDIO_OPENAL)
|
||||
return "OpenAL";
|
||||
#elif defined(AUDIO_PULSEAUDIO)
|
||||
return "PulseAudio";
|
||||
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
return "PulseAudioSimple";
|
||||
#elif defined(AUDIO_AO)
|
||||
return "libao";
|
||||
#elif defined(AUDIO_ASIO)
|
||||
return "ASIO";
|
||||
#else
|
||||
return "None";
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
105
ruby/audio/audio.hpp
Normal file
105
ruby/audio/audio.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
struct Audio;
|
||||
|
||||
struct AudioDriver {
|
||||
AudioDriver(Audio& super) : super(super) {}
|
||||
virtual ~AudioDriver() = default;
|
||||
|
||||
virtual auto create() -> bool { return true; }
|
||||
virtual auto driverName() -> string { return "None"; }
|
||||
virtual auto ready() -> bool { return true; }
|
||||
|
||||
virtual auto hasExclusive() -> bool { return false; }
|
||||
virtual auto hasContext() -> bool { return false; }
|
||||
virtual auto hasDevices() -> vector<string> { return {"Default"}; }
|
||||
virtual auto hasBlocking() -> bool { return false; }
|
||||
virtual auto hasDynamic() -> bool { return false; }
|
||||
virtual auto hasChannels() -> vector<uint> { return {0}; }
|
||||
virtual auto hasFrequencies() -> vector<uint> { return {0}; }
|
||||
virtual auto hasLatencies() -> vector<uint> { return {0}; }
|
||||
|
||||
auto hasDevice(string device) -> bool { return (bool)hasDevices().find(device); }
|
||||
auto hasChannels(uint channels) -> bool { return (bool)hasChannels().find(channels); }
|
||||
auto hasFrequency(uint frequency) -> bool { return (bool)hasFrequencies().find(frequency); }
|
||||
auto hasLatency(uint latency) -> bool { return (bool)hasLatencies().find(latency); }
|
||||
|
||||
virtual auto setExclusive(bool exclusive) -> bool { return true; }
|
||||
virtual auto setContext(uintptr context) -> bool { return true; }
|
||||
virtual auto setDevice(string device) -> bool { return true; }
|
||||
virtual auto setBlocking(bool blocking) -> bool { return true; }
|
||||
virtual auto setDynamic(bool dynamic) -> bool { return true; }
|
||||
virtual auto setChannels(uint channels) -> bool { return true; }
|
||||
virtual auto setFrequency(uint frequency) -> bool { return true; }
|
||||
virtual auto setLatency(uint latency) -> bool { return true; }
|
||||
|
||||
virtual auto clear() -> void {}
|
||||
virtual auto level() -> double { return 0.5; }
|
||||
virtual auto output(const double samples[]) -> void {}
|
||||
|
||||
protected:
|
||||
Audio& super;
|
||||
friend class Audio;
|
||||
|
||||
bool exclusive = false;
|
||||
uintptr context = 0;
|
||||
string device = "Default";
|
||||
bool blocking = false;
|
||||
bool dynamic = false;
|
||||
uint channels = 0;
|
||||
uint frequency = 0;
|
||||
uint latency = 0;
|
||||
};
|
||||
|
||||
struct Audio {
|
||||
static auto hasDrivers() -> vector<string>;
|
||||
static auto hasDriver(string driver) -> bool { return (bool)hasDrivers().find(driver); }
|
||||
static auto optimalDriver() -> string;
|
||||
static auto safestDriver() -> string;
|
||||
|
||||
Audio() : self(*this) {}
|
||||
explicit operator bool() const { return (bool)driver; }
|
||||
auto reset() -> void { driver.reset(); }
|
||||
auto create(string driver = "") -> bool;
|
||||
auto driverName() -> string { return driver->driverName(); }
|
||||
auto ready() -> bool { return driver->ready(); }
|
||||
|
||||
auto hasExclusive() -> bool { return driver->hasExclusive(); }
|
||||
auto hasContext() -> bool { return driver->hasContext(); }
|
||||
auto hasDevices() -> vector<string> { return driver->hasDevices(); }
|
||||
auto hasBlocking() -> bool { return driver->hasBlocking(); }
|
||||
auto hasDynamic() -> bool { return driver->hasDynamic(); }
|
||||
auto hasChannels() -> vector<uint> { return driver->hasChannels(); }
|
||||
auto hasFrequencies() -> vector<uint> { return driver->hasFrequencies(); }
|
||||
auto hasLatencies() -> vector<uint> { return driver->hasLatencies(); }
|
||||
|
||||
auto hasDevice(string device) -> bool { return driver->hasDevice(device); }
|
||||
auto hasChannels(uint channels) -> bool { return driver->hasChannels(channels); }
|
||||
auto hasFrequency(uint frequency) -> bool { return driver->hasFrequency(frequency); }
|
||||
auto hasLatency(uint latency) -> bool { return driver->hasLatency(latency); }
|
||||
|
||||
auto exclusive() -> bool { return driver->exclusive; }
|
||||
auto context() -> uintptr { return driver->context; }
|
||||
auto device() -> string { return driver->device; }
|
||||
auto blocking() -> bool { return driver->blocking; }
|
||||
auto dynamic() -> bool { return driver->dynamic; }
|
||||
auto channels() -> uint { return driver->channels; }
|
||||
auto frequency() -> uint { return driver->frequency; }
|
||||
auto latency() -> uint { return driver->latency; }
|
||||
|
||||
auto setExclusive(bool exclusive) -> bool;
|
||||
auto setContext(uintptr context) -> bool;
|
||||
auto setDevice(string device) -> bool;
|
||||
auto setBlocking(bool blocking) -> bool;
|
||||
auto setDynamic(bool dynamic) -> bool;
|
||||
auto setChannels(uint channels) -> bool;
|
||||
auto setFrequency(uint frequency) -> bool;
|
||||
auto setLatency(uint latency) -> bool;
|
||||
|
||||
auto clear() -> void;
|
||||
auto level() -> double;
|
||||
auto output(const double samples[]) -> void;
|
||||
|
||||
protected:
|
||||
Audio& self;
|
||||
unique_pointer<AudioDriver> driver;
|
||||
vector<nall::DSP::Resampler::Cubic> resamplers;
|
||||
};
|
@@ -1,8 +1,15 @@
|
||||
#include <dsound.h>
|
||||
|
||||
struct AudioDirectSound : Audio {
|
||||
AudioDirectSound() { initialize(); }
|
||||
~AudioDirectSound() { terminate(); }
|
||||
AudioDirectSound() {
|
||||
Audio::setFrequency(48000.0);
|
||||
Audio::setLatency(40);
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioDirectSound() {
|
||||
terminate();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "DirectSound"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
@@ -19,6 +26,9 @@ struct AudioDirectSound : Audio {
|
||||
return {40, 60, 80, 100};
|
||||
}
|
||||
|
||||
auto defaultFrequency() -> double override { return 48000.0; }
|
||||
auto defaultLatency() -> uint override { return 40; }
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == Audio::blocking()) return true;
|
||||
if(!Audio::setBlocking(blocking)) return false;
|
||||
|
@@ -7,8 +7,17 @@
|
||||
#endif
|
||||
|
||||
struct AudioOpenAL : Audio {
|
||||
AudioOpenAL() { initialize(); }
|
||||
~AudioOpenAL() { terminate(); }
|
||||
AudioOpenAL() {
|
||||
Audio::setDevice(availableDevices().first());
|
||||
Audio::setChannels(2);
|
||||
Audio::setFrequency(48000.0);
|
||||
Audio::setLatency(20);
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioOpenAL() {
|
||||
terminate();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "OpenAL"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
@@ -21,7 +30,12 @@ struct AudioOpenAL : Audio {
|
||||
|
||||
auto availableDevices() -> vector<string> override {
|
||||
vector<string> devices;
|
||||
for(auto& device : queryDevices()) devices.append(device);
|
||||
if(const char* list = alcGetString(nullptr, ALC_DEVICE_SPECIFIER)) {
|
||||
while(list && *list) {
|
||||
result.append(list);
|
||||
list += strlen(list) + 1;
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
@@ -37,38 +51,35 @@ struct AudioOpenAL : Audio {
|
||||
return {20, 40, 60, 80, 100};
|
||||
}
|
||||
|
||||
auto context() -> uintptr override { return 0; }
|
||||
auto dynamic() -> bool override { return false; }
|
||||
|
||||
auto setDevice(string device) -> bool override {
|
||||
if(device == this->device()) return true;
|
||||
if(device == Audio::device()) return true;
|
||||
if(!Audio::setDevice(device)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == this->blocking()) return true;
|
||||
if(blocking == Audio::blocking()) return true;
|
||||
if(!Audio::setBlocking(blocking)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setChannels(uint channels) -> bool override {
|
||||
if(channels == this->channels()) return true;
|
||||
if(!Audio::setChannels(channels)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setFrequency(double frequency) -> bool override {
|
||||
if(frequency == this->frequency()) return true;
|
||||
if(frequency == Audio::frequency()) return true;
|
||||
if(!Audio::setFrequency(frequency)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setLatency(uint latency) -> bool override {
|
||||
if(latency == this->latency()) return true;
|
||||
if(latency == Audio::latency()) return true;
|
||||
if(!Audio::setLatency(latency)) return false;
|
||||
if(ready()) updateLatency();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto output(const double samples[]) -> void override {
|
||||
auto write(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;
|
||||
@@ -171,20 +182,6 @@ private:
|
||||
_buffer = nullptr;
|
||||
}
|
||||
|
||||
auto queryDevices() -> vector<string> {
|
||||
vector<string> result;
|
||||
|
||||
const char* list = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||
if(!list) return result;
|
||||
|
||||
while(list && *list) {
|
||||
result.append(list);
|
||||
list += strlen(list) + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto updateLatency() -> void {
|
||||
delete[] _buffer;
|
||||
_bufferSize = _frequency * _latency / 1000.0 + 0.5;
|
||||
|
@@ -13,77 +13,54 @@
|
||||
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
|
||||
#endif
|
||||
|
||||
struct AudioOSS : Audio {
|
||||
AudioOSS() { initialize(); }
|
||||
struct AudioOSS : AudioDriver {
|
||||
AudioOSS& self;
|
||||
|
||||
AudioOSS(Audio& super) : AudioDriver(super), self(*this) {}
|
||||
~AudioOSS() { terminate(); }
|
||||
|
||||
auto driver() -> string override { return "OSS"; }
|
||||
auto create() -> bool {
|
||||
super.setDevice("/dev/dsp");
|
||||
super.setChannels(2);
|
||||
super.setFrequency(48000);
|
||||
super.setLatency(3);
|
||||
buffer.resize(64);
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto driverName() -> string override { return "OSS"; }
|
||||
auto ready() -> bool override { return _fd >= 0; }
|
||||
|
||||
auto hasDevice() -> bool override { return true; }
|
||||
auto hasDynamic() -> bool override { return true; }
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasChannels() -> bool override { return true; }
|
||||
auto hasFrequency() -> bool override { return true; }
|
||||
auto hasLatency() -> bool override { return true; }
|
||||
auto hasDynamic() -> bool override { return true; }
|
||||
|
||||
auto availableDevices() -> vector<string> override {
|
||||
auto hasDevices() -> vector<string> override {
|
||||
vector<string> devices;
|
||||
devices.append("/dev/dsp");
|
||||
for(auto& device : directory::files("/dev/", "dsp?*")) devices.append(string{"/dev/", device});
|
||||
return devices;
|
||||
}
|
||||
|
||||
auto defaultChannels() -> uint override { return 2; }
|
||||
auto defaultFrequency() -> double override { return 48000.0; }
|
||||
auto defaultLatency() -> uint override { return 3; }
|
||||
|
||||
auto availableChannels() -> vector<uint> override {
|
||||
auto hasChannels() -> vector<uint> override {
|
||||
return {1, 2};
|
||||
}
|
||||
|
||||
auto availableFrequencies() -> vector<double> override {
|
||||
return {44100.0, 48000.0, 96000.0};
|
||||
auto hasFrequencies() -> vector<uint> override {
|
||||
return {44100, 48000, 96000};
|
||||
}
|
||||
|
||||
auto availableLatencies() -> vector<uint> override {
|
||||
auto hasLatencies() -> vector<uint> override {
|
||||
return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
}
|
||||
|
||||
auto setDevice(string device) -> bool override {
|
||||
if(device == Audio::device()) return true;
|
||||
if(!Audio::setDevice(device)) return false;
|
||||
return initialize();
|
||||
}
|
||||
auto setDevice(string device) -> bool override { return initialize(); }
|
||||
auto setBlocking(bool blocking) -> bool override { return updateBlocking(); }
|
||||
auto setChannels(uint channels) -> bool override { return initialize(); }
|
||||
auto setFrequency(uint frequency) -> bool override { return initialize(); }
|
||||
auto setLatency(uint latency) -> bool override { return initialize(); }
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
if(blocking == Audio::blocking()) return true;
|
||||
if(!Audio::setBlocking(blocking)) return false;
|
||||
return updateBlocking();
|
||||
}
|
||||
|
||||
auto setDynamic(bool dynamic) -> bool override {
|
||||
if(dynamic == Audio::dynamic()) return true;
|
||||
if(!Audio::setDynamic(dynamic)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setChannels(uint channels) -> bool override {
|
||||
if(channels == Audio::channels()) return true;
|
||||
if(!Audio::setChannels(channels)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setFrequency(double frequency) -> bool override {
|
||||
if(frequency == Audio::frequency()) return true;
|
||||
if(!Audio::setFrequency(frequency)) return false;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setLatency(uint latency) -> bool override {
|
||||
if(latency == Audio::latency()) return true;
|
||||
if(!Audio::setLatency(latency)) return false;
|
||||
return initialize();
|
||||
auto clear() -> void override {
|
||||
buffer.resize(64);
|
||||
}
|
||||
|
||||
auto level() -> double override {
|
||||
@@ -95,18 +72,9 @@ struct AudioOSS : Audio {
|
||||
auto output(const double samples[]) -> void override {
|
||||
if(!ready()) return;
|
||||
|
||||
if(!_dynamic) {
|
||||
for(uint n : range(channels())) {
|
||||
sample(sclamp<16>(samples[n] * 32767.0));
|
||||
}
|
||||
} else {
|
||||
Audio::outputDynamic(samples);
|
||||
while(pending()) {
|
||||
for(auto& resampler : _resamplers) {
|
||||
auto sample = (uint16_t)sclamp<16>(resampler.read() * 32767.0);
|
||||
auto unused = write(_fd, &sample, 2);
|
||||
}
|
||||
}
|
||||
for(uint n : range(self.channels)) {
|
||||
buffer.write(sclamp<16>(samples[n] * 32767.0));
|
||||
if(buffer.full()) write(_fd, buffer.data(), buffer.size<uint8_t>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,23 +82,20 @@ private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
|
||||
if(!availableDevices().find(_device)) {
|
||||
Audio::setDevice(availableDevices().left());
|
||||
}
|
||||
Audio::setChannels(channels());
|
||||
if(!hasDevices().find(self.device)) self.device = hasDevices().first();
|
||||
|
||||
_fd = open(_device, O_WRONLY, O_NONBLOCK);
|
||||
_fd = open(self.device, O_WRONLY, O_NONBLOCK);
|
||||
if(_fd < 0) return false;
|
||||
|
||||
int cooked = 1;
|
||||
ioctl(_fd, SNDCTL_DSP_COOKEDMODE, &cooked);
|
||||
//policy: 0 = minimum latency (higher CPU usage); 10 = maximum latency (lower CPU usage)
|
||||
int policy = min(10, _latency);
|
||||
int policy = min(10, self.latency);
|
||||
ioctl(_fd, SNDCTL_DSP_POLICY, &policy);
|
||||
int channels = _channels;
|
||||
int channels = self.channels;
|
||||
ioctl(_fd, SNDCTL_DSP_CHANNELS, &channels);
|
||||
ioctl(_fd, SNDCTL_DSP_SETFMT, &_format);
|
||||
int frequency = _frequency;
|
||||
int frequency = self.frequency;
|
||||
ioctl(_fd, SNDCTL_DSP_SPEED, &frequency);
|
||||
updateBlocking();
|
||||
audio_buf_info info;
|
||||
@@ -150,23 +115,14 @@ private:
|
||||
if(!ready()) return false;
|
||||
auto flags = fcntl(_fd, F_GETFL);
|
||||
if(flags < 0) return false;
|
||||
_blocking ? flags &=~ O_NONBLOCK : flags |= O_NONBLOCK;
|
||||
self.blocking ? flags &=~ O_NONBLOCK : flags |= O_NONBLOCK;
|
||||
fcntl(_fd, F_SETFL, flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto sample(uint16_t sample) -> void {
|
||||
_outputBuffer[_outputOffset++] = sample;
|
||||
if(_outputOffset >= sizeof(_outputBuffer) / sizeof(uint16_t)) {
|
||||
write(_fd, &_outputBuffer, sizeof(_outputBuffer));
|
||||
_outputOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int _fd = -1;
|
||||
int _format = AFMT_S16_LE;
|
||||
int _bufferSize = 1;
|
||||
|
||||
uint _outputOffset = 0;
|
||||
uint16_t _outputBuffer[64];
|
||||
queue<int16_t> buffer;
|
||||
};
|
||||
|
Reference in New Issue
Block a user