Update to v106r50 release.

byuu says:

Changelog:

  - emulator/video,audio: various cleanups
  - emulator/audio: removed reverb effect (it breaks very badly on
    high-frequency systems)
  - emulator/audio: the Nyquist anti-aliasing lowpass filter is now
    generated automatically instead of set per-core
      - at 44.1KHz output, it's set to 22KHz; at 48KHz, it's set to
        22KHz; at 96KHz, it's set to 25KHz
      - this filter now takes the bsnes emulation speed setting into
        account
  - all system/video.cpp files removed; inlined in System::power() and
    Interface::set() instead
  - sfc/cpu: pre-compute `HTIME` as `HTIME+1<<2` for faster comparisons of
    HIRQs
  - sfc/cpu: re-add check to block IRQs on the last dot of each frame
    (minor speed hit)
  - hiro/gtk3: fixed headers for Linux compilation finally
  - hiro/gtk,qt: fixed settings.cpp logic so initial values are used
    when no settings.bml file exists
  - hiro/gtk: started a minor experiment to specify theming information
    in settings.bml files
  - nall/dsp: allow the precision type (double) to be overridden (to
    float)
  - nall: add some helpers for generating pre-compiled headers
      - it was a failure to try using them for higan, however ...
  - nall: add some helpers for reading fallback values from empty
    `Markup::Node[search]` statements

Todo:

  - CRITICAL: a lot of my IRQ/NMI/HDMA timing tests are failing with the
    fast PPU ... need to figure out why
  - space between Emulator::video functions and Emulator::audio
    functions in gb/system/system.cpp
  - remove Audio/Reverb/Enable from settings.bml in target-bsnes
This commit is contained in:
Tim Allen 2018-07-21 21:06:40 +10:00
parent 65a3e6c676
commit 35ff15f83e
60 changed files with 218 additions and 267 deletions

View File

@ -47,5 +47,5 @@ include $(ui)/GNUmakefile
-include obj/*.d
clean:
$(call delete,out/*)
$(call delete,obj/*)
$(call delete,out/*)

View File

@ -5,30 +5,14 @@ namespace Emulator {
#include "stream.cpp"
Audio audio;
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
interface = nullptr;
if(channels_) channels = channels_();
if(frequency_) frequency = frequency_();
streams.reset();
reverb.reset();
reverb.resize(channels);
for(auto c : range(channels)) {
reverb[c].resize(7);
reverb[c][0].resize(1229);
reverb[c][1].resize(1559);
reverb[c][2].resize(1907);
reverb[c][3].resize(4057);
reverb[c][4].resize(8117);
reverb[c][5].resize(8311);
reverb[c][6].resize(9931);
}
Audio::~Audio() {
reset(nullptr);
}
auto Audio::setInterface(Interface* interface) -> void {
auto Audio::reset(Interface* interface) -> void {
this->interface = interface;
streams.reset();
channels = 0;
}
auto Audio::setFrequency(double frequency) -> void {
@ -46,11 +30,8 @@ auto Audio::setBalance(double balance) -> void {
this->balance = balance;
}
auto Audio::setReverb(bool enabled) -> void {
this->reverbEnable = enabled;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
this->channels = max(this->channels, channels);
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, this->frequency);
streams.append(stream);
@ -67,7 +48,7 @@ auto Audio::process() -> void {
for(auto& sample : samples) sample = 0.0;
for(auto& stream : streams) {
double buffer[16];
double buffer[channels];
uint length = stream->read(buffer), offset = 0;
for(auto& sample : samples) {
@ -78,13 +59,6 @@ auto Audio::process() -> void {
for(auto c : range(channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
if(reverbEnable) {
samples[c] *= 0.125;
for(auto n : range(7)) samples[c] += 0.125 * reverb[c][n].last();
for(auto n : range(7)) reverb[c][n].write(samples[c]);
samples[c] *= 8.000;
}
}
if(channels == 2) {

View File

@ -12,13 +12,12 @@ struct Filter;
struct Stream;
struct Audio {
auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void;
auto setInterface(Interface* interface) -> void;
~Audio();
auto reset(Interface* interface) -> void;
auto setFrequency(double frequency) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
auto setReverb(bool enabled) -> void;
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
@ -29,14 +28,11 @@ private:
vector<shared_pointer<Stream>> streams;
uint channels = 0;
double frequency = 0.0;
double frequency = 48000.0;
double volume = 1.0;
double balance = 0.0;
bool reverbEnable = false;
vector<vector<queue<double>>> reverb;
friend class Stream;
};
@ -68,6 +64,7 @@ struct Stream {
private:
struct Channel {
vector<Filter> filters;
vector<DSP::IIR::Biquad> nyquist;
DSP::Resampler::Cubic resampler;
};
vector<Channel> channels;

View File

@ -16,13 +16,28 @@ auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency)
if(outputFrequency) this->outputFrequency = outputFrequency();
for(auto& channel : channels) {
channel.nyquist.reset();
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
if(this->inputFrequency >= this->outputFrequency * 2) {
//add a low-pass filter to prevent aliasing during resampling
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
for(auto& channel : channels) {
uint passes = 3;
for(uint pass : range(passes)) {
DSP::IIR::Biquad filter;
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
channel.nyquist.append(filter);
}
}
}
}
auto Stream::addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes) -> void {
for(auto& channel : channels) {
for(auto pass : range(passes)) {
for(uint pass : range(passes)) {
Filter filter{order};
if(order == Filter::Order::First) {
@ -63,6 +78,9 @@ auto Stream::write(const double samples[]) -> void {
case Filter::Order::Second: sample = filter.biquad.process(sample); break;
}
}
for(auto& filter : channels[c].nyquist) {
sample = filter.process(sample);
}
channels[c].resampler.write(sample);
}

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.49";
static const string Version = "106.50";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -77,7 +77,6 @@ auto APU::power(bool reset) -> void {
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 90.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 440.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 14000.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
pulse[0].power();
pulse[1].power();

View File

@ -177,7 +177,7 @@ auto Interface::get(const string& name) -> any {
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
Emulator::video.setPalette();
return true;
}
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;

View File

@ -2,7 +2,6 @@
namespace Famicom {
#include "video.cpp"
#include "serialization.cpp"
System system;
Scheduler scheduler;
@ -63,13 +62,10 @@ auto System::unload() -> void {
}
auto System::power(bool reset) -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
cartridge.power();

View File

@ -16,10 +16,6 @@ struct System {
auto init() -> void;
auto term() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@ -1,6 +0,0 @@
auto System::configureVideoPalette() -> void {
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
}

View File

@ -56,7 +56,6 @@ auto APU::power() -> void {
if(!Model::SuperGameBoy()) {
stream = Emulator::audio.createStream(2, frequency());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
}
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;

View File

@ -100,13 +100,15 @@ auto Interface::get(const string& name) -> any {
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
system.configureVideoEffects();
if(Model::SuperGameBoy()) return true;
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
if(Model::SuperGameBoy()) return true;
Emulator::video.setPalette();
return true;
}

View File

@ -2,7 +2,6 @@
namespace GameBoy {
#include "video.cpp"
#include "serialization.cpp"
System system;
Scheduler scheduler;
@ -83,13 +82,10 @@ auto System::unload() -> void {
auto System::power() -> void {
if(model() != Model::SuperGameBoy) {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::audio.reset(interface);
}
scheduler.reset();

View File

@ -1,9 +0,0 @@
auto System::configureVideoPalette() -> void {
if(model() == Model::SuperGameBoy) return;
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
if(model() == Model::SuperGameBoy) return;
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
}

View File

@ -78,7 +78,6 @@ auto APU::power() -> void {
create(APU::Enter, system.frequency());
stream = Emulator::audio.createStream(2, frequency() / 64.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
clock = 0;
square1.power();

View File

@ -131,19 +131,19 @@ auto Interface::get(const string& name) -> any {
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
system.configureVideoEffects();
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
Emulator::video.setPalette();
return true;
}
if(name == "Rotate Display" && value.is<bool>()) {
settings.rotateLeft = value.get<bool>();
system.configureVideoEffects();
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
return true;
}

View File

@ -5,7 +5,6 @@ namespace GameBoyAdvance {
System system;
Scheduler scheduler;
#include "bios.cpp"
#include "video.cpp"
#include "serialization.cpp"
auto System::init() -> void {
@ -15,13 +14,12 @@ auto System::term() -> void {
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
bus.power();

View File

@ -27,10 +27,6 @@ struct System {
auto run() -> void;
auto runToSave() -> void;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@ -1,8 +0,0 @@
auto System::configureVideoPalette() -> void {
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
}

View File

@ -39,7 +39,6 @@ auto PSG::power(bool reset) -> void {
stream = Emulator::audio.createStream(1, frequency() / 16.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 2840.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
select = 0;
for(auto n : range(15)) {

View File

@ -62,12 +62,10 @@ auto System::unload() -> void {
}
auto System::power(bool reset) -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
cartridge.power();

View File

@ -159,7 +159,6 @@ auto YM2612::power(bool reset) -> void {
stream = Emulator::audio.createStream(2, frequency() / 144.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 2840.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
io = {};
lfo = {};

View File

@ -45,7 +45,6 @@ auto PSG::power() -> void {
create(PSG::Enter, system.colorburst() / 16.0);
stream = Emulator::audio.createStream(2, frequency());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
select = 0;
for(auto n : range(15)) {

View File

@ -59,12 +59,10 @@ auto System::unload() -> void {
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
cartridge.power();

View File

@ -53,7 +53,6 @@ auto PSG::power() -> void {
create(PSG::Enter, system.colorburst());
stream = Emulator::audio.createStream(2, frequency());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
io = {};
for(auto C : range(6)) channel[C].power(C);

View File

@ -49,12 +49,10 @@ auto System::unload() -> void {
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
cartridge.power();

View File

@ -47,7 +47,6 @@ auto ICD::power() -> void {
create(ICD::Enter, (Frequency ? Frequency : system.cpuFrequency()) / 5.0);
stream = Emulator::audio.createStream(2, frequency() / 2.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
r6003 = 0x00;
r6004 = 0xff;
@ -91,7 +90,7 @@ auto ICD::reset() -> void {
writeAddress = 0;
packetSize = 0;
joypID = 0;
joypID = 3;
joyp15Lock = 0;
joyp14Lock = 0;
pulseLock = true;

View File

@ -139,8 +139,8 @@ private:
uint8 wrdivb = 0xff;
//$4207-$420a
uint9 hirqPos = 0x1ff;
uint9 virqPos = 0x1ff;
uint12 htime = 0x1ff + 1 << 2;
uint9 vtime = 0x1ff;
//$420d
uint romSpeed = 8;

View File

@ -174,19 +174,23 @@ auto CPU::writeCPU(uint24 addr, uint8 data) -> void {
return;
case 0x4207: //HTIMEL
io.hirqPos.bits(0,7) = data;
io.htime = (io.htime >> 2) - 1;
io.htime.bits(0,7) = data;
io.htime = (io.htime + 1) << 2;
return;
case 0x4208: //HTIMEH
io.hirqPos.bit(8) = data.bit(0);
io.htime = (io.htime >> 2) - 1;
io.htime.bit(8) = data.bit(0);
io.htime = (io.htime + 1) << 2;
return;
case 0x4209: //VTIMEL
io.virqPos.bits(0,7) = data;
io.vtime.bits(0,7) = data;
return;
case 0x420a: //VTIMEH
io.virqPos.bit(8) = data.bit(0);
io.vtime.bit(8) = data.bit(0);
return;
case 0x420b: //DMAEN

View File

@ -18,8 +18,9 @@ auto CPU::pollInterrupts() -> void {
//IRQ test
if(status.irqValid.raise(io.irqEnable
&& (!io.virqEnable || vcounter(10) == io.virqPos)
&& (!io.hirqEnable || hcounter(10) == io.hirqPos + 1 << 2)
&& (!io.virqEnable || vcounter(10) == io.vtime)
&& (!io.hirqEnable || hcounter(10) == io.htime)
&& (vcounter(6) || hcounter(6)) //IRQs cannot trigger on last dot of fields
)) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles
}

View File

@ -65,8 +65,8 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(io.wrdiva);
s.integer(io.wrdivb);
s.integer(io.hirqPos);
s.integer(io.virqPos);
s.integer(io.htime);
s.integer(io.vtime);
s.integer(io.romSpeed);

View File

@ -91,12 +91,10 @@ auto System::power(bool reset) -> void {
hacks.fastPPU = settings.fastPPU;
hacks.fastDSP = settings.fastDSP;
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
random.entropy(Random::Entropy::Low);

View File

@ -72,7 +72,4 @@ auto Program::updateAudioEffects() -> void {
double balance = max(-1.0, min(+1.0, (settings["Audio/Balance"].integer() - 50) / 50.0));
Emulator::audio.setBalance(balance);
bool reverb = settings["Audio/Reverb"].boolean();
Emulator::audio.setReverb(reverb);
}

View File

@ -4,9 +4,7 @@ auto Program::load() -> void {
for(auto& media : emulator->media) {
if(media.type != "sfc") continue;
Emulator::audio.reset(2, audio->frequency());
if(emulator->load(media.id)) {
gameQueue = {};
screenshot = {};
frameAdvance = false;
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
@ -56,6 +54,8 @@ auto Program::load() -> void {
break;
}
gameQueue = {};
}
auto Program::loadFile(string location) -> vector<uint8_t> {

View File

@ -52,12 +52,6 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
balanceValue.setText(value);
program->updateAudioEffects();
}).doChange();
reverb.setText("Reverb").setChecked(settings["Audio/Reverb"].boolean()).onToggle([&] {
settings["Audio/Reverb"].setValue(reverb.checked());
program->updateAudioEffects();
});
//todo: does not work properly with Super Game Boy
reverb.setVisible(false);
}
auto AudioSettings::updateDevice() -> void {

View File

@ -55,7 +55,6 @@ public:
Label balanceLabel{&effectsLayout, Size{0, 0}};
Label balanceValue{&effectsLayout, Size{50, 0}};
HorizontalSlider balanceSlider{&effectsLayout, Size{~0, 0}};
CheckLabel reverb{&layout, Size{~0, 0}};
};
struct InputSettings : TabFrameItem {

View File

@ -19,17 +19,16 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa
mediumPaths.append(locate({"systems/", medium.name, ".sys/"}));
Emulator::audio.reset(2, audio->frequency());
inputManager->bind(emulator = &interface);
if(!emulator->load(medium.id)) {
emulator = nullptr;
mediumPaths.reset();
return;
}
updateAudioDriver();
updateAudioEffects();
connectDevices();
emulator->power();
updateAudioDriver();
updateAudioEffects();
presentation->resizeViewport();
presentation->setTitle(emulator->title());

View File

@ -171,9 +171,6 @@ auto Program::updateAudioEffects() -> void {
auto balance = max(-1.0, min(1.0, (settings["Audio/Balance"].integer() - 50) / 50.0));
Emulator::audio.setBalance(balance);
auto reverbEnable = settings["Audio/Reverb/Enable"].boolean();
Emulator::audio.setReverb(reverbEnable);
}
auto Program::focused() -> bool {

View File

@ -48,8 +48,6 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
balanceValue.setAlignment(0.5);
balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] { updateEffects(); });
reverbEnable.setText("Reverb").setChecked(settings["Audio/Reverb/Enable"].boolean()).onToggle([&] { updateEffects(); });
updateDevice();
updateEffects(true);
}
@ -79,7 +77,5 @@ auto AudioSettings::updateEffects(bool initializing) -> void {
settings["Audio/Balance"].setValue(balanceSlider.position());
balanceValue.setText({balanceSlider.position(), "%"});
settings["Audio/Reverb/Enable"].setValue(reverbEnable.checked());
if(!initializing) program->updateAudioEffects();
}

View File

@ -58,7 +58,6 @@ Settings::Settings() {
set("Audio/Mute", false);
set("Audio/Volume", 100);
set("Audio/Balance", 50);
set("Audio/Reverb/Enable", false);
set("Input/Driver", ruby::Input::safestDriver());
set("Input/Frequency", 5);

View File

@ -109,7 +109,6 @@ struct AudioSettings : TabFrameItem {
Label balanceLabel{&balanceLayout, Size{80, 0}};
Label balanceValue{&balanceLayout, Size{50, 0}};
HorizontalSlider balanceSlider{&balanceLayout, Size{~0, 0}};
CheckLabel reverbEnable{&layout, Size{~0, 0}};
auto updateDevice() -> void;
auto updateEffects(bool initializing = false) -> void;

View File

@ -6,11 +6,12 @@ namespace Emulator {
Video video;
Video::~Video() {
reset();
reset(nullptr);
}
auto Video::reset() -> void {
interface = nullptr;
auto Video::reset(Interface* interface) -> void {
this->interface = interface;
sprites.reset();
delete buffer;
buffer = nullptr;
@ -25,10 +26,6 @@ auto Video::reset() -> void {
effects.rotateLeft = false;
}
auto Video::setInterface(Interface* interface) -> void {
this->interface = interface;
}
auto Video::setPalette() -> void {
if(!interface) return;

View File

@ -14,9 +14,7 @@ struct Video {
};
~Video();
auto reset() -> void;
auto setInterface(Interface* interface) -> void;
auto reset(Interface* interface) -> void;
auto setPalette() -> void;
auto setSaturation(double saturation) -> void;

View File

@ -68,7 +68,6 @@ auto APU::power() -> void {
create(APU::Enter, 3'072'000);
stream = Emulator::audio.createStream(2, frequency());
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
bus.map(this, 0x004a, 0x004c);
bus.map(this, 0x004e, 0x0050);

View File

@ -106,19 +106,19 @@ auto Interface::get(const string& name) -> any {
auto Interface::set(const string& name, const any& value) -> bool {
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
system.configureVideoEffects();
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
system.configureVideoPalette();
Emulator::video.setPalette();
return true;
}
if(name == "Rotate Display" && value.is<bool>()) {
settings.rotateLeft = value.get<bool>();
system.configureVideoEffects();
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
return true;
}

View File

@ -6,7 +6,6 @@ System system;
Scheduler scheduler;
Cheat cheat;
#include "io.cpp"
#include "video.cpp"
#include "serialization.cpp"
auto System::init() -> void {
@ -61,13 +60,12 @@ auto System::unload() -> void {
}
auto System::power() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
configureVideoPalette();
configureVideoEffects();
Emulator::video.reset(interface);
Emulator::video.setPalette();
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
Emulator::audio.reset(interface);
scheduler.reset();
bus.power();

View File

@ -22,10 +22,6 @@ struct System : IO {
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
//video.cpp
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;
//serialization.cpp
auto serializeInit() -> void;
auto serialize() -> serializer;

View File

@ -1,8 +0,0 @@
auto System::configureVideoPalette() -> void {
Emulator::video.setPalette();
}
auto System::configureVideoEffects() -> void {
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
Emulator::video.setEffect(Emulator::Video::Effect::RotateLeft, settings.rotateLeft);
}

View File

@ -43,9 +43,13 @@
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#if defined(Hiro_SourceEdit)
#include <gtksourceview/gtksourceview.h>
#include <gtksourceview/gtksourcelanguagemanager.h>
#include <gtksourceview/gtksourcestyleschememanager.h>
#if HIRO_GTK==2
#include <gtksourceview/gtksourceview.h>
#include <gtksourceview/gtksourcelanguagemanager.h>
#include <gtksourceview/gtksourcestyleschememanager.h>
#elif HIRO_GTK==3
#include <gtksourceview/gtksource.h>
#endif
#endif
#include <nall/xorg/guard.hpp>
#endif

View File

@ -8,16 +8,20 @@ Settings::Settings() {
auto document = BML::unserialize(file::read({path, "gtk3.bml"}));
#endif
auto get = [&](string_view name) {
return document[name];
};
#define get(name, type, value) \
if(auto node = document[name]) value = node.type()
geometry.frameX = get("Geometry/FrameX").integer();
geometry.frameY = get("Geometry/FrameY").integer();
geometry.frameWidth = get("Geometry/FrameWidth").integer();
geometry.frameHeight = get("Geometry/FrameHeight").integer();
geometry.menuHeight = get("Geometry/MenuHeight").integer();
geometry.statusHeight = get("Geometry/StatusHeight").integer();
get("Geometry/FrameX", integer, geometry.frameX);
get("Geometry/FrameY", integer, geometry.frameY);
get("Geometry/FrameWidth", integer, geometry.frameWidth);
get("Geometry/FrameHeight", integer, geometry.frameHeight);
get("Geometry/MenuHeight", integer, geometry.menuHeight);
get("Geometry/StatusHeight", integer, geometry.statusHeight);
get("Theme/ActionIcons", boolean, theme.actionIcons);
get("Theme/WidgetColors", boolean, theme.widgetColors);
#undef get
}
Settings::~Settings() {
@ -25,9 +29,9 @@ Settings::~Settings() {
directory::create(path, 0755);
Markup::Node document;
auto set = [&](string_view name, string_view value) {
document(name).setValue(value);
};
#define set(name, value) \
document(name).setValue(value)
set("Geometry/FrameX", geometry.frameX);
set("Geometry/FrameY", geometry.frameY);
@ -36,6 +40,11 @@ Settings::~Settings() {
set("Geometry/MenuHeight", geometry.menuHeight);
set("Geometry/StatusHeight", geometry.statusHeight);
set("Theme/ActionIcons", theme.actionIcons);
set("Theme/WidgetColors", theme.widgetColors);
#undef set
#if HIRO_GTK==2
file::write({path, "gtk2.bml"}, BML::serialize(document));
#elif HIRO_GTK==3

View File

@ -14,6 +14,11 @@ struct Settings {
int menuHeight = 8;
int statusHeight = 4;
} geometry;
struct Theme {
bool actionIcons = true;
bool widgetColors = true;
} theme;
};
static Settings settings;

View File

@ -257,14 +257,14 @@ auto pTableView::_doDataFunc(GtkTreeViewColumn* gtkColumn, GtkCellRenderer* rend
pango_font_description_free(font);
if(auto color = cell->foregroundColor(true)) {
auto gdkColor = CreateColor(color);
g_object_set(G_OBJECT(renderer), "foreground-gdk", &gdkColor, nullptr);
if(settings.theme.widgetColors) g_object_set(G_OBJECT(renderer), "foreground-gdk", &gdkColor, nullptr);
} else {
g_object_set(G_OBJECT(renderer), "foreground-set", false, nullptr);
}
}
if(auto color = cell->backgroundColor(true)) {
auto gdkColor = CreateColor(color);
g_object_set(G_OBJECT(renderer), "cell-background-gdk", &gdkColor, nullptr);
if(settings.theme.widgetColors) g_object_set(G_OBJECT(renderer), "cell-background-gdk", &gdkColor, nullptr);
} else {
g_object_set(G_OBJECT(renderer), "cell-background-set", false, nullptr);
}

View File

@ -8,16 +8,17 @@ Settings::Settings() {
auto document = BML::unserialize(file::read({path, "qt5.bml"}));
#endif
auto get = [&](string_view name) {
return document[name];
};
#define get(name, type, value) \
if(auto node = document[name]) value = node.type()
geometry.frameX = get("Geometry/FrameX").integer();
geometry.frameY = get("Geometry/FrameY").integer();
geometry.frameWidth = get("Geometry/FrameWidth").integer();
geometry.frameHeight = get("Geometry/FrameHeight").integer();
geometry.menuHeight = get("Geometry/MenuHeight").integer();
geometry.statusHeight = get("Geometry/StatusHeight").integer();
get("Geometry/FrameX", integer, geometry.frameX);
get("Geometry/FrameY", integer, geometry.frameY);
get("Geometry/FrameWidth", integer, geometry.frameWidth);
get("Geometry/FrameHeight", integer, geometry.frameHeight);
get("Geometry/MenuHeight", integer, geometry.menuHeight);
get("Geometry/StatusHeight", integer, geometry.statusHeight);
#undef get
}
Settings::~Settings() {
@ -25,9 +26,9 @@ Settings::~Settings() {
directory::create(path, 0755);
Markup::Node document;
auto set = [&](string_view name, string_view value) {
document(name).setValue(value);
};
#define set(name, value) \
document(name).setValue(value)
set("Geometry/FrameX", geometry.frameX);
set("Geometry/FrameY", geometry.frameY);
@ -36,6 +37,8 @@ Settings::~Settings() {
set("Geometry/MenuHeight", geometry.menuHeight);
set("Geometry/StatusHeight", geometry.statusHeight);
#undef set
#if HIRO_QT==4
file::write({path, "qt4.bml"}, BML::serialize(document));
#elif HIRO_QT==5

View File

@ -164,7 +164,6 @@ auto pWindow::setMaximumSize(Size size) -> void {
static auto maximumSize = qtWindow->maximumSize();
if(size) {
//once this is called, no matter what the size is, Qt will no longer allow the window to be maximized
qtWindow->setMaximumSize(size.width(), size.height() + _menuHeight() + _statusHeight());
} else {
qtWindow->setMaximumSize(maximumSize);

View File

@ -40,7 +40,9 @@ ifeq ($(platform),)
endif
flags.c = -x c -std=c11
flags.h = -x c-header -std=c11
flags.cpp = -x c++ -std=c++14
flags.hpp = -x c++-header -std=c++14
flags.objc = -x objective-c -std=c11
flags.objcpp = -x objective-c++ -std=c++14
flags.deps = -MMD -MP -MF $(@:.o=.d)
@ -50,6 +52,7 @@ ifeq ($(compiler),)
ifeq ($(platform),windows)
compiler := g++
flags.cpp := -x c++ -std=gnu++14
flags.hpp := -x c++-header -std=gnu++14
else ifeq ($(platform),macos)
compiler := clang++
else ifeq ($(platform),linux)
@ -133,7 +136,10 @@ endif
# paths
prefix := $(HOME)/.local
object.path := obj
ifeq ($(object.path),)
object.path := obj
endif
# rules
default: all;
@ -155,7 +161,11 @@ compile = \
$(compiler) $(flags.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.cpp,$<), \
$(compiler) $(flags.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
)) \
,$(if $(filter %.h,$<), \
$(compiler) $(flags.h) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.hpp,$<), \
$(compiler) $(flags.hpp) $(flags) $1 -c $< -o $@ \
)))) \
)
# function rwildcard(directory, pattern)

11
nall/dsp/dsp.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
namespace nall { namespace DSP {
#if defined(NALL_DSP_PRECISION)
using real = NALL_DSP_PRECISION;
#else
using real = double;
#endif
}}

View File

@ -1,5 +1,7 @@
#pragma once
#include <nall/dsp/dsp.hpp>
//transposed direct form II biquadratic second-order IIR filter
namespace nall { namespace DSP { namespace IIR {
@ -15,22 +17,22 @@ struct Biquad {
HighShelf,
};
inline auto reset(Type type, double cutoffFrequency, double samplingFrequency, double quality, double gain = 0.0) -> void;
inline auto process(double in) -> double; //normalized sample (-1.0 to +1.0)
inline auto reset(Type type, real cutoffFrequency, real samplingFrequency, real quality, real gain = 0.0) -> void;
inline auto process(real in) -> real; //normalized sample (-1.0 to +1.0)
inline static auto butterworth(uint order, uint phase) -> double;
inline static auto butterworth(uint order, uint phase) -> real;
private:
Type type;
double cutoffFrequency;
double samplingFrequency;
double quality; //frequency response quality
double gain; //peak gain
double a0, a1, a2, b1, b2; //coefficients
double z1, z2; //second-order IIR
real cutoffFrequency;
real samplingFrequency;
real quality; //frequency response quality
real gain; //peak gain
real a0, a1, a2, b1, b2; //coefficients
real z1, z2; //second-order IIR
};
auto Biquad::reset(Type type, double cutoffFrequency, double samplingFrequency, double quality, double gain) -> void {
auto Biquad::reset(Type type, real cutoffFrequency, real samplingFrequency, real quality, real gain) -> void {
this->type = type;
this->cutoffFrequency = cutoffFrequency;
this->samplingFrequency = samplingFrequency;
@ -40,10 +42,10 @@ auto Biquad::reset(Type type, double cutoffFrequency, double samplingFrequency,
z1 = 0.0;
z2 = 0.0;
double v = pow(10, fabs(gain) / 20.0);
double k = tan(Math::Pi * cutoffFrequency / samplingFrequency);
double q = quality;
double n = 0.0;
real v = pow(10, fabs(gain) / 20.0);
real k = tan(Math::Pi * cutoffFrequency / samplingFrequency);
real q = quality;
real n = 0.0;
switch(type) {
@ -140,15 +142,15 @@ auto Biquad::reset(Type type, double cutoffFrequency, double samplingFrequency,
}
}
auto Biquad::process(double in) -> double {
double out = in * a0 + z1;
auto Biquad::process(real in) -> real {
real out = in * a0 + z1;
z1 = in * a1 + z2 - b1 * out;
z2 = in * a2 - b2 * out;
return out;
}
//compute Q values for N-order butterworth filtering
auto Biquad::butterworth(uint order, uint phase) -> double {
auto Biquad::butterworth(uint order, uint phase) -> real {
return -0.5 / cos(Math::Pi * (phase + order + 0.5) / order);
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <nall/dsp/dsp.hpp>
//one-pole first-order IIR filter
namespace nall { namespace DSP { namespace IIR {
@ -10,24 +12,24 @@ struct OnePole {
HighPass,
};
inline auto reset(Type type, double cutoffFrequency, double samplingFrequency) -> void;
inline auto process(double in) -> double; //normalized sample (-1.0 to +1.0)
inline auto reset(Type type, real cutoffFrequency, real samplingFrequency) -> void;
inline auto process(real in) -> real; //normalized sample (-1.0 to +1.0)
private:
Type type;
double cutoffFrequency;
double samplingFrequency;
double a0, b1; //coefficients
double z1; //first-order IIR
real cutoffFrequency;
real samplingFrequency;
real a0, b1; //coefficients
real z1; //first-order IIR
};
auto OnePole::reset(Type type, double cutoffFrequency, double samplingFrequency) -> void {
auto OnePole::reset(Type type, real cutoffFrequency, real samplingFrequency) -> void {
this->type = type;
this->cutoffFrequency = cutoffFrequency;
this->samplingFrequency = samplingFrequency;
z1 = 0.0;
double x = cos(2.0 * Math::Pi * cutoffFrequency / samplingFrequency);
real x = cos(2.0 * Math::Pi * cutoffFrequency / samplingFrequency);
if(type == Type::LowPass) {
b1 = +2.0 - x - sqrt((+2.0 - x) * (+2.0 - x) - 1);
a0 = 1.0 - b1;
@ -37,7 +39,7 @@ auto OnePole::reset(Type type, double cutoffFrequency, double samplingFrequency)
}
}
auto OnePole::process(double in) -> double {
auto OnePole::process(real in) -> real {
return z1 = in * a0 + z1 * b1;
}

View File

@ -1,26 +1,27 @@
#pragma once
#include <nall/queue.hpp>
#include <nall/dsp/dsp.hpp>
namespace nall { namespace DSP { namespace Resampler {
struct Cubic {
inline auto reset(double inputFrequency, double outputFrequency, uint queueSize = 0) -> void;
inline auto reset(real inputFrequency, real outputFrequency, uint queueSize = 0) -> void;
inline auto pending() const -> bool { return samples.pending(); }
inline auto read() -> double { return samples.read(); }
inline auto write(double sample) -> void;
inline auto read() -> real { return samples.read(); }
inline auto write(real sample) -> void;
private:
double inputFrequency;
double outputFrequency;
real inputFrequency;
real outputFrequency;
double ratio;
double fraction;
double history[4];
queue<double> samples;
real ratio;
real fraction;
real history[4];
queue<real> samples;
};
auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void {
auto Cubic::reset(real inputFrequency, real outputFrequency, uint queueSize) -> void {
this->inputFrequency = inputFrequency;
this->outputFrequency = outputFrequency;
if(!queueSize) queueSize = outputFrequency * 0.02; //20ms
@ -31,7 +32,7 @@ auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize)
samples.resize(queueSize);
}
auto Cubic::write(double sample) -> void {
auto Cubic::write(real sample) -> void {
auto& mu = fraction;
auto& s = history;
@ -41,10 +42,10 @@ auto Cubic::write(double sample) -> void {
s[3] = sample;
while(mu <= 1.0) {
double A = s[3] - s[2] - s[0] + s[1];
double B = s[0] - s[1] - A;
double C = s[2] - s[0];
double D = s[1];
real A = s[3] - s[2] - s[0] + s[1];
real B = s[0] - s[1] - A;
real C = s[2] - s[0];
real D = s[1];
samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
mu += ratio;

View File

@ -63,6 +63,12 @@ struct Node {
auto natural() const -> uintmax { return text().natural(); }
auto real() const -> double { return text().real(); }
auto text(const string& fallback) const -> string { return bool(*this) ? text() : fallback; }
auto boolean(bool fallback) const -> bool { return bool(*this) ? boolean() : fallback; }
auto integer(intmax fallback) const -> intmax { return bool(*this) ? integer() : fallback; }
auto natural(uintmax fallback) const -> uintmax { return bool(*this) ? natural() : fallback; }
auto real(double fallback) const -> double { return bool(*this) ? real() : fallback; }
auto setName(const string& name = "") -> Node& { shared->_name = name; return *this; }
auto setValue(const string& value = "") -> Node& { shared->_value = value; return *this; }