Update to 20180728 release.

byuu says:

Sigh, I seem to be spiraling a bit here ... but the work is very
important. Hopefully I can get a solid WIP together soon. But for now...

I've integrated dynamic rate control into ruby::Audio via
setDynamic(bool) for now. It's very demanding, as you would expect. When
it's not in use, I realized the OSS driver's performance was pretty bad
due to calling write() for every sample for every channel. I implemented
a tiny 256-sample buffer and bsnes went from 290fps to 330fps on my
FreeBSD desktop. It may be possible to do the same buffering with DRC,
but for now, I'm not doing so, and adjusting the audio input frequency
on every sample.

I also added ruby::Video::setFlush(bool), which is available only in the
OpenGL drivers, and this causes glFinish() to be called after swapping
display buffers. I really couldn't think of a good name for this, "hard
GPU sync" sounds kind of silly. In my view, flush is what commits queued
events. Eg fflush(). OpenGL of course treats glFlush differently (I
really don't even know what the point of it is even after reading the
manual ...), and then has glFinish ... meh, whatever. It's
setFlush(bool) until I come up with something better. Also as expected,
this one's a big hit to performance.

To implement the DRC, I started putting helper functions into the ruby
video/audio/input core classes. And then the XVideo driver started
crashing. It took hours and hours and hours to track down the problem:
you have to clear XSetWindowAttributes to zero before calling
XCreateWindow. No amount of `--sync`, `gdb break gdk_x_error`, `-Og`,
etc will make Xlib be even remotely helpful in debugging errors like
this.

The GLX, GLX2, and XVideo drivers basically worked by chance before. If
the stack frame had the right memory cleared, it worked. Otherwise it'd
crash with BadValue, and my changing things broke that condition on the
XVideo driver. So this has been fixed in all three now.

Once XVideo was running again, I realized that non-power of two video
sizes were completely broken for the YUV formats. It took a while, but I
managed to fix all of that as well.

At this point, most of ruby is going to be broken outside of FreeBSD, as
I still need to finish updating all the drivers.
This commit is contained in:
Tim Allen 2018-07-28 21:21:39 +10:00
parent 876b4be1d2
commit 716c95f279
29 changed files with 1291 additions and 1044 deletions

View File

@ -65,7 +65,7 @@ auto Stream::pending() const -> bool {
}
auto Stream::read(double samples[]) -> uint {
for(auto c : range(channels.size())) samples[c] = channels[c].resampler.read();
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
return channels.size();
}

View File

@ -393,7 +393,7 @@ auto Presentation::updateDeviceSelections() -> void {
//generate a list of size multipliers
auto Presentation::updateSizeMenu() -> void {
assert(sizeMenu.actions() == 0); //should only be called once
assert(sizeMenu.actionCount() == 0); //should only be called once
//determine the largest multiplier that can be used by the largest monitor found
uint height = 1;

View File

@ -19,10 +19,15 @@ auto Program::updateAudioDriver() -> void {
auto Program::updateAudioExclusive() -> void {
if(!audio) return;
audio->clear();
audio->setExclusive(settings["Audio/Exclusive"].boolean());
updateAudioFrequency();
updateAudioLatency();
if(audio->hasExclusive()) {
audio->setExclusive(settings["Audio/Exclusive"].boolean());
settingsWindow->audio.exclusiveMode.setEnabled(true).setChecked(settings["Audio/Exclusive"].boolean());
audio->clear();
updateAudioFrequency();
updateAudioLatency();
} else {
settingsWindow->audio.exclusiveMode.setEnabled(false).setChecked(false);
}
}
auto Program::updateAudioDevice() -> void {

View File

@ -58,6 +58,7 @@ struct Program : Emulator::Platform {
auto renameState(string from, string to) -> bool;
//video.cpp
auto updateVideoExclusive() -> void;
auto updateVideoDriver() -> void;
auto updateVideoBlocking() -> void;
auto updateVideoShader() -> void;

View File

@ -1,7 +1,7 @@
auto Program::updateVideoDriver() -> void {
video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle());
video->setExclusive(false);
updateVideoExclusive();
updateVideoBlocking();
updateVideoShader();
@ -27,6 +27,15 @@ auto Program::updateVideoDriver() -> void {
presentation->updateShaders();
}
auto Program::updateVideoExclusive() -> void {
video->setExclusive(false);
if(video->hasExclusive()) {
settingsWindow->video.exclusiveMode.setEnabled(true).setChecked(settings["Video/Exclusive"].boolean());
} else {
settingsWindow->video.exclusiveMode.setEnabled(false).setChecked(false);
}
}
auto Program::updateVideoBlocking() -> void {
video->setBlocking(settings["Video/Blocking"].boolean());
}

View File

@ -21,12 +21,8 @@ auto pAction::setVisible(bool visible) -> void {
}
//GTK+ uses _ for mnemonics, __ for _
//transform so that & is used for mnemonics, && for &
auto pAction::_mnemonic(string text) -> string {
text.transform("&_", "\x01\x02");
text.replace("\x01\x01", "&");
text.transform("\x01", "_");
text.replace("\x02", "__");
text.replace("_", "__");
return text;
}

View File

@ -487,7 +487,7 @@ auto pWindow::_setScreenSaver(bool screenSaver) -> void {
if(pApplication::xdgScreenSaver) {
if(this->screenSaver != screenSaver) {
this->screenSaver = screenSaver;
execute("xdg-screensaver", screenSaver ? "resume" : "suspend", string{"0x", hex(handle())});
invoke("xdg-screensaver", screenSaver ? "resume" : "suspend", string{"0x", hex(handle())});
}
}
#endif

View File

@ -7,8 +7,9 @@ namespace nall { namespace DSP { namespace Resampler {
struct Cubic {
inline auto reset(real inputFrequency, real outputFrequency, uint queueSize = 0) -> void;
inline auto pending() const -> bool { return samples.pending(); }
inline auto read() -> real { return samples.read(); }
inline auto setInputFrequency(real inputFrequency) -> void;
inline auto pending() const -> bool;
inline auto read() -> real;
inline auto write(real sample) -> void;
private:
@ -32,6 +33,19 @@ auto Cubic::reset(real inputFrequency, real outputFrequency, uint queueSize) ->
samples.resize(queueSize);
}
auto Cubic::setInputFrequency(real inputFrequency) -> void {
this->inputFrequency = inputFrequency;
ratio = inputFrequency / outputFrequency;
}
auto Cubic::pending() const -> bool {
return samples.pending();
}
auto Cubic::read() -> real {
return samples.read();
}
auto Cubic::write(real sample) -> void {
auto& mu = fraction;
auto& s = history;

View File

@ -1,5 +1,10 @@
#pragma once
#include <nall/maybe.hpp>
#include <nall/range.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall { namespace HID {
struct Input {

View File

@ -12,7 +12,7 @@ ifeq ($(ruby),)
ruby += audio.oss audio.alsa audio.openal audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib input.udev
else ifeq ($(platform),bsd)
ruby += video.glx video.xvideo video.xshm
ruby += video.glx video.glx2 video.xvideo video.xshm
ruby += audio.oss audio.openal
ruby += input.sdl input.xlib
endif
@ -68,7 +68,7 @@ endif
ruby.objects := $(object.path)/ruby.o
$(object.path)/ruby.o: $(ruby.path)/ruby.cpp
$(object.path)/ruby.o: $(ruby.path)/ruby.cpp $(call rwildcard,$(ruby.path))
$(info Compiling $< ...)
@$(compiler) $(ruby.flags) $(flags) $(flags.deps) -c $< -o $@

254
ruby/audio.cpp Normal file
View File

@ -0,0 +1,254 @@
#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 {
_exclusive = exclusive;
return true;
}
auto Audio::setContext(uintptr context) -> bool {
_context = context;
return true;
}
auto Audio::setDevice(string device) -> bool {
_device = device;
return true;
}
auto Audio::setBlocking(bool blocking) -> bool {
_blocking = blocking;
Audio::setFrequency(frequency());
return true;
}
auto Audio::setDynamic(bool dynamic) -> bool {
_dynamic = dynamic;
return true;
}
auto Audio::setChannels(uint channels) -> bool {
_channels = channels;
_resamplers.reset();
_resamplers.resize(channels);
Audio::setFrequency(frequency());
return true;
}
auto Audio::setFrequency(double frequency) -> bool {
_frequency = frequency;
for(auto& resampler : _resamplers) {
resampler.reset(frequency, frequency);
}
return true;
}
auto Audio::setLatency(uint latency) -> bool {
_latency = latency;
return true;
}
//protected functions
auto Audio::pending() -> bool {
return _resamplers && _resamplers[0].pending();
}
auto Audio::outputDynamic(const double samples[]) -> void {
auto maxDelta = 0.005;
double fillLevel = level();
double dynamicFrequency = ((1.0 - maxDelta) + 2.0 * fillLevel * maxDelta) * frequency();
for(auto& resampler : _resamplers) {
resampler.setInputFrequency(dynamicFrequency);
resampler.write(*samples++);
}
}
//static functions
auto Audio::create(string driver) -> Audio* {
Audio* audio = nullptr;
if(!driver) driver = optimalDriver();
#if defined(AUDIO_ALSA)
if(driver == "ALSA") audio = new AudioALSA;
#endif
#if defined(AUDIO_AO)
if(driver == "libao") audio = new AudioAO;
#endif
#if defined(AUDIO_ASIO)
if(driver == "ASIO") audio = new AudioASIO;
#endif
#if defined(AUDIO_DIRECTSOUND)
if(driver == "DirectSound") audio = new AudioDirectSound;
#endif
#if defined(AUDIO_OPENAL)
if(driver == "OpenAL") audio = new AudioOpenAL;
#endif
#if defined(AUDIO_OSS)
if(driver == "OSS") audio = new AudioOSS;
#endif
#if defined(AUDIO_PULSEAUDIO)
if(driver == "PulseAudio") audio = new AudioPulseAudio;
#endif
#if defined(AUDIO_PULSEAUDIOSIMPLE)
if(driver == "PulseAudioSimple") audio = new AudioPulseAudioSimple;
#endif
#if defined(AUDIO_WASAPI)
if(driver == "WASAPI") audio = new AudioWASAPI;
#endif
#if defined(AUDIO_XAUDIO2)
if(driver == "XAudio2") audio = new AudioXAudio2;
#endif
if(!audio) audio = new Audio;
return audio;
}
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
}
auto Audio::availableDrivers() -> 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"};
}
}

62
ruby/audio.hpp Normal file
View File

@ -0,0 +1,62 @@
struct Audio {
static auto create(string driver = "") -> Audio*;
static auto optimalDriver() -> string;
static auto safestDriver() -> string;
static auto availableDrivers() -> vector<string>;
virtual ~Audio() = default;
virtual auto driver() -> string { return "None"; }
virtual auto ready() -> bool { return true; }
virtual auto availableDevices() -> vector<string> { return {"Default"}; }
virtual auto availableChannels() -> vector<uint> { return {2}; }
virtual auto availableFrequencies() -> vector<double> { return {48000.0}; }
virtual auto availableLatencies() -> vector<uint> { return {0}; }
virtual auto hasExclusive() -> bool { return false; }
virtual auto hasContext() -> bool { return false; }
virtual auto hasDevice() -> bool { return false; }
virtual auto hasBlocking() -> bool { return false; }
virtual auto hasDynamic() -> bool { return false; }
virtual auto hasChannels() -> bool { return false; }
virtual auto hasFrequency() -> bool { return false; }
virtual auto hasLatency() -> bool { return false; }
virtual auto exclusive() -> bool { return _exclusive; }
virtual auto context() -> uintptr { return _context; }
virtual auto device() -> string { return _device; }
virtual auto blocking() -> bool { return _blocking; }
virtual auto dynamic() -> bool { return _dynamic; }
virtual auto channels() -> uint { return _channels; }
virtual auto frequency() -> double { return _frequency; }
virtual auto latency() -> uint { return _latency; }
virtual auto setExclusive(bool exclusive) -> bool;
virtual auto setContext(uintptr context) -> bool;
virtual auto setDevice(string device) -> bool;
virtual auto setBlocking(bool blocking) -> bool;
virtual auto setDynamic(bool dynamic) -> bool;
virtual auto setChannels(uint channels) -> bool;
virtual auto setFrequency(double frequency) -> bool;
virtual auto setLatency(uint latency) -> bool;
virtual auto clear() -> void {}
virtual auto level() -> double { return 0.5; }
virtual auto output(const double samples[]) -> void {}
protected:
virtual auto pending() -> bool;
virtual auto outputDynamic(const double samples[]) -> void;
bool _exclusive = false;
uintptr _context = 0;
string _device = "Default";
bool _blocking = false;
bool _dynamic = false;
uint _channels = 2;
double _frequency = 48000.0;
uint _latency = 0;
vector<nall::DSP::Resampler::Cubic> _resamplers;
};

View File

@ -4,54 +4,67 @@ struct AudioALSA : Audio {
AudioALSA() { initialize(); }
~AudioALSA() { terminate(); }
auto availableDevices() -> string_vector {
auto driver() -> string override {
return "ALSA";
}
auto ready() -> bool override {
return _ready;
}
auto availableDevices() -> vector<string> override {
return queryDevices();
}
auto availableFrequencies() -> vector<double> {
return {44100.0, 48000.0, 96000.0};
}
auto availableLatencies() -> vector<uint> {
return {20, 40, 60, 80, 100};
}
auto availableChannels() -> vector<uint> {
auto availableChannels() -> vector<uint> override {
return {2};
}
auto ready() -> bool { return _ready; }
auto device() -> string { return _device; }
auto blocking() -> bool { return _blocking; }
auto channels() -> uint { return 2; }
auto frequency() -> double { return _frequency; }
auto latency() -> uint { return _latency; }
auto availableFrequencies() -> vector<double> override {
return {44100.0, 48000.0, 96000.0};
}
auto setDevice(string device) -> bool {
if(_device == device) return true;
_device = device;
auto availableLatencies() -> vector<uint> override {
return {20, 40, 60, 80, 100};
}
auto hasDevice() -> 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 setDevice(string device) -> bool override {
if(device == this->device()) return true;
if(!Audio::setDevice(device)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
auto setBlocking(bool blocking) -> bool override {
if(blocking == this->blocking()) return true;
if(!Audio::setBlocking(blocking)) return false;
return true;
}
auto setFrequency(double frequency) -> bool {
if(_frequency == frequency) return true;
_frequency = frequency;
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(!Audio::setFrequency(frequency)) return false;
return initialize();
}
auto setLatency(uint latency) -> bool {
if(_latency == latency) return true;
_latency = latency;
auto setLatency(uint latency) -> bool override {
if(latency == this->latency()) return true;
if(!Audio::setLatency(latency)) return false;
return initialize();
}
auto output(const double samples[]) -> void {
auto output(const double samples[]) -> void override {
if(!ready()) return;
_buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
@ -169,10 +182,6 @@ private:
}
bool _ready = false;
string _device;
bool _blocking = true;
double _frequency = 48000.0;
uint _latency = 40;
snd_pcm_t* _interface = nullptr;
snd_pcm_uframes_t _bufferSize;

View File

@ -10,57 +10,70 @@ struct AudioOpenAL : Audio {
AudioOpenAL() { initialize(); }
~AudioOpenAL() { terminate(); }
auto availableDevices() -> vector<string> {
auto driver() -> string override {
return "OpenAL";
}
auto ready() -> bool override {
return _ready;
}
auto availableDevices() -> vector<string> override {
vector<string> devices;
for(auto& device : queryDevices()) devices.append(device);
return devices;
}
auto availableFrequencies() -> vector<double> {
return {44100.0, 48000.0, 96000.0};
}
auto availableLatencies() -> vector<uint> {
return {20, 40, 60, 80, 100};
}
auto availableChannels() -> vector<uint> {
auto availableChannels() -> vector<uint> override {
return {2};
}
auto ready() -> bool { return _ready; }
auto device() -> string { return _device; }
auto blocking() -> bool { return _blocking; }
auto channels() -> uint { return _channels; }
auto frequency() -> double { return (double)_frequency; }
auto latency() -> uint { return _latency; }
auto availableFrequencies() -> vector<double> override {
return {44100.0, 48000.0, 96000.0};
}
auto setDevice(string device) -> bool {
if(_device == device) return true;
_device = device;
auto availableLatencies() -> vector<uint> override {
return {20, 40, 60, 80, 100};
}
auto hasDevice() -> 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 setDevice(string device) -> bool override {
if(device == this->device()) return true;
if(!Audio::setDevice(device)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
auto setBlocking(bool blocking) -> bool override {
if(blocking == this->blocking()) return true;
if(!Audio::setBlocking(blocking)) return false;
return true;
}
auto setFrequency(double frequency) -> bool {
if(_frequency == (uint)frequency) return true;
_frequency = (uint)frequency;
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(!Audio::setFrequency(frequency)) return false;
return initialize();
}
auto setLatency(uint latency) -> bool {
if(_latency == latency) return true;
_latency = latency;
if(_ready) updateLatency();
auto setLatency(uint latency) -> bool override {
if(latency == this->latency()) return true;
if(!Audio::setLatency(latency)) return false;
if(ready()) updateLatency();
return true;
}
auto output(const double samples[]) -> void {
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;
@ -184,11 +197,6 @@ private:
}
bool _ready = false;
string _device;
bool _blocking = true;
uint _channels = 2;
uint _frequency = 48000;
uint _latency = 20;
ALCdevice* _openAL = nullptr;
ALCcontext* _context = nullptr;

View File

@ -17,68 +17,97 @@ struct AudioOSS : Audio {
AudioOSS() { initialize(); }
~AudioOSS() { terminate(); }
auto availableDevices() -> vector<string> {
auto driver() -> string override {
return "OSS";
}
auto ready() -> bool override {
return _fd >= 0;
}
auto availableDevices() -> 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 availableFrequencies() -> vector<double> {
return {44100.0, 48000.0, 96000.0};
}
auto availableLatencies() -> vector<uint> {
return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}
auto availableChannels() -> vector<uint> {
auto availableChannels() -> vector<uint> override {
return {1, 2};
}
auto ready() -> bool { return _ready; }
auto device() -> string { return _device; }
auto blocking() -> bool { return _blocking; }
auto channels() -> uint { return _channels; }
auto frequency() -> double { return _frequency; }
auto latency() -> uint { return _latency; }
auto availableFrequencies() -> vector<double> override {
return {44100.0, 48000.0, 96000.0};
}
auto setDevice(string device) -> bool {
if(_device == device) return true;
_device = device;
auto availableLatencies() -> vector<uint> override {
return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}
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 setDevice(string device) -> bool override {
if(device == this->device()) return true;
if(!Audio::setDevice(device)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
updateBlocking();
auto setBlocking(bool blocking) -> bool override {
if(blocking == this->blocking()) return true;
if(!Audio::setBlocking(blocking)) return false;
return updateBlocking();
}
auto setDynamic(bool dynamic) -> bool override {
if(dynamic == this->dynamic()) return true;
if(!Audio::setDynamic(dynamic)) return false;
return true;
}
auto setChannels(uint channels) -> bool {
if(_channels == channels) return true;
_channels = channels;
auto setChannels(uint channels) -> bool override {
if(channels == this->channels()) return true;
if(!Audio::setChannels(channels)) return false;
return initialize();
}
auto setFrequency(double frequency) -> bool {
if(_frequency == frequency) return true;
_frequency = frequency;
auto setFrequency(double frequency) -> bool override {
if(frequency == this->frequency()) return true;
if(!Audio::setFrequency(frequency)) return false;
return initialize();
}
auto setLatency(uint latency) -> bool {
if(_latency == latency) return true;
_latency = latency;
auto setLatency(uint latency) -> bool override {
if(latency == this->latency()) return true;
if(!Audio::setLatency(latency)) return false;
return initialize();
}
auto output(const double samples[]) -> void {
auto level() -> double override {
audio_buf_info info;
ioctl(_fd, SNDCTL_DSP_GETOSPACE, &info);
return (double)(_bufferSize - info.bytes) / _bufferSize;
}
auto output(const double samples[]) -> void override {
if(!ready()) return;
for(auto n : range(_channels)) {
auto sample = (uint16_t)sclamp<16>(samples[n] * 32767.0);
auto unused = write(_fd, &sample, 2);
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);
}
}
}
}
@ -87,8 +116,9 @@ private:
terminate();
if(!availableDevices().find(_device)) {
_device = availableDevices().left();
Audio::setDevice(availableDevices().left());
}
Audio::setChannels(channels());
_fd = open(_device, O_WRONLY, O_NONBLOCK);
if(_fd < 0) return false;
@ -103,33 +133,41 @@ private:
ioctl(_fd, SNDCTL_DSP_SETFMT, &_format);
int frequency = _frequency;
ioctl(_fd, SNDCTL_DSP_SPEED, &frequency);
updateBlocking();
return _ready = true;
audio_buf_info info;
ioctl(_fd, SNDCTL_DSP_GETOSPACE, &info);
_bufferSize = info.bytes;
return true;
}
auto terminate() -> void {
_ready = false;
if(_fd < 0) return;
if(!ready()) return;
close(_fd);
_fd = -1;
}
auto updateBlocking() -> void {
if(!_ready) return;
auto updateBlocking() -> bool {
if(!ready()) return false;
auto flags = fcntl(_fd, F_GETFL);
if(flags < 0) return;
if(flags < 0) return false;
_blocking ? flags &=~ O_NONBLOCK : flags |= O_NONBLOCK;
fcntl(_fd, F_SETFL, flags);
return true;
}
bool _ready = false;
string _device;
bool _blocking = true;
uint _channels = 2;
double _frequency = 48000.0;
uint _latency = 2;
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[256];
};

142
ruby/input.cpp Normal file
View File

@ -0,0 +1,142 @@
#if defined(INPUT_CARBON)
#include <ruby/input/carbon.cpp>
#endif
#if defined(INPUT_QUARTZ)
#include <ruby/input/quartz.cpp>
#endif
#if defined(INPUT_SDL)
#include <ruby/input/sdl.cpp>
#endif
#if defined(INPUT_UDEV)
#include <ruby/input/udev.cpp>
#endif
#if defined(INPUT_WINDOWS)
#include <ruby/input/windows.cpp>
#endif
#if defined(INPUT_XLIB)
#include <ruby/input/xlib.cpp>
#endif
namespace ruby {
auto Input::setContext(uintptr context) -> bool {
_context = context;
return true;
}
auto Input::onChange(const function<void (shared_pointer<HID::Device>, uint, uint, int16_t, int16_t)>& onChange) -> void {
_onChange = onChange;
}
auto Input::doChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
if(_onChange) _onChange(device, group, input, oldValue, newValue);
}
//protected functions
//static functions
auto Input::create(string driver) -> Input* {
Input* input = nullptr;
if(!driver) driver = optimalDriver();
#if defined(INPUT_WINDOWS)
if(driver == "Windows") input = new InputWindows;
#endif
#if defined(INPUT_QUARTZ)
if(driver == "Quartz") input = new InputQuartz;
#endif
#if defined(INPUT_CARBON)
if(driver == "Carbon") input = new InputCarbon;
#endif
#if defined(INPUT_UDEV)
if(driver == "udev") input = new InputUdev;
#endif
#if defined(INPUT_SDL)
if(driver == "SDL") input = new InputSDL;
#endif
#if defined(INPUT_XLIB)
if(driver == "Xlib") input = new InputXlib;
#endif
if(!input) input = new Input;
return input;
}
auto Input::optimalDriver() -> string {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_QUARTZ)
return "Quartz";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "None";
#endif
}
auto Input::safestDriver() -> string {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_QUARTZ)
return "Quartz";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "none";
#endif
}
auto Input::availableDrivers() -> vector<string> {
return {
#if defined(INPUT_WINDOWS)
"Windows",
#endif
#if defined(INPUT_QUARTZ)
"Quartz",
#endif
#if defined(INPUT_CARBON)
"Carbon",
#endif
#if defined(INPUT_UDEV)
"udev",
#endif
#if defined(INPUT_SDL)
"SDL",
#endif
#if defined(INPUT_XLIB)
"Xlib",
#endif
"None"};
}
}

31
ruby/input.hpp Normal file
View File

@ -0,0 +1,31 @@
struct Input {
static auto create(string driver = "") -> Input*;
static auto optimalDriver() -> string;
static auto safestDriver() -> string;
static auto availableDrivers() -> vector<string>;
virtual ~Input() = default;
virtual auto driver() -> string { return "None"; }
virtual auto ready() -> bool { return true; }
virtual auto hasContext() -> bool { return false; }
virtual auto context() -> uintptr { return _context; }
virtual auto setContext(uintptr context) -> bool;
virtual auto acquired() -> bool { return false; }
virtual auto acquire() -> bool { return false; }
virtual auto release() -> bool { return false; }
virtual auto poll() -> vector<shared_pointer<nall::HID::Device>> { return {}; }
virtual auto rumble(uint64_t id, bool enable) -> bool { return false; }
auto onChange(const function<void (shared_pointer<nall::HID::Device>, uint, uint, int16_t, int16_t)>&) -> void;
auto doChange(shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void;
protected:
uintptr _context = 0;
function<void (shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue)> _onChange;
};

View File

@ -10,29 +10,30 @@ struct InputSDL : Input {
InputSDL() : _keyboard(*this), _mouse(*this), _joypad(*this) { initialize(); }
~InputSDL() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "SDL"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto hasContext() -> bool override { return true; }
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto setContext(uintptr context) -> bool override {
if(context == this->context()) return true;
if(!Input::setContext(context)) return false;
return initialize();
}
auto acquired() -> bool {
auto acquired() -> bool override {
return _mouse.acquired();
}
auto acquire() -> bool {
auto acquire() -> bool override {
return _mouse.acquire();
}
auto release() -> bool {
auto release() -> bool override {
return _mouse.release();
}
auto poll() -> vector<shared_pointer<HID::Device>> {
auto poll() -> vector<shared_pointer<HID::Device>> override {
vector<shared_pointer<HID::Device>> devices;
_keyboard.poll(devices);
_mouse.poll(devices);
@ -40,7 +41,7 @@ struct InputSDL : Input {
return devices;
}
auto rumble(uint64_t id, bool enable) -> bool {
auto rumble(uint64_t id, bool enable) -> bool override {
return false;
}
@ -62,7 +63,6 @@ private:
}
bool _ready = false;
uintptr _context = 0;
InputKeyboardXlib _keyboard;
InputMouseXlib _mouse;

View File

@ -11,36 +11,37 @@ struct InputXlib : Input {
InputXlib() : _keyboard(*this), _mouse(*this) { initialize(); }
~InputXlib() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "Xlib"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto hasContext() -> bool override { return true; }
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto setContext(uintptr context) -> bool override {
if(context == this->context()) return true;
if(!Input::setContext(context)) return false;
return initialize();
}
auto acquired() -> bool {
auto acquired() -> bool override {
return _mouse.acquired();
}
auto acquire() -> bool {
auto acquire() -> bool override {
return _mouse.acquire();
}
auto release() -> bool {
auto release() -> bool override {
return _mouse.release();
}
auto poll() -> vector<shared_pointer<HID::Device>> {
auto poll() -> vector<shared_pointer<HID::Device>> override {
vector<shared_pointer<HID::Device>> devices;
_keyboard.poll(devices);
_mouse.poll(devices);
return devices;
}
auto rumble(uint64_t id, bool enable) -> bool {
auto rumble(uint64_t id, bool enable) -> bool override {
return false;
}
@ -60,7 +61,6 @@ private:
}
bool _ready = false;
uintptr _context = 0;
InputKeyboardXlib _keyboard;
InputMouseXlib _mouse;

View File

@ -7,8 +7,6 @@
using namespace nall;
using namespace ruby;
/* Shared */
#undef deprecated
#undef mkdir
#undef usleep
@ -26,501 +24,6 @@ using namespace ruby;
#include <windows.h>
#endif
/* Video */
#if defined(VIDEO_CGL)
#include <ruby/video/cgl.cpp>
#endif
#if defined(VIDEO_DIRECT3D)
#include <ruby/video/direct3d.cpp>
#endif
#if defined(VIDEO_DIRECTDRAW)
#include <ruby/video/directdraw.cpp>
#endif
#if defined(VIDEO_GDI)
#include <ruby/video/gdi.cpp>
#endif
#if defined(VIDEO_GLX)
#include <ruby/video/glx.cpp>
#endif
#if defined(VIDEO_GLX2)
#include <ruby/video/glx2.cpp>
#endif
#if defined(VIDEO_WGL)
#include <ruby/video/wgl.cpp>
#endif
#if defined(VIDEO_XSHM)
#include <ruby/video/xshm.cpp>
#endif
#if defined(VIDEO_XVIDEO)
#include <ruby/video/xvideo.cpp>
#endif
namespace ruby {
auto Video::create(string driver) -> Video* {
Video* video = nullptr;
if(!driver) driver = optimalDriver();
#if defined(VIDEO_CGL)
if(driver == "OpenGL") video = new VideoCGL;
#endif
#if defined(VIDEO_DIRECT3D)
if(driver == "Direct3D") video = new VideoDirect3D;
#endif
#if defined(VIDEO_DIRECTDRAW)
if(driver == "DirectDraw") video = new VideoDirectDraw;
#endif
#if defined(VIDEO_GDI)
if(driver == "GDI") video = new VideoGDI;
#endif
#if defined(VIDEO_GLX)
if(driver == "OpenGL") video = new VideoGLX;
#endif
#if defined(VIDEO_GLX2)
if(driver == "OpenGL2") video = new VideoGLX2;
#endif
#if defined(VIDEO_WGL)
if(driver == "OpenGL") video = new VideoWGL;
#endif
#if defined(VIDEO_XSHM)
if(driver == "XShm") video = new VideoXShm;
#endif
#if defined(VIDEO_XVIDEO)
if(driver == "XVideo") video = new VideoXVideo;
#endif
if(!video) driver = "None", video = new Video;
video->_driver = driver;
return video;
}
auto Video::optimalDriver() -> string {
#if defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_GLX)
return "OpenGL";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_XSHM)
return "XShm";
#else
return "None";
#endif
}
auto Video::safestDriver() -> string {
#if defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_XSHM)
return "XShm";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_GLX)
return "OpenGL";
#else
return "None";
#endif
}
auto Video::availableDrivers() -> vector<string> {
return {
#if defined(VIDEO_WGL)
"OpenGL",
#endif
#if defined(VIDEO_DIRECT3D)
"Direct3D",
#endif
#if defined(VIDEO_DIRECTDRAW)
"DirectDraw",
#endif
#if defined(VIDEO_GDI)
"GDI",
#endif
#if defined(VIDEO_CGL)
"OpenGL",
#endif
#if defined(VIDEO_GLX)
"OpenGL",
#endif
#if defined(VIDEO_GLX2)
"OpenGL2",
#endif
#if defined(VIDEO_XVIDEO)
"XVideo",
#endif
#if defined(VIDEO_XSHM)
"XShm",
#endif
"None"};
}
}
/* Audio */
#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::create(string driver) -> Audio* {
Audio* audio = nullptr;
if(!driver) driver = optimalDriver();
#if defined(AUDIO_ALSA)
if(driver == "ALSA") audio = new AudioALSA;
#endif
#if defined(AUDIO_AO)
if(driver == "libao") audio = new AudioAO;
#endif
#if defined(AUDIO_ASIO)
if(driver == "ASIO") audio = new AudioASIO;
#endif
#if defined(AUDIO_DIRECTSOUND)
if(driver == "DirectSound") audio = new AudioDirectSound;
#endif
#if defined(AUDIO_OPENAL)
if(driver == "OpenAL") audio = new AudioOpenAL;
#endif
#if defined(AUDIO_OSS)
if(driver == "OSS") audio = new AudioOSS;
#endif
#if defined(AUDIO_PULSEAUDIO)
if(driver == "PulseAudio") audio = new AudioPulseAudio;
#endif
#if defined(AUDIO_PULSEAUDIOSIMPLE)
if(driver == "PulseAudioSimple") audio = new AudioPulseAudioSimple;
#endif
#if defined(AUDIO_WASAPI)
if(driver == "WASAPI") audio = new AudioWASAPI;
#endif
#if defined(AUDIO_XAUDIO2)
if(driver == "XAudio2") audio = new AudioXAudio2;
#endif
if(!audio) driver = "None", audio = new Audio;
audio->_driver = driver;
return audio;
}
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
}
auto Audio::availableDrivers() -> 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"};
}
}
/* Input */
#if defined(INPUT_CARBON)
#include <ruby/input/carbon.cpp>
#endif
#if defined(INPUT_QUARTZ)
#include <ruby/input/quartz.cpp>
#endif
#if defined(INPUT_SDL)
#include <ruby/input/sdl.cpp>
#endif
#if defined(INPUT_UDEV)
#include <ruby/input/udev.cpp>
#endif
#if defined(INPUT_WINDOWS)
#include <ruby/input/windows.cpp>
#endif
#if defined(INPUT_XLIB)
#include <ruby/input/xlib.cpp>
#endif
namespace ruby {
auto Input::create(string driver) -> Input* {
Input* input = nullptr;
if(!driver) driver = optimalDriver();
#if defined(INPUT_WINDOWS)
if(driver == "Windows") input = new InputWindows;
#endif
#if defined(INPUT_QUARTZ)
if(driver == "Quartz") input = new InputQuartz;
#endif
#if defined(INPUT_CARBON)
if(driver == "Carbon") input = new InputCarbon;
#endif
#if defined(INPUT_UDEV)
if(driver == "udev") input = new InputUdev;
#endif
#if defined(INPUT_SDL)
if(driver == "SDL") input = new InputSDL;
#endif
#if defined(INPUT_XLIB)
if(driver == "Xlib") input = new InputXlib;
#endif
if(!input) driver = "None", input = new Input;
input->_driver = driver;
return input;
}
auto Input::optimalDriver() -> string {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_QUARTZ)
return "Quartz";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "None";
#endif
}
auto Input::safestDriver() -> string {
#if defined(INPUT_WINDOWS)
return "Windows";
#elif defined(INPUT_QUARTZ)
return "Quartz";
#elif defined(INPUT_CARBON)
return "Carbon";
#elif defined(INPUT_UDEV)
return "udev";
#elif defined(INPUT_SDL)
return "SDL";
#elif defined(INPUT_XLIB)
return "Xlib";
#else
return "none";
#endif
}
auto Input::availableDrivers() -> vector<string> {
return {
#if defined(INPUT_WINDOWS)
"Windows",
#endif
#if defined(INPUT_QUARTZ)
"Quartz",
#endif
#if defined(INPUT_CARBON)
"Carbon",
#endif
#if defined(INPUT_UDEV)
"udev",
#endif
#if defined(INPUT_SDL)
"SDL",
#endif
#if defined(INPUT_XLIB)
"Xlib",
#endif
"None"};
}
}
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"

View File

@ -1,118 +1,28 @@
#pragma once
#include <nall/nall.hpp>
#include <nall/platform.hpp>
#include <nall/directory.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/hid.hpp>
#include <nall/image.hpp>
#include <nall/matrix.hpp>
#include <nall/range.hpp>
#include <nall/set.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
#include <nall/dsp/resampler/cubic.hpp>
using nall::function;
using nall::shared_pointer;
using nall::string;
using nall::vector;
namespace ruby {
struct Video {
static auto create(nall::string driver = "") -> Video*;
static auto optimalDriver() -> nall::string;
static auto safestDriver() -> nall::string;
static auto availableDrivers() -> nall::vector<nall::string>;
virtual ~Video() = default;
virtual auto ready() -> bool { return true; }
virtual auto driver() -> nall::string { return _driver; }
virtual auto exclusive() -> bool { return false; }
virtual auto context() -> uintptr { return 0; }
virtual auto blocking() -> bool { return false; }
virtual auto depth() -> uint { return 24; }
virtual auto smooth() -> bool { return false; }
virtual auto shader() -> nall::string { return ""; }
virtual auto setExclusive(bool exclusive) -> bool { return false; }
virtual auto setContext(uintptr context) -> bool { return false; }
virtual auto setBlocking(bool blocking) -> bool { return false; }
virtual auto setDepth(uint depth) -> bool { return false; }
virtual auto setSmooth(bool smooth) -> bool { return false; }
virtual auto setShader(nall::string shader) -> bool { return false; }
virtual auto clear() -> void {}
virtual auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
virtual auto unlock() -> void {}
virtual auto output() -> void {}
virtual auto poll() -> void {}
auto onUpdate(const nall::function<void (uint, uint)>& callback) { _onUpdate = callback; }
auto doUpdate(uint width, uint height) -> void {
if(_onUpdate) return _onUpdate(width, height);
}
private:
nall::string _driver;
nall::function<void (uint, uint)> _onUpdate;
};
struct Audio {
static auto create(nall::string driver = "") -> Audio*;
static auto optimalDriver() -> nall::string;
static auto safestDriver() -> nall::string;
static auto availableDrivers() -> nall::vector<nall::string>;
virtual ~Audio() = default;
virtual auto availableDevices() -> nall::vector<nall::string> { return {"Default"}; }
virtual auto availableFrequencies() -> nall::vector<double> { return {44100.0}; }
virtual auto availableLatencies() -> nall::vector<uint> { return {0}; }
virtual auto availableChannels() -> nall::vector<uint> { return {2}; }
virtual auto ready() -> bool { return true; }
virtual auto driver() -> nall::string { return _driver; }
virtual auto exclusive() -> bool { return false; }
virtual auto context() -> uintptr { return 0; }
virtual auto device() -> nall::string { return "None"; }
virtual auto blocking() -> bool { return false; }
virtual auto channels() -> uint { return 2; }
virtual auto frequency() -> double { return 48000.0; }
virtual auto latency() -> uint { return 0; }
virtual auto setExclusive(bool exclusive) -> bool { return false; }
virtual auto setContext(uintptr context) -> bool { return false; }
virtual auto setDevice(nall::string device) -> bool { return false; }
virtual auto setBlocking(bool blocking) -> bool { return false; }
virtual auto setChannels(uint channels) -> bool { return false; }
virtual auto setFrequency(double frequency) -> bool { return false; }
virtual auto setLatency(uint latency) -> bool { return false; }
virtual auto clear() -> void {}
virtual auto output(const double samples[]) -> void {}
private:
nall::string _driver;
};
struct Input {
static auto create(nall::string driver = "") -> Input*;
static auto optimalDriver() -> nall::string;
static auto safestDriver() -> nall::string;
static auto availableDrivers() -> nall::vector<nall::string>;
virtual ~Input() = default;
virtual auto ready() -> bool { return true; }
virtual auto driver() -> nall::string { return _driver; }
virtual auto context() -> uintptr { return 0; }
virtual auto setContext(uintptr context) -> bool { return false; }
virtual auto acquired() -> bool { return false; }
virtual auto acquire() -> bool { return false; }
virtual auto release() -> bool { return false; }
virtual auto poll() -> nall::vector<nall::shared_pointer<nall::HID::Device>> { return {}; }
virtual auto rumble(uint64_t id, bool enable) -> bool { return false; }
auto onChange(const nall::function<void (nall::shared_pointer<nall::HID::Device>, uint, uint, int16_t, int16_t)>& callback) { _onChange = callback; }
auto doChange(nall::shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {
if(_onChange) return _onChange(device, group, input, oldValue, newValue);
}
private:
nall::string _driver;
nall::function<void (nall::shared_pointer<nall::HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue)> _onChange;
};
#include "video.hpp"
#include "audio.hpp"
#include "input.hpp"
}

220
ruby/video.cpp Normal file
View File

@ -0,0 +1,220 @@
#if defined(VIDEO_CGL)
#include <ruby/video/cgl.cpp>
#endif
#if defined(VIDEO_DIRECT3D)
#include <ruby/video/direct3d.cpp>
#endif
#if defined(VIDEO_DIRECTDRAW)
#include <ruby/video/directdraw.cpp>
#endif
#if defined(VIDEO_GDI)
#include <ruby/video/gdi.cpp>
#endif
#if defined(VIDEO_GLX)
#include <ruby/video/glx.cpp>
#endif
#if defined(VIDEO_GLX2)
#include <ruby/video/glx2.cpp>
#endif
#if defined(VIDEO_WGL)
#include <ruby/video/wgl.cpp>
#endif
#if defined(VIDEO_XSHM)
#include <ruby/video/xshm.cpp>
#endif
#if defined(VIDEO_XVIDEO)
#include <ruby/video/xvideo.cpp>
#endif
namespace ruby {
auto Video::setExclusive(bool exclusive) -> bool {
_exclusive = exclusive;
return true;
}
auto Video::setContext(uintptr context) -> bool {
_context = context;
return true;
}
auto Video::setBlocking(bool blocking) -> bool {
_blocking = blocking;
return true;
}
auto Video::setFlush(bool flush) -> bool {
_flush = flush;
return true;
}
auto Video::setFormat(string format) -> bool {
_format = format;
return true;
}
auto Video::setSmooth(bool smooth) -> bool {
_smooth = smooth;
return true;
}
auto Video::setShader(string shader) -> bool {
_shader = shader;
return true;
}
auto Video::onUpdate(const function<void (uint, uint)>& onUpdate) -> void {
_onUpdate = onUpdate;
}
auto Video::doUpdate(uint width, uint height) -> void {
if(_onUpdate) return _onUpdate(width, height);
}
//protected functions
//static functions
auto Video::create(string driver) -> Video* {
Video* video = nullptr;
if(!driver) driver = optimalDriver();
#if defined(VIDEO_CGL)
if(driver == "OpenGL") video = new VideoCGL;
#endif
#if defined(VIDEO_DIRECT3D)
if(driver == "Direct3D") video = new VideoDirect3D;
#endif
#if defined(VIDEO_DIRECTDRAW)
if(driver == "DirectDraw") video = new VideoDirectDraw;
#endif
#if defined(VIDEO_GDI)
if(driver == "GDI") video = new VideoGDI;
#endif
#if defined(VIDEO_GLX)
if(driver == "OpenGL") video = new VideoGLX;
#endif
#if defined(VIDEO_GLX2)
if(driver == "OpenGL2") video = new VideoGLX2;
#endif
#if defined(VIDEO_WGL)
if(driver == "OpenGL") video = new VideoWGL;
#endif
#if defined(VIDEO_XSHM)
if(driver == "XShm") video = new VideoXShm;
#endif
#if defined(VIDEO_XVIDEO)
if(driver == "XVideo") video = new VideoXVideo;
#endif
if(!video) video = new Video;
return video;
}
auto Video::optimalDriver() -> string {
#if defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_GLX)
return "OpenGL";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_XSHM)
return "XShm";
#else
return "None";
#endif
}
auto Video::safestDriver() -> string {
#if defined(VIDEO_DIRECT3D)
return "Direct3D";
#elif defined(VIDEO_WGL)
return "OpenGL";
#elif defined(VIDEO_DIRECTDRAW)
return "DirectDraw";
#elif defined(VIDEO_GDI)
return "GDI";
#elif defined(VIDEO_CGL)
return "OpenGL";
#elif defined(VIDEO_XSHM)
return "XShm";
#elif defined(VIDEO_XVIDEO)
return "XVideo";
#elif defined(VIDEO_GLX2)
return "OpenGL2";
#elif defined(VIDEO_GLX)
return "OpenGL";
#else
return "None";
#endif
}
auto Video::availableDrivers() -> vector<string> {
return {
#if defined(VIDEO_WGL)
"OpenGL",
#endif
#if defined(VIDEO_DIRECT3D)
"Direct3D",
#endif
#if defined(VIDEO_DIRECTDRAW)
"DirectDraw",
#endif
#if defined(VIDEO_GDI)
"GDI",
#endif
#if defined(VIDEO_CGL)
"OpenGL",
#endif
#if defined(VIDEO_GLX)
"OpenGL",
#endif
#if defined(VIDEO_GLX2)
"OpenGL2",
#endif
#if defined(VIDEO_XVIDEO)
"XVideo",
#endif
#if defined(VIDEO_XSHM)
"XShm",
#endif
"None"};
}
}

57
ruby/video.hpp Normal file
View File

@ -0,0 +1,57 @@
struct Video {
static auto create(string driver = "") -> Video*;
static auto optimalDriver() -> string;
static auto safestDriver() -> string;
static auto availableDrivers() -> vector<string>;
virtual ~Video() = default;
virtual auto driver() -> string { return "None"; }
virtual auto ready() -> bool { return true; }
virtual auto availableFormats() -> vector<string> { return {"Default"}; }
virtual auto hasExclusive() -> bool { return false; }
virtual auto hasContext() -> bool { return false; }
virtual auto hasBlocking() -> bool { return false; }
virtual auto hasFlush() -> bool { return false; }
virtual auto hasFormat() -> bool { return false; }
virtual auto hasSmooth() -> bool { return false; }
virtual auto hasShader() -> bool { return false; }
virtual auto exclusive() -> bool { return _exclusive; }
virtual auto context() -> uintptr { return _context; }
virtual auto blocking() -> bool { return _blocking; }
virtual auto flush() -> bool { return _flush; }
virtual auto format() -> string { return _format; }
virtual auto smooth() -> bool { return _smooth; }
virtual auto shader() -> string { return _shader; }
virtual auto setExclusive(bool exclusive) -> bool;
virtual auto setContext(uintptr context) -> bool;
virtual auto setBlocking(bool blocking) -> bool;
virtual auto setFlush(bool flush) -> bool;
virtual auto setFormat(string format) -> bool;
virtual auto setSmooth(bool smooth) -> bool;
virtual auto setShader(string shader) -> bool;
virtual auto clear() -> void {}
virtual auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; }
virtual auto unlock() -> void {}
virtual auto output() -> void {}
virtual auto poll() -> void {}
auto onUpdate(const function<void (uint, uint)>&) -> void;
auto doUpdate(uint width, uint height) -> void;
protected:
bool _exclusive = false;
uintptr _context = 0;
bool _blocking = false;
bool _flush = false;
string _format = "Default";
bool _smooth = false;
string _shader = "";
function<void (uint, uint)> _onUpdate;
};

View File

@ -11,67 +11,88 @@ struct VideoGLX : Video, OpenGL {
VideoGLX() { initialize(); }
~VideoGLX() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "OpenGL"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto blocking() -> bool { return _blocking; }
auto depth() -> uint { return _depth; }
auto smooth() -> bool { return _smooth; }
auto shader() -> string { return _shader; }
auto availableFormats() -> vector<string> override {
return {"R8G8B8", "R10G10B10"};
}
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto hasContext() -> bool override { return true; }
auto hasBlocking() -> bool override { return true; }
auto hasFlush() -> bool override { return true; }
auto hasFormat() -> bool override { return true; }
auto hasSmooth() -> bool override { return true; }
auto hasShader() -> bool override { return true; }
auto setContext(uintptr context) -> bool override {
if(context == Video::context()) return true;
if(!Video::setContext(context)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
auto setBlocking(bool blocking) -> bool override {
if(blocking == Video::blocking()) return true;
if(!Video::setBlocking(blocking)) return false;
if(glXSwapInterval) glXSwapInterval(_blocking);
return true;
}
auto setDepth(uint depth) -> bool {
if(_depth == depth) return true;
switch(depth) {
case 24: _depth = depth; OpenGL::inputFormat = GL_RGBA8; return true;
case 30: _depth = depth; OpenGL::inputFormat = GL_RGB10_A2; return true;
default: return false;
auto setFlush(bool flush) -> bool override {
if(flush == Video::flush()) return true;
if(!Video::setFlush(flush)) return false;
return true;
}
auto setFormat(string format) -> bool override {
if(format == Video::format()) return true;
if(!Video::setFormat(format)) return false;
if(format == "R8G8B8") {
OpenGL::inputFormat = GL_RGBA8;
return true;
}
if(format == "R10G10B10") {
OpenGL::inputFormat = GL_RGB10_A2;
return true;
}
return false;
}
auto setSmooth(bool smooth) -> bool {
if(_smooth == smooth) return true;
_smooth = smooth;
if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
auto setSmooth(bool smooth) -> bool override {
if(smooth == Video::smooth()) return true;
if(!Video::setSmooth(smooth)) return false;
if(!shader()) OpenGL::filter = smooth ? GL_LINEAR : GL_NEAREST;
return true;
}
auto setShader(string shader) -> bool {
if(_shader == shader) return true;
OpenGL::shader(_shader = shader);
if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
auto setShader(string shader) -> bool override {
if(shader == Video::shader()) return true;
if(!Video::setShader(shader)) return false;
OpenGL::setShader(shader);
if(!shader) OpenGL::filter = smooth() ? GL_LINEAR : GL_NEAREST;
return true;
}
auto clear() -> void {
auto clear() -> void override {
if(!ready()) return;
OpenGL::clear();
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
if(!ready()) return false;
OpenGL::size(width, height);
return OpenGL::lock(data, pitch);
}
auto unlock() -> void {
auto unlock() -> void override {
if(!ready()) return;
}
auto output() -> void {
auto output() -> void override {
if(!ready()) return;
//we must ensure that the child window is the same size as the parent window.
@ -89,9 +110,10 @@ struct VideoGLX : Video, OpenGL {
OpenGL::outputHeight = parent.height;
OpenGL::output();
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
if(flush()) glFinish();
}
auto poll() -> void {
auto poll() -> void override {
while(XPending(_display)) {
XEvent event;
XNextEvent(_display, &event);
@ -118,15 +140,19 @@ private:
XWindowAttributes windowAttributes;
XGetWindowAttributes(_display, (Window)_context, &windowAttributes);
int redDepth = Video::format() == "R10G10B10" ? 10 : 8;
int greenDepth = Video::format() == "R10G10B10" ? 10 : 8;
int blueDepth = Video::format() == "R10G10B10" ? 10 : 8;
//let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute
int attributeList[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, (int)(_depth / 3),
GLX_GREEN_SIZE, (int)(_depth / 3) + (int)(_depth % 3),
GLX_BLUE_SIZE, (int)(_depth / 3),
GLX_RED_SIZE, redDepth,
GLX_GREEN_SIZE, greenDepth,
GLX_BLUE_SIZE, blueDepth,
None
};
@ -141,7 +167,7 @@ private:
//it is not possible to change the Visual of an already realized (created) window.
//therefore a new child window, using the same GLX Visual, must be created and binded to it.
_colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
XSetWindowAttributes attributes = {};
attributes.colormap = _colormap;
attributes.border_pixel = 0;
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
@ -230,11 +256,6 @@ private:
}
bool _ready = false;
uintptr _context = 0;
bool _blocking = false;
uint _depth = 24;
bool _smooth = true;
string _shader;
auto (*glXSwapInterval)(int) -> int = nullptr;

View File

@ -25,32 +25,40 @@ struct VideoGLX2 : Video {
VideoGLX2() { initialize(); }
~VideoGLX2() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "OpenGL2"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto blocking() -> bool { return _blocking; }
auto smooth() -> bool { return _smooth; }
auto hasContext() -> bool override { return true; }
auto hasBlocking() -> bool override { return true; }
auto hasFlush() -> bool override { return true; }
auto hasSmooth() -> bool override { return true; }
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto setContext(uintptr context) -> bool override {
if(context == this->context()) return true;
if(!Video::setContext(context)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
if(_ready && glXSwapInterval) glXSwapInterval(_blocking);
auto setBlocking(bool blocking) -> bool override {
if(blocking == this->blocking()) return true;
if(!Video::setBlocking(blocking)) return false;
if(ready() && glXSwapInterval) glXSwapInterval(_blocking);
return true;
}
auto setSmooth(bool smooth) -> bool {
if(_smooth == smooth) return true;
_smooth = smooth;
auto setFlush(bool flush) -> bool override {
if(flush == this->flush()) return true;
if(!Video::setFlush(flush)) return false;
return true;
}
auto clear() -> void {
auto setSmooth(bool smooth) -> bool override {
if(smooth == this->smooth()) return true;
if(!Video::setSmooth(smooth)) return false;
return true;
}
auto clear() -> void override {
if(!ready()) return;
memory::fill<uint32_t>(_glBuffer, _glWidth * _glHeight);
glClearColor(0.0, 0.0, 0.0, 1.0);
@ -59,18 +67,18 @@ struct VideoGLX2 : Video {
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
if(!ready()) return false;
if(width != _width || height != _height) resize(width, height);
pitch = _glWidth * sizeof(uint32_t);
return data = _glBuffer;
}
auto unlock() -> void {
auto unlock() -> void override {
if(!ready()) return;
}
auto output() -> void {
auto output() -> void override {
if(!ready()) return;
XWindowAttributes parent, child;
@ -109,9 +117,10 @@ struct VideoGLX2 : Video {
glFlush();
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
if(flush()) glFinish();
}
auto poll() -> void {
auto poll() -> void override {
while(XPending(_display)) {
XEvent event;
XNextEvent(_display, &event);
@ -154,7 +163,7 @@ private:
auto vi = glXGetVisualFromFBConfig(_display, fbConfig[0]);
_colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
XSetWindowAttributes attributes = {};
attributes.colormap = _colormap;
attributes.border_pixel = 0;
_window = XCreateWindow(_display, (Window)_context, 0, 0, windowAttributes.width, windowAttributes.height,
@ -250,9 +259,6 @@ private:
auto (*glXSwapInterval)(int) -> int = nullptr;
bool _ready = false;
uintptr _context = 0;
bool _blocking = false;
bool _smooth = true;
Display* _display = nullptr;
int _screen = 0;

View File

@ -1,4 +1,4 @@
auto OpenGL::shader(const string& pathname) -> void {
auto OpenGL::setShader(const string& pathname) -> void {
for(auto& program : programs) program.release();
programs.reset();
@ -196,13 +196,13 @@ auto OpenGL::initialize() -> bool {
OpenGLSurface::allocate();
glrLinkProgram(program);
shader("");
setShader("");
return initialized = true;
}
auto OpenGL::terminate() -> void {
if(!initialized) return;
shader(""); //release shader resources (eg frame[] history)
setShader(""); //release shader resources (eg frame[] history)
OpenGLSurface::release();
if(buffer) { delete[] buffer; buffer = nullptr; }
initialized = false;

View File

@ -65,7 +65,7 @@ struct OpenGLProgram : OpenGLSurface {
};
struct OpenGL : OpenGLProgram {
auto shader(const string& pathname) -> void;
auto setShader(const string& pathname) -> void;
auto allocateHistory(uint size) -> void;
auto clear() -> void;
auto lock(uint32_t*& data, uint& pitch) -> bool;

View File

@ -12,23 +12,25 @@ struct VideoXShm : Video {
VideoXShm() { initialize(); }
~VideoXShm() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "XShm"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto smooth() -> bool { return _smooth; }
auto hasContext() -> bool override { return true; }
auto hasSmooth() -> bool override { return true; }
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto setContext(uintptr context) -> bool override {
if(context == this->context()) return true;
if(!Video::setContext(context)) return false;
return initialize();
}
auto setSmooth(bool smooth) -> bool {
_smooth = smooth;
auto setSmooth(bool smooth) -> bool override {
if(smooth == this->smooth()) return true;
if(!Video::setSmooth(smooth)) return false;
return true;
}
auto clear() -> void {
auto clear() -> void override {
if(!ready()) return;
auto dp = _inputBuffer;
uint length = _inputWidth * _inputHeight;
@ -36,7 +38,7 @@ struct VideoXShm : Video {
output();
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
if(!ready()) return false;
if(!_inputBuffer || _inputWidth != width || _inputHeight != height) {
if(_inputBuffer) delete[] _inputBuffer;
@ -50,11 +52,11 @@ struct VideoXShm : Video {
return true;
}
auto unlock() -> void {
auto unlock() -> void override {
if(!ready()) return;
}
auto output() -> void {
auto output() -> void override {
if(!ready()) return;
size();
@ -190,8 +192,6 @@ private:
}
bool _ready = false;
uintptr _context = 0;
bool _smooth = true;
uint32_t* _inputBuffer = nullptr;
uint _inputWidth = 0;

View File

@ -10,20 +10,26 @@ struct VideoXVideo : Video {
VideoXVideo() { initialize(); }
~VideoXVideo() { terminate(); }
auto ready() -> bool { return _ready; }
auto driver() -> string override { return "XVideo"; }
auto ready() -> bool override { return _ready; }
auto context() -> uintptr { return _context; }
auto blocking() -> bool { return _blocking; }
auto availableFormats() -> vector<string> override {
return _formatNames;
}
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
auto hasContext() -> bool override { return true; }
auto hasBlocking() -> bool override { return true; }
auto hasFormat() -> bool override { return true; }
auto setContext(uintptr context) -> bool override {
if(context == Video::context()) return true;
if(!Video::setContext(context)) return false;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
auto setBlocking(bool blocking) -> bool override {
if(blocking == Video::blocking()) return true;
if(!Video::setBlocking(blocking)) return false;
bool result = false;
Display* display = XOpenDisplay(nullptr);
@ -36,7 +42,13 @@ struct VideoXVideo : Video {
return result;
}
auto clear() -> void {
auto setFormat(string format) -> bool override {
if(format == Video::format()) return true;
if(!Video::setFormat(format)) return false;
return initialize();
}
auto clear() -> void override {
if(!ready()) return;
memory::fill<uint32_t>(_buffer, _bufferWidth * _bufferHeight);
//clear twice in case video is double buffered ...
@ -44,18 +56,18 @@ struct VideoXVideo : Video {
output();
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
if(!ready()) return false;
if(width != _width || height != _height) resize(_width = width, _height = height);
pitch = _bufferWidth * 4;
return data = _buffer;
}
auto unlock() -> void {
auto unlock() -> void override {
if(!ready()) return;
}
auto output() -> void {
auto output() -> void override {
if(!ready()) return;
XWindowAttributes target;
@ -74,16 +86,15 @@ struct VideoXVideo : Video {
//update target width and height attributes
XGetWindowAttributes(_display, _window, &target);
switch(_format) {
case XvFormatRGB32: renderRGB32(_width, _height); break;
case XvFormatRGB24: renderRGB24(_width, _height); break;
case XvFormatRGB16: renderRGB16(_width, _height); break;
case XvFormatRGB15: renderRGB15(_width, _height); break;
case XvFormatUYVY: renderUYVY (_width, _height); break;
case XvFormatYUY2: renderYUY2 (_width, _height); break;
case XvFormatYV12: renderYV12 (_width, _height); break;
case XvFormatI420: renderI420 (_width, _height); break;
}
auto& name = _formatName;
if(name == "RGB32") renderRGB32(_width, _height);
if(name == "RGB24") renderRGB24(_width, _height);
if(name == "RGB16") renderRGB16(_width, _height);
if(name == "RGB15") renderRGB15(_width, _height);
if(name == "UYVY" ) renderUYVY (_width, _height);
if(name == "YUY2" ) renderYUY2 (_width, _height);
if(name == "YV12" ) renderYV12 (_width, _height);
if(name == "I420" ) renderI420 (_width, _height);
XvShmPutImage(_display, _port, _window, _gc, _image,
0, 0, _width, _height,
@ -91,7 +102,7 @@ struct VideoXVideo : Video {
true);
}
auto poll() -> void {
auto poll() -> void override {
while(XPending(_display)) {
XEvent event;
XNextEvent(_display, &event);
@ -111,12 +122,14 @@ private:
_display = XOpenDisplay(nullptr);
if(!XShmQueryExtension(_display)) {
print("VideoXv: XShm extension not found.\n");
print("XVideo: XShm extension not found.\n");
return false;
}
//find an appropriate Xv port
_port = -1;
int depth = 0;
int visualID = 0;
XvAdaptorInfo* adaptorInfo = nullptr;
uint adaptorCount = 0;
XvQueryAdaptors(_display, DefaultRootWindow(_display), &adaptorCount, &adaptorInfo);
@ -127,42 +140,42 @@ private:
if(!(adaptorInfo[n].type & XvImageMask)) continue;
_port = adaptorInfo[n].base_id;
_depth = adaptorInfo[n].formats->depth;
_visualID = adaptorInfo[n].formats->visual_id;
depth = adaptorInfo[n].formats->depth;
visualID = adaptorInfo[n].formats->visual_id;
break;
}
XvFreeAdaptorInfo(adaptorInfo);
if(_port < 0) {
print("VideoXv: failed to find valid XvPort.\n");
print("XVideo: failed to find valid XvPort.\n");
return false;
}
//create child window to attach to parent window.
//this is so that even if parent window visual depth doesn't match Xv visual
//(common with composited windows), Xv can still render to child window.
XWindowAttributes window_attributes;
XGetWindowAttributes(_display, (Window)_context, &window_attributes);
XWindowAttributes windowAttributes;
XGetWindowAttributes(_display, (Window)_context, &windowAttributes);
XVisualInfo visualTemplate;
visualTemplate.visualid = _visualID;
visualTemplate.visualid = visualID;
visualTemplate.screen = DefaultScreen(_display);
visualTemplate.depth = _depth;
visualTemplate.depth = depth;
visualTemplate.visual = 0;
int visualMatches = 0;
XVisualInfo* visualInfo = XGetVisualInfo(_display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualTemplate, &visualMatches);
auto visualInfo = XGetVisualInfo(_display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualTemplate, &visualMatches);
if(visualMatches < 1 || !visualInfo->visual) {
if(visualInfo) XFree(visualInfo);
print("VideoXv: unable to find Xv-compatible visual.\n");
print("XVideo: unable to find Xv-compatible visual.\n");
return false;
}
_colormap = XCreateColormap(_display, (Window)_context, visualInfo->visual, AllocNone);
XSetWindowAttributes attributes;
XSetWindowAttributes attributes = {};
attributes.colormap = _colormap;
attributes.border_pixel = 0;
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
/* border_width = */ 0, _depth, InputOutput, visualInfo->visual,
/* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height,
/* border_width = */ 0, depth, InputOutput, visualInfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XSelectInput(_display, _window, ExposureMask);
XFree(visualInfo);
@ -172,7 +185,7 @@ private:
_gc = XCreateGC(_display, _window, 0, 0);
int attributeCount = 0;
XvAttribute* attributeList = XvQueryPortAttributes(_display, _port, &attributeCount);
auto attributeList = XvQueryPortAttributes(_display, _port, &attributeCount);
for(auto n : range(attributeCount)) {
if(string{attributeList[n].name} == "XV_AUTOPAINT_COLORKEY") {
//set colorkey to auto paint, so that Xv video output is always visible
@ -182,96 +195,19 @@ private:
}
XFree(attributeList);
//find optimal rendering format
_format = XvFormatUnknown;
int formatCount = 0;
XvImageFormatValues* format = XvListImageFormats(_display, _port, &formatCount);
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvRGB && format[n].bits_per_pixel == 32) {
_format = XvFormatRGB32;
_fourCC = format[n].id;
break;
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvRGB && format[n].bits_per_pixel == 24) {
_format = XvFormatRGB24;
_fourCC = format[n].id;
break;
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0xf800) {
_format = XvFormatRGB16;
_fourCC = format[n].id;
break;
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0x7c00) {
_format = XvFormatRGB15;
_fourCC = format[n].id;
break;
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
if(format[n].component_order[0] == 'U' && format[n].component_order[1] == 'Y'
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == 'Y'
) {
_format = XvFormatUYVY;
_fourCC = format[n].id;
break;
}
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'U'
&& format[n].component_order[2] == 'Y' && format[n].component_order[3] == 'V'
) {
_format = XvFormatYUY2;
_fourCC = format[n].id;
break;
}
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 12 && format[n].format == XvPlanar) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'V'
&& format[n].component_order[2] == 'U' && format[n].component_order[3] == '\x00'
) {
_format = XvFormatYV12;
_fourCC = format[n].id;
break;
}
}
}
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
if(format[n].type == XvYUV && format[n].bits_per_pixel == 12 && format[n].format == XvPlanar) {
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'U'
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == '\x00'
) {
_format = XvFormatI420;
_fourCC = format[n].id;
break;
}
}
}
free(format);
if(_format == XvFormatUnknown) {
print("VideoXv: unable to find a supported image format.\n");
queryAvailableFormats();
if(!_formatNames) {
print("XVideo: unable to find a supported image format.\n");
return false;
}
if(auto match = _formatNames.find(Video::format())) {
_formatID = _formatIDs[match()];
_formatName = _formatNames[match()];
} else {
_formatID = _formatIDs[0];
_formatName = _formatNames[0];
Video::setFormat(_formatName);
}
_ready = true;
initializeTables();
@ -291,6 +227,11 @@ private:
_image = nullptr;
}
if(_gc) {
XFreeGC(_display, _gc);
_gc = 0;
}
if(_window) {
XUnmapWindow(_display, _window);
_window = 0;
@ -312,6 +253,49 @@ private:
delete[] _vtable, _vtable = nullptr;
}
auto queryAvailableFormats() -> void {
auto& ids = _formatIDs;
auto& names = _formatNames;
ids.reset();
names.reset();
int count = 0;
auto array = XvListImageFormats(_display, _port, &count);
for(uint sort : range(8)) {
for(uint n : range(count)) {
auto id = array[n].id;
auto type = array[n].type;
auto format = array[n].format;
auto depth = array[n].bits_per_pixel;
auto redMask = array[n].red_mask;
auto order = array[n].component_order;
string components;
for(uint n : range(4)) if(char c = order[n]) components.append(c);
if(type == XvRGB) {
if(sort == 0 && depth == 32) ids.append(id), names.append("RGB32");
if(sort == 1 && depth == 24) ids.append(id), names.append("RGB24");
if(sort == 2 && depth <= 16 && redMask == 0xf800) ids.append(id), names.append("RGB16");
if(sort == 3 && depth <= 16 && redMask == 0x7c00) ids.append(id), names.append("RGB15");
}
if(type == XvYUV && format == XvPacked) {
if(sort == 4 && depth == 16 && components == "UYVY") ids.append(id), names.append("UYVY");
if(sort == 5 && depth == 16 && components == "YUYV") ids.append(id), names.append("YUY2");
}
if(type == XvYUV && format == XvPlanar) {
if(sort == 6 && depth == 12 && components == "YVU" ) ids.append(id), names.append("YV12");
if(sort == 7 && depth == 12 && components == "YUV" ) ids.append(id), names.append("I420");
}
}
}
free(array);
}
auto resize(uint width, uint height) -> void {
if(_bufferWidth >= width && _bufferHeight >= height) return;
_bufferWidth = max(width, _bufferWidth);
@ -331,7 +315,7 @@ private:
XFree(_image);
}
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
_image = XvShmCreateImage(_display, _port, _formatID, 0, _bufferWidth, _bufferHeight, &_shmInfo);
_shmInfo.shmid = shmget(IPC_PRIVATE, _image->data_size, IPC_CREAT | 0777);
_shmInfo.shmaddr = _image->data = (char*)shmat(_shmInfo.shmid, 0, 0);
@ -343,38 +327,36 @@ private:
}
auto renderRGB32(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
uint32_t* output = (uint32_t*)_image->data;
for(uint y : range(height)) {
memory::copy<uint32_t>(output, input, width);
input += _bufferWidth;
output += _bufferWidth;
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint32_t*)_image->data + y * (_image->pitches[0] >> 2);
for(uint x : range(width)) {
uint32_t p = *input++;
*output++ = p;
}
}
}
auto renderRGB24(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
uint8_t* output = (uint8_t*)_image->data;
for(uint y : range(height)) {
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint8_t*)_image->data + y * _image->pitches[0];
for(uint x : range(width)) {
uint32_t p = *input++;
*output++ = p >> 0;
*output++ = p >> 8;
*output++ = p >> 16;
}
input += (_bufferWidth - width);
output += (_bufferWidth - width) * 3;
}
}
auto renderRGB16(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
for(uint x : range(width)) {
uint32_t p = toRGB16(*input++);
*output++ = p;
@ -386,25 +368,22 @@ private:
}
auto renderRGB15(uint width, uint height) -> void {
uint32_t* input = (uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
for(uint x : range(width)) {
uint32_t p = toRGB15(*input++);
*output++ = p;
}
input += _bufferWidth - width;
output += _bufferWidth - width;
}
}
auto renderUYVY(uint width, uint height) -> void {
const uint32_t* input = (const uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
for(uint x : range(width >> 1)) {
uint32_t p0 = toRGB16(*input++);
uint32_t p1 = toRGB16(*input++);
@ -412,17 +391,14 @@ private:
*output++ = _ytable[p0] << 8 | ((_utable[p0] + _utable[p1]) >> 1) << 0;
*output++ = _ytable[p1] << 8 | ((_vtable[p0] + _vtable[p1]) >> 1) << 0;
}
input += _bufferWidth - width;
output += _bufferWidth - width;
}
}
auto renderYUY2(uint width, uint height) -> void {
const uint32_t* input = (const uint32_t*)_buffer;
uint16_t* output = (uint16_t*)_image->data;
for(uint y : range(height)) {
auto input = (const uint32_t*)_buffer + y * width;
auto output = (uint16_t*)_image->data + y * (_image->pitches[0] >> 1);
for(uint x : range(width >> 1)) {
uint32_t p0 = toRGB16(*input++);
uint32_t p1 = toRGB16(*input++);
@ -430,22 +406,17 @@ private:
*output++ = ((_utable[p0] + _utable[p1]) >> 1) << 8 | _ytable[p0] << 0;
*output++ = ((_vtable[p0] + _vtable[p1]) >> 1) << 8 | _ytable[p1] << 0;
}
input += _bufferWidth - width;
output += _bufferWidth - width;
}
}
auto renderYV12(uint width, uint height) -> void {
const uint w = _bufferWidth, h = _bufferHeight;
for(uint y : range(height >> 1)) {
const uint32_t* input0 = (const uint32_t*)_buffer + (2 * y * w);
const uint32_t* input1 = input0 + w;
uint16_t* youtput0 = (uint16_t*)_image->data + ((2 * y * w) >> 1);
uint16_t* youtput1 = youtput0 + (w >> 1);
uint8_t* voutput = (uint8_t*)_image->data + (w * h) + ((2 * y * w) >> 2);
uint8_t* uoutput = (uint8_t*)_image->data + (w * h) + ((w * h) >> 2) + ((2 * y * w) >> 2);
auto input0 = (const uint32_t*)_buffer + (2 * y + 0) * width;
auto input1 = (const uint32_t*)_buffer + (2 * y + 1) * width;
auto youtput0 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 0) * (_image->pitches[0] >> 1);
auto youtput1 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 1) * (_image->pitches[0] >> 1);
auto voutput = (uint8_t*)_image->data + _image->offsets[1] + y * _image->pitches[1];
auto uoutput = (uint8_t*)_image->data + _image->offsets[2] + y * _image->pitches[2];
for(uint x : range(width >> 1)) {
uint16_t p0 = toRGB16(*input0++);
@ -462,15 +433,13 @@ private:
}
auto renderI420(uint width, uint height) -> void {
const uint w = _bufferWidth, h = _bufferHeight;
for(uint y : range(height >> 1)) {
const uint32_t* input0 = (const uint32_t*)_buffer + (2 * y * w);
const uint32_t* input1 = input0 + w;
uint16_t* youtput0 = (uint16_t*)_image->data + ((2 * y * w) >> 1);
uint16_t* youtput1 = youtput0 + (w >> 1);
uint8_t* uoutput = (uint8_t*)_image->data + (w * h) + ((2 * y * w) >> 2);
uint8_t* voutput = (uint8_t*)_image->data + (w * h) + ((w * h) >> 2) + ((2 * y * w) >> 2);
auto input0 = (const uint32_t*)_buffer + (2 * y + 0) * width;
auto input1 = (const uint32_t*)_buffer + (2 * y + 1) * width;
auto youtput0 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 0) * (_image->pitches[0] >> 1);
auto youtput1 = (uint16_t*)_image->data + (_image->offsets[0] >> 1) + (2 * y + 1) * (_image->pitches[0] >> 1);
auto uoutput = (uint8_t*)_image->data + _image->offsets[1] + y * _image->pitches[1];
auto voutput = (uint8_t*)_image->data + _image->offsets[2] + y * _image->pitches[2];
for(uint x : range(width >> 1)) {
uint16_t p0 = toRGB16(*input0++);
@ -525,8 +494,6 @@ private:
}
bool _ready = false;
uintptr _context = 0;
bool _blocking = false;
uint _width = 0;
uint _height = 0;
@ -539,18 +506,6 @@ private:
uint8_t* _utable = nullptr;
uint8_t* _vtable = nullptr;
enum XvFormat : uint {
XvFormatRGB32,
XvFormatRGB24,
XvFormatRGB16,
XvFormatRGB15,
XvFormatUYVY,
XvFormatYUY2,
XvFormatYV12,
XvFormatI420,
XvFormatUnknown,
};
Display* _display = nullptr;
GC _gc = 0;
Window _window = 0;
@ -558,10 +513,11 @@ private:
XShmSegmentInfo _shmInfo;
int _port = -1;
int _depth = 0;
int _visualID = 0;
XvImage* _image = nullptr;
XvFormat _format = XvFormatUnknown;
uint32_t _fourCC = 0;
vector<int> _formatIDs;
vector<string> _formatNames;
int _formatID = 0;
string _formatName;
};