mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
65a3e6c676
commit
35ff15f83e
|
@ -47,5 +47,5 @@ include $(ui)/GNUmakefile
|
|||
-include obj/*.d
|
||||
|
||||
clean:
|
||||
$(call delete,out/*)
|
||||
$(call delete,obj/*)
|
||||
$(call delete,out/*)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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/";
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
auto System::configureVideoPalette() -> void {
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoEffects() -> void {
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
}}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue