mirror of https://github.com/bsnes-emu/bsnes.git
Update to v098r05 release.
byuu says: Changelog: - WS/WSC: re-added support for screen rotation (code is inside WS core) - ruby: changed sample(uint16_t left, uint16_t right) to sample(int16_t left, int16_t right); - requires casting to uint prior to shifting in each driver, but I felt it was misleading to use uint16_t just to avoid that - ruby: WASAPI is now built in by default; has wareya's improvements, and now supports latency adjust - tomoko: audio settings panel has new "Exclusive Mode" checkbox for WASAPI driver only - note: although the setting *does* take effect in real-time, I'd suggest restarting the emulator after changing it - tomoko: audio latency can now be set to 0ms (which in reality means "the minimum supported by the driver") - all: increased cothread size from 512KiB to 2MiB to see if it fixes bullshit AMD driver crashes - this appears to cause a slight speed penalty due to cache locality going down between threads, though
This commit is contained in:
parent
a2d3b8ba15
commit
55e507d5df
|
@ -8,7 +8,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "098.04";
|
static const string Version = "098.05";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Famicom {
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||||
this->frequency = frequency;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace GameBoy {
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||||
this->frequency = frequency;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace GameBoyAdvance {
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||||
this->frequency = frequency;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace SuperFamicom {
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||||
this->frequency = frequency;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ ui_objects += $(if $(call streq,$(platform),windows),ui-resource)
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
ruby += video.direct3d video.wgl video.directdraw video.gdi
|
ruby += video.direct3d video.wgl video.directdraw video.gdi
|
||||||
ruby += audio.xaudio2 audio.directsound
|
ruby += audio.wasapi audio.xaudio2 audio.directsound
|
||||||
ruby += input.windows
|
ruby += input.windows
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
ruby += video.cgl
|
ruby += video.cgl
|
||||||
|
|
|
@ -29,6 +29,7 @@ Settings::Settings() {
|
||||||
|
|
||||||
set("Audio/Driver", ruby::Audio::optimalDriver());
|
set("Audio/Driver", ruby::Audio::optimalDriver());
|
||||||
set("Audio/Device", "");
|
set("Audio/Device", "");
|
||||||
|
set("Audio/Exclusive", false);
|
||||||
set("Audio/Synchronize", true);
|
set("Audio/Synchronize", true);
|
||||||
set("Audio/Mute", false);
|
set("Audio/Mute", false);
|
||||||
set("Audio/Volume", 100);
|
set("Audio/Volume", 100);
|
||||||
|
|
|
@ -43,7 +43,7 @@ Program::Program(lstring args) {
|
||||||
|
|
||||||
dsp.setPrecision(16);
|
dsp.setPrecision(16);
|
||||||
dsp.setBalance(0.0);
|
dsp.setBalance(0.0);
|
||||||
dsp.setFrequency(32040);
|
dsp.setFrequency(44100);
|
||||||
dsp.setResampler(DSP::ResampleEngine::Sinc);
|
dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||||
dsp.setResamplerFrequency(audio->get(Audio::Frequency).get<uint>());
|
dsp.setResamplerFrequency(audio->get(Audio::Frequency).get<uint>());
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ Program::Program(lstring args) {
|
||||||
new ToolsManager;
|
new ToolsManager;
|
||||||
|
|
||||||
updateVideoShader();
|
updateVideoShader();
|
||||||
updateAudioVolume();
|
updateAudio();
|
||||||
|
|
||||||
args.takeFirst(); //ignore program location in argument parsing
|
args.takeFirst(); //ignore program location in argument parsing
|
||||||
for(auto& argument : args) {
|
for(auto& argument : args) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ struct Program : Emulator::Interface::Bind {
|
||||||
auto updateStatusText() -> void;
|
auto updateStatusText() -> void;
|
||||||
auto updateVideoShader() -> void;
|
auto updateVideoShader() -> void;
|
||||||
auto updateAudio() -> void;
|
auto updateAudio() -> void;
|
||||||
|
auto updateAudioMode() -> void;
|
||||||
auto updateAudioVolume() -> void;
|
auto updateAudioVolume() -> void;
|
||||||
auto updateDSP() -> void;
|
auto updateDSP() -> void;
|
||||||
|
|
||||||
|
|
|
@ -71,10 +71,15 @@ auto Program::updateAudio() -> void {
|
||||||
if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite);
|
if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite);
|
||||||
if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc);
|
if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||||
dsp.setResamplerFrequency(audio->get(Audio::Frequency).get<uint>());
|
dsp.setResamplerFrequency(audio->get(Audio::Frequency).get<uint>());
|
||||||
|
updateAudioMode();
|
||||||
updateAudioVolume();
|
updateAudioVolume();
|
||||||
updateDSP();
|
updateDSP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Program::updateAudioMode() -> void {
|
||||||
|
audio->set(Audio::Exclusive, settings["Audio/Exclusive"].boolean());
|
||||||
|
}
|
||||||
|
|
||||||
auto Program::updateAudioVolume() -> void {
|
auto Program::updateAudioVolume() -> void {
|
||||||
dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01);
|
dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,19 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
frequencyCombo.setEnabled(false); //not user configurable
|
frequencyCombo.setEnabled(false); //not user configurable
|
||||||
|
|
||||||
latencyLabel.setText("Latency:");
|
latencyLabel.setText("Latency:");
|
||||||
|
latencyCombo.append(ComboButtonItem().setText("0ms"));
|
||||||
latencyCombo.append(ComboButtonItem().setText("20ms"));
|
latencyCombo.append(ComboButtonItem().setText("20ms"));
|
||||||
latencyCombo.append(ComboButtonItem().setText("40ms"));
|
latencyCombo.append(ComboButtonItem().setText("40ms"));
|
||||||
latencyCombo.append(ComboButtonItem().setText("60ms"));
|
latencyCombo.append(ComboButtonItem().setText("60ms"));
|
||||||
latencyCombo.append(ComboButtonItem().setText("80ms"));
|
latencyCombo.append(ComboButtonItem().setText("80ms"));
|
||||||
latencyCombo.append(ComboButtonItem().setText("100ms"));
|
latencyCombo.append(ComboButtonItem().setText("100ms"));
|
||||||
switch(settings["Audio/Latency"].natural()) {
|
switch(settings["Audio/Latency"].natural()) {
|
||||||
case 20: latencyCombo.item(0)->setSelected(); break;
|
case 0: latencyCombo.item(0)->setSelected(); break;
|
||||||
case 40: latencyCombo.item(1)->setSelected(); break;
|
case 20: latencyCombo.item(1)->setSelected(); break;
|
||||||
case 60: latencyCombo.item(2)->setSelected(); break;
|
case 40: latencyCombo.item(2)->setSelected(); break;
|
||||||
case 80: latencyCombo.item(3)->setSelected(); break;
|
case 60: latencyCombo.item(3)->setSelected(); break;
|
||||||
case 100: latencyCombo.item(4)->setSelected(); break;
|
case 80: latencyCombo.item(4)->setSelected(); break;
|
||||||
|
case 100: latencyCombo.item(5)->setSelected(); break;
|
||||||
}
|
}
|
||||||
latencyCombo.onChange([&] { update(); });
|
latencyCombo.onChange([&] { update(); });
|
||||||
|
|
||||||
|
@ -36,17 +38,21 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
volumeLabel.setText("Volume:");
|
volumeLabel.setText("Volume:");
|
||||||
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); });
|
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); });
|
||||||
|
|
||||||
|
exclusiveMode.setText("Exclusive Mode").setVisible(audio->cap(Audio::Exclusive));
|
||||||
|
exclusiveMode.setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] { updateMode(); });
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AudioSettings::update() -> void {
|
auto AudioSettings::update() -> void {
|
||||||
if(auto item = latencyCombo.selected()) {
|
if(auto item = latencyCombo.selected()) {
|
||||||
uint latency = 60;
|
uint latency = 60;
|
||||||
if(item->offset() == 0) latency = 20;
|
if(item->offset() == 0) latency = 0;
|
||||||
if(item->offset() == 1) latency = 40;
|
if(item->offset() == 1) latency = 20;
|
||||||
if(item->offset() == 2) latency = 60;
|
if(item->offset() == 2) latency = 40;
|
||||||
if(item->offset() == 3) latency = 80;
|
if(item->offset() == 3) latency = 60;
|
||||||
if(item->offset() == 4) latency = 100;
|
if(item->offset() == 4) latency = 80;
|
||||||
|
if(item->offset() == 5) latency = 100;
|
||||||
settings["Audio/Latency"].setValue(latency);
|
settings["Audio/Latency"].setValue(latency);
|
||||||
}
|
}
|
||||||
if(auto item = resamplerCombo.selected()) {
|
if(auto item = resamplerCombo.selected()) {
|
||||||
|
@ -57,7 +63,12 @@ auto AudioSettings::update() -> void {
|
||||||
settings["Audio/Resampler"].setValue(resampler);
|
settings["Audio/Resampler"].setValue(resampler);
|
||||||
}
|
}
|
||||||
updateVolume();
|
updateVolume();
|
||||||
program->updateAudio();
|
updateMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AudioSettings::updateMode() -> void {
|
||||||
|
settings["Audio/Exclusive"].setValue(exclusiveMode.checked());
|
||||||
|
program->updateAudioMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AudioSettings::updateVolume() -> void {
|
auto AudioSettings::updateVolume() -> void {
|
||||||
|
|
|
@ -30,8 +30,10 @@ struct AudioSettings : TabFrameItem {
|
||||||
Label volumeLabel{&volumeLayout, Size{80, 0}};
|
Label volumeLabel{&volumeLayout, Size{80, 0}};
|
||||||
Label volumeValue{&volumeLayout, Size{80, 0}};
|
Label volumeValue{&volumeLayout, Size{80, 0}};
|
||||||
HorizontalSlider volumeSlider{&volumeLayout, Size{~0, 0}};
|
HorizontalSlider volumeSlider{&volumeLayout, Size{~0, 0}};
|
||||||
|
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
|
||||||
|
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
auto updateMode() -> void;
|
||||||
auto updateVolume() -> void;
|
auto updateVolume() -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ Interface::Interface() {
|
||||||
information.manufacturer = "Bandai";
|
information.manufacturer = "Bandai";
|
||||||
information.name = "WonderSwan";
|
information.name = "WonderSwan";
|
||||||
information.width = 224; //note: technically 224x144; but screen can be rotated
|
information.width = 224; //note: technically 224x144; but screen can be rotated
|
||||||
information.height = 144; //by using a square size; this can be done in the core
|
information.height = 224; //by using a square size; this can be done in the core
|
||||||
information.overscan = false;
|
information.overscan = false;
|
||||||
information.aspectRatio = 1.0;
|
information.aspectRatio = 1.0;
|
||||||
information.resettable = false;
|
information.resettable = false;
|
||||||
|
|
|
@ -28,7 +28,10 @@ auto PPU::main() -> void {
|
||||||
if(l.screenTwoEnable) renderScreenTwo();
|
if(l.screenTwoEnable) renderScreenTwo();
|
||||||
if(l.spriteEnable) renderSprite();
|
if(l.spriteEnable) renderSprite();
|
||||||
}
|
}
|
||||||
output[s.vclk * 224 + s.hclk] = s.pixel.color;
|
switch(l.orientation) {
|
||||||
|
case 0: output[(s.vclk + 40) * 224 + s.hclk] = s.pixel.color; break;
|
||||||
|
case 1: output[(223 - s.hclk) * 224 + (s.vclk + 40)] = s.pixel.color; break;
|
||||||
|
}
|
||||||
step(1);
|
step(1);
|
||||||
}
|
}
|
||||||
step(32);
|
step(32);
|
||||||
|
@ -72,7 +75,11 @@ auto PPU::scanline() -> void {
|
||||||
auto PPU::frame() -> void {
|
auto PPU::frame() -> void {
|
||||||
s.field = !s.field;
|
s.field = !s.field;
|
||||||
s.vclk = 0;
|
s.vclk = 0;
|
||||||
Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 144);
|
Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 224);
|
||||||
|
if(l.orientation != system.orientation()) {
|
||||||
|
l.orientation = system.orientation();
|
||||||
|
memory::fill(output, 224 * 224 * sizeof(uint32));
|
||||||
|
}
|
||||||
scheduler.exit(Scheduler::Event::Frame);
|
scheduler.exit(Scheduler::Event::Frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct PPU : Thread, IO {
|
||||||
uint12 color;
|
uint12 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 output[224 * 144];
|
uint32 output[224 * 224];
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
bool field;
|
bool field;
|
||||||
|
@ -44,6 +44,9 @@ struct PPU : Thread, IO {
|
||||||
} s;
|
} s;
|
||||||
|
|
||||||
struct Latches {
|
struct Latches {
|
||||||
|
//frame(), power()
|
||||||
|
bool orientation;
|
||||||
|
|
||||||
//latchRegisters()
|
//latchRegisters()
|
||||||
uint8 backColor;
|
uint8 backColor;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ auto PPU::serialize(serializer& s) -> void {
|
||||||
s.integer((uint&)this->s.pixel.source);
|
s.integer((uint&)this->s.pixel.source);
|
||||||
s.integer(this->s.pixel.color);
|
s.integer(this->s.pixel.color);
|
||||||
|
|
||||||
|
s.integer(l.orientation);
|
||||||
s.integer(l.backColor);
|
s.integer(l.backColor);
|
||||||
s.integer(l.screenOneEnable);
|
s.integer(l.screenOneEnable);
|
||||||
s.integer(l.screenOneMapBase);
|
s.integer(l.screenOneMapBase);
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace WonderSwan {
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||||
this->frequency = frequency;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,10 +65,10 @@ struct AudioALSA : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
if(!device.handle) return;
|
if(!device.handle) return;
|
||||||
|
|
||||||
buffer.data[buffer.length++] = left + (right << 16);
|
buffer.data[buffer.length++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if(buffer.length < device.period_size) return;
|
if(buffer.length < device.period_size) return;
|
||||||
|
|
||||||
snd_pcm_sframes_t avail;
|
snd_pcm_sframes_t avail;
|
||||||
|
|
|
@ -31,8 +31,8 @@ struct AudioAO : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t l_sample, uint16_t r_sample) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
uint32_t samp = (l_sample << 0) + (r_sample << 16);
|
uint32_t samp = (uint16_t)left << 0 | (uint16_t)right << 0;
|
||||||
ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian
|
ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,8 @@ struct AudioDS : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
device.buffer[device.bufferoffset++] = left + (right << 16);
|
device.buffer[device.bufferoffset++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if(device.bufferoffset < device.latency) return;
|
if(device.bufferoffset < device.latency) return;
|
||||||
device.bufferoffset = 0;
|
device.bufferoffset = 0;
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,8 @@ struct AudioOpenAL : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
buffer.data[buffer.length++] = left << 0 | right << 16;
|
buffer.data[buffer.length++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if(buffer.length < buffer.size) return;
|
if(buffer.length < buffer.size) return;
|
||||||
|
|
||||||
ALuint albuffer = 0;
|
ALuint albuffer = 0;
|
||||||
|
|
|
@ -69,8 +69,8 @@ struct AudioOSS : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
uint32_t sample = left << 0 | right << 16;
|
uint32_t sample = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
auto unused = write(device.fd, &sample, 4);
|
auto unused = write(device.fd, &sample, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,9 +64,9 @@ struct AudioPulseAudio : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size);
|
pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size);
|
||||||
buffer.data[buffer.offset++] = left + (right << 16);
|
buffer.data[buffer.offset++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return;
|
if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
|
@ -38,10 +38,10 @@ struct AudioPulseAudioSimple : Audio {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
if(!device.handle) return;
|
if(!device.handle) return;
|
||||||
|
|
||||||
buffer.data[buffer.offset++] = left + (right << 16);
|
buffer.data[buffer.offset++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if(buffer.offset >= 64) {
|
if(buffer.offset >= 64) {
|
||||||
int error;
|
int error;
|
||||||
pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error);
|
pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error);
|
||||||
|
|
|
@ -5,19 +5,25 @@
|
||||||
#include <devicetopology.h>
|
#include <devicetopology.h>
|
||||||
#include <endpointvolume.h>
|
#include <endpointvolume.h>
|
||||||
|
|
||||||
#include <nall/dsp.hpp>
|
|
||||||
|
|
||||||
struct AudioWASAPI : Audio {
|
struct AudioWASAPI : Audio {
|
||||||
~AudioWASAPI() { term(); }
|
~AudioWASAPI() { term(); }
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool exclusive = false;
|
bool exclusive = false;
|
||||||
bool synchronize = false;
|
uint latency = 80;
|
||||||
uint frequency = 48000;
|
bool synchronize = true;
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint channels = 0;
|
||||||
|
uint frequency = 0;
|
||||||
|
uint mode = 0;
|
||||||
|
uint precision = 0;
|
||||||
|
} device;
|
||||||
|
|
||||||
auto cap(const string& name) -> bool {
|
auto cap(const string& name) -> bool {
|
||||||
if(name == Audio::Exclusive) return true;
|
if(name == Audio::Exclusive) return true;
|
||||||
|
if(name == Audio::Latency) return true;
|
||||||
if(name == Audio::Synchronize) return true;
|
if(name == Audio::Synchronize) return true;
|
||||||
if(name == Audio::Frequency) return true;
|
if(name == Audio::Frequency) return true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -25,145 +31,132 @@ struct AudioWASAPI : Audio {
|
||||||
|
|
||||||
auto get(const string& name) -> any {
|
auto get(const string& name) -> any {
|
||||||
if(name == Audio::Exclusive) return settings.exclusive;
|
if(name == Audio::Exclusive) return settings.exclusive;
|
||||||
|
if(name == Audio::Latency) return settings.latency;
|
||||||
if(name == Audio::Synchronize) return settings.synchronize;
|
if(name == Audio::Synchronize) return settings.synchronize;
|
||||||
if(name == Audio::Frequency) return settings.frequency;
|
if(name == Audio::Frequency) return device.frequency;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto set(const string& name, const any& value) -> bool {
|
auto set(const string& name, const any& value) -> bool {
|
||||||
if(name == Audio::Exclusive && value.get<bool>()) {
|
if(name == Audio::Exclusive && value.get<bool>()) {
|
||||||
|
if(audioDevice) term(), init();
|
||||||
settings.exclusive = value.get<bool>();
|
settings.exclusive = value.get<bool>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(name == Audio::Latency && value.get<uint>()) {
|
||||||
|
if(audioDevice) term(), init();
|
||||||
|
settings.latency = value.get<uint>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if(name == Audio::Synchronize && value.is<bool>()) {
|
if(name == Audio::Synchronize && value.is<bool>()) {
|
||||||
settings.synchronize = value.get<bool>();
|
settings.synchronize = value.get<bool>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(name == Audio::Frequency && value.is<uint>()) {
|
|
||||||
settings.frequency = value.get<uint>();
|
|
||||||
dsp.setFrequency(settings.frequency);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16 left, uint16 right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
int samples[] = {(int16)left, (int16)right};
|
queuedFrames.append((uint16_t)left << 0 | (uint16_t)right << 16);
|
||||||
dsp.sample(samples);
|
|
||||||
while(dsp.pending()) {
|
if(!available() && queuedFrames.size() >= bufferSize) {
|
||||||
dsp.read(samples);
|
if(settings.synchronize) while(!available()); //wait for free sample slot
|
||||||
write(samples);
|
else queuedFrames.takeFirst(); //drop sample (run ahead)
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cachedFrame = 0;
|
||||||
|
for(auto n : range(available())) {
|
||||||
|
if(queuedFrames) cachedFrame = queuedFrames.takeFirst();
|
||||||
|
write(cachedFrame >> 0, cachedFrame >> 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto clear() -> void {
|
auto clear() -> void {
|
||||||
audioClient->Stop();
|
audioClient->Stop();
|
||||||
renderClient->GetBuffer(bufferFrameCount, &bufferData);
|
audioClient->Reset();
|
||||||
|
for(auto n : range(available())) write(0, 0);
|
||||||
renderClient->ReleaseBuffer(bufferFrameCount, 0);
|
|
||||||
audioClient->Start();
|
audioClient->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto init() -> bool {
|
auto init() -> bool {
|
||||||
if(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator) != S_OK) return false;
|
if(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator) != S_OK) return false;
|
||||||
if(enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device) != S_OK) return false;
|
if(enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &audioDevice) != S_OK) return false;
|
||||||
if(device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&audioClient) != S_OK) return false;
|
if(audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&audioClient) != S_OK) return false;
|
||||||
|
|
||||||
if(settings.exclusive) {
|
if(settings.exclusive) {
|
||||||
if(device->OpenPropertyStore(STGM_READ, &propertyStore) != S_OK) return false;
|
if(audioDevice->OpenPropertyStore(STGM_READ, &propertyStore) != S_OK) return false;
|
||||||
if(propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &propVariant) != S_OK) return false;
|
if(propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &propVariant) != S_OK) return false;
|
||||||
waveFormat = (WAVEFORMATEX*)propVariant.blob.pBlobData;
|
waveFormat = (WAVEFORMATEX*)propVariant.blob.pBlobData;
|
||||||
if(audioClient->GetDevicePeriod(nullptr, &devicePeriod) != S_OK) return false;
|
if(audioClient->GetDevicePeriod(nullptr, &devicePeriod) != S_OK) return false;
|
||||||
if(audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, 0, devicePeriod, devicePeriod, waveFormat, nullptr) != S_OK) return false;
|
auto latency = max(devicePeriod, (REFERENCE_TIME)settings.latency * 10'000); //1ms to 100ns units
|
||||||
|
if(audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, 0, latency, latency, waveFormat, nullptr) != S_OK) return false;
|
||||||
|
DWORD taskIndex = 0;
|
||||||
taskHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskIndex);
|
taskHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskIndex);
|
||||||
} else {
|
} else {
|
||||||
if(audioClient->GetMixFormat(&waveFormat) != S_OK) return false;
|
if(audioClient->GetMixFormat(&waveFormat) != S_OK) return false;
|
||||||
if(audioClient->GetDevicePeriod(&devicePeriod, nullptr)) return false;
|
if(audioClient->GetDevicePeriod(&devicePeriod, nullptr)) return false;
|
||||||
if(audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, devicePeriod, 0, waveFormat, nullptr) != S_OK) return false;
|
auto latency = max(devicePeriod, (REFERENCE_TIME)settings.latency * 10'000); //1ms to 100ns units
|
||||||
|
if(audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, latency, 0, waveFormat, nullptr) != S_OK) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(audioClient->GetService(IID_IAudioRenderClient, (void**)&renderClient) != S_OK) return false;
|
if(audioClient->GetService(IID_IAudioRenderClient, (void**)&renderClient) != S_OK) return false;
|
||||||
if(audioClient->GetBufferSize(&bufferFrameCount) != S_OK) return false;
|
if(audioClient->GetBufferSize(&bufferSize) != S_OK) return false;
|
||||||
|
|
||||||
switch(((WAVEFORMATEXTENSIBLE*)waveFormat)->SubFormat.Data1) {
|
device.channels = waveFormat->nChannels;
|
||||||
case 1: ieee = false; break; //fixed point
|
device.frequency = waveFormat->nSamplesPerSec;
|
||||||
case 3: ieee = true; break; //floating point
|
device.mode = ((WAVEFORMATEXTENSIBLE*)waveFormat)->SubFormat.Data1;
|
||||||
default: return false; //unknown format; abort
|
device.precision = waveFormat->wBitsPerSample;
|
||||||
}
|
|
||||||
|
|
||||||
dsp.setChannels(2);
|
|
||||||
dsp.setPrecision(16);
|
|
||||||
dsp.setFrequency(settings.frequency);
|
|
||||||
|
|
||||||
dsp.setResampler(DSP::ResampleEngine::Linear);
|
|
||||||
dsp.setResamplerFrequency(waveFormat->nSamplesPerSec);
|
|
||||||
dsp.setChannels(waveFormat->nChannels);
|
|
||||||
dsp.setPrecision(waveFormat->wBitsPerSample);
|
|
||||||
|
|
||||||
print("[WASAPI]\n");
|
|
||||||
print("Channels: ", waveFormat->nChannels, "\n");
|
|
||||||
print("Precision: ", waveFormat->wBitsPerSample, "\n");
|
|
||||||
print("Frequency: ", waveFormat->nSamplesPerSec, "\n");
|
|
||||||
print("IEEE-754: ", ieee, "\n");
|
|
||||||
print("Exclusive: ", settings.exclusive, "\n\n");
|
|
||||||
|
|
||||||
audioClient->Start();
|
audioClient->Start();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto term() -> void {
|
auto term() -> void {
|
||||||
if(audioClient) {
|
if(audioClient) audioClient->Stop();
|
||||||
audioClient->Stop();
|
if(renderClient) renderClient->Release(), renderClient = nullptr;
|
||||||
}
|
if(waveFormat) CoTaskMemFree(waveFormat), waveFormat = nullptr;
|
||||||
|
if(audioClient) audioClient->Release(), audioClient = nullptr;
|
||||||
if(taskHandle) {
|
if(audioDevice) audioDevice->Release(), audioDevice = nullptr;
|
||||||
AvRevertMmThreadCharacteristics(taskHandle);
|
if(taskHandle) AvRevertMmThreadCharacteristics(taskHandle), taskHandle = nullptr;
|
||||||
taskHandle = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto write(int samples[]) -> void {
|
auto available() -> uint {
|
||||||
while(true) {
|
uint32_t padding = 0;
|
||||||
uint32 padding = 0;
|
audioClient->GetCurrentPadding(&padding);
|
||||||
audioClient->GetCurrentPadding(&padding);
|
return bufferSize - padding;
|
||||||
if(bufferFrameCount - padding < 1) {
|
}
|
||||||
if(!settings.synchronize) return;
|
|
||||||
continue;
|
auto write(int16_t left, int16_t right) -> void {
|
||||||
}
|
if(renderClient->GetBuffer(1, &bufferData) != S_OK) return;
|
||||||
break;
|
|
||||||
|
if(device.channels >= 2 && device.mode == 1 && device.precision == 16) {
|
||||||
|
auto buffer = (int16_t*)bufferData;
|
||||||
|
buffer[0] = left;
|
||||||
|
buffer[1] = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderClient->GetBuffer(1, &bufferData);
|
if(device.channels >= 2 && device.mode == 3 && device.precision == 32) {
|
||||||
|
|
||||||
if(ieee) {
|
|
||||||
auto buffer = (float*)bufferData;
|
auto buffer = (float*)bufferData;
|
||||||
buffer[0] = (int16)samples[0] / 32768.0;
|
buffer[0] = left / 32768.0;
|
||||||
buffer[1] = (int16)samples[1] / 32768.0;
|
buffer[1] = right / 32768.0;
|
||||||
} else {
|
|
||||||
auto buffer = (int16*)bufferData;
|
|
||||||
buffer[0] = (int16)samples[0];
|
|
||||||
buffer[1] = (int16)samples[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderClient->ReleaseBuffer(1, 0);
|
renderClient->ReleaseBuffer(1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DSP dsp;
|
|
||||||
IMMDeviceEnumerator* enumerator = nullptr;
|
IMMDeviceEnumerator* enumerator = nullptr;
|
||||||
IMMDevice* device = nullptr;
|
IMMDevice* audioDevice = nullptr;
|
||||||
IPropertyStore* propertyStore = nullptr;
|
IPropertyStore* propertyStore = nullptr;
|
||||||
IAudioClient* audioClient = nullptr;
|
IAudioClient* audioClient = nullptr;
|
||||||
IAudioRenderClient* renderClient = nullptr;
|
IAudioRenderClient* renderClient = nullptr;
|
||||||
WAVEFORMATEX* waveFormat = nullptr;
|
WAVEFORMATEX* waveFormat = nullptr;
|
||||||
PROPVARIANT propVariant;
|
PROPVARIANT propVariant;
|
||||||
HANDLE taskHandle = nullptr;
|
HANDLE taskHandle = nullptr;
|
||||||
DWORD taskIndex = 0;
|
|
||||||
REFERENCE_TIME devicePeriod = 0;
|
REFERENCE_TIME devicePeriod = 0;
|
||||||
uint32 bufferFrameCount = 0;
|
uint32_t bufferSize = 0; //in frames
|
||||||
uint8* bufferData = nullptr;
|
uint8_t* bufferData = nullptr;
|
||||||
bool ieee = false;
|
vector<uint32_t> queuedFrames;
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,8 +78,8 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback {
|
||||||
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
|
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sample(uint16_t left, uint16_t right) -> void {
|
auto sample(int16_t left, int16_t right) -> void {
|
||||||
device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = left + (right << 16);
|
device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = (uint16_t)left << 0 | (uint16_t)right << 16;
|
||||||
if(device.bufferoffset < device.latency) return;
|
if(device.bufferoffset < device.latency) return;
|
||||||
device.bufferoffset = 0;
|
device.bufferoffset = 0;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
/*
|
#pragma once
|
||||||
xaudio2.hpp (2010-08-14)
|
|
||||||
author: OV2
|
|
||||||
|
|
||||||
ruby-specific header to provide mingw-friendly xaudio2 interfaces
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XAUDIO2_RUBY_H
|
//ruby-specific header to provide mingw-friendly xaudio2 interfaces
|
||||||
#define XAUDIO2_RUBY_H
|
|
||||||
|
|
||||||
//64-bit GCC fix
|
//64-bit GCC fix
|
||||||
#define GUID_EXT EXTERN_C
|
#define GUID_EXT EXTERN_C
|
||||||
|
@ -34,20 +28,6 @@ DECLARE_INTERFACE(IXAudio2Voice);
|
||||||
#define XAUDIO2_DEBUG_ENGINE 0x0001
|
#define XAUDIO2_DEBUG_ENGINE 0x0001
|
||||||
#define XAUDIO2_VOICE_NOSRC 0x0004
|
#define XAUDIO2_VOICE_NOSRC 0x0004
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
WAVEFORMATEX Format;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
WORD wValidBitsPerSample;
|
|
||||||
WORD wSamplesPerBlock;
|
|
||||||
WORD wReserved;
|
|
||||||
} Samples;
|
|
||||||
DWORD dwChannelMask;
|
|
||||||
GUID SubFormat;
|
|
||||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
|
|
||||||
typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
|
|
||||||
|
|
||||||
typedef enum XAUDIO2_DEVICE_ROLE
|
typedef enum XAUDIO2_DEVICE_ROLE
|
||||||
{
|
{
|
||||||
NotDefaultDevice = 0x0,
|
NotDefaultDevice = 0x0,
|
||||||
|
@ -337,4 +317,3 @@ __inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||||
}
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
/* ruby
|
/* ruby
|
||||||
* author: byuu
|
* author: byuu
|
||||||
* license: ISC
|
* license: ISC
|
||||||
* version: 0.14 (2015-11-19)
|
* version: 0.15 (2016-04-18)
|
||||||
*
|
*
|
||||||
* ruby is a cross-platform hardware abstraction layer
|
* ruby is a cross-platform hardware abstraction layer.
|
||||||
* it provides a common interface to video, audio and input devices
|
* it provides a common interface to video, audio and input devices.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <nall/nall.hpp>
|
#include <nall/nall.hpp>
|
||||||
|
@ -62,7 +62,7 @@ struct Audio {
|
||||||
virtual auto get(const nall::string& name) -> nall::any { return false; }
|
virtual auto get(const nall::string& name) -> nall::any { return false; }
|
||||||
virtual auto set(const nall::string& name, const nall::any& value) -> bool { return false; }
|
virtual auto set(const nall::string& name, const nall::any& value) -> bool { return false; }
|
||||||
|
|
||||||
virtual auto sample(uint16_t left, uint16_t right) -> void {}
|
virtual auto sample(int16_t left, int16_t right) -> void {}
|
||||||
virtual auto clear() -> void {}
|
virtual auto clear() -> void {}
|
||||||
|
|
||||||
virtual auto init() -> bool { return true; }
|
virtual auto init() -> bool { return true; }
|
||||||
|
|
Loading…
Reference in New Issue