Skip to content

Commit

Permalink
v106.234
Browse files Browse the repository at this point in the history
Corrected audio resampling when inputRate < outputRate
Improved Super Game Boy audio mixing
  • Loading branch information
byuu committed Dec 20, 2019
1 parent a927852 commit 9100e15
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 107 deletions.
4 changes: 2 additions & 2 deletions higan/emulator/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ using namespace nall;

namespace higan {
static const string Name = "higan";
static const string Version = "106.233";
static const string Version = "106.234";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";

//incremented only when serialization format changes
static const string SerializerVersion = "106.210";
static const string SerializerVersion = "106.234";

namespace Constants {
namespace Colorburst {
Expand Down
1 change: 1 addition & 0 deletions higan/emulator/node/audio/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ auto Stream::setChannels(uint channels) -> void {

auto Stream::setFrequency(double frequency) -> void {
_frequency = frequency;
setResamplerFrequency(_resamplerFrequency);
}

auto Stream::setResamplerFrequency(double resamplerFrequency) -> void {
Expand Down
4 changes: 2 additions & 2 deletions higan/emulator/node/audio/stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ struct Stream : Object {
DSP::Resampler::Cubic resampler;
};
vector<Channel> _channels;
double _frequency = 0;
double _resamplerFrequency = 0;
double _frequency = 48000.0;
double _resamplerFrequency = 48000.0;
};
16 changes: 10 additions & 6 deletions higan/sfc/coprocessor/icd/icd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ ICD icd;
#include "io.cpp"
#include "serialization.cpp"

auto ICD::clockFrequency() const -> double {
return Frequency ? Frequency : system.cpuFrequency();
}

auto ICD::load(Node::Peripheral parent, Node::Peripheral from) -> void {
GameBoy::superGameBoy = this;
GameBoy::SuperGameBoyInterface::load((Node::Object&)parent, Node::serialize(from));
Expand All @@ -15,16 +19,15 @@ auto ICD::load(Node::Peripheral parent, Node::Peripheral from) -> void {

auto ICD::unload() -> void {
GameBoy::SuperGameBoyInterface::unload();
}

auto ICD::name() const -> string {
return GameBoy::interface->game();
cpu.coprocessors.removeByValue(this);
Thread::destroy();
}

auto ICD::main() -> void {
#if 0
static uint n=0;
float x=sin((2*3.141592*(n++/64)*1000.0)/44100.0)*0.1;
static uint n = 0;
float x = sin((2*3.141592*(n++/64)*1000.0)/44100.0)*0.1;
GameBoy::apu.stream->sample(x, x);
Thread::step(2);
Thread::synchronize(cpu);
Expand All @@ -43,7 +46,7 @@ auto ICD::main() -> void {

auto ICD::power(bool reset) -> void {
//SGB1 uses CPU oscillator; SGB2 uses dedicated oscillator
Thread::create((Frequency ? Frequency : system.cpuFrequency()) / 5.0, [&] {
Thread::create(clockFrequency() / 5.0, [&] {
while(true) {
if(scheduler.serializing()) GameBoy::system.runToSave();
scheduler.serialize();
Expand Down Expand Up @@ -82,6 +85,7 @@ auto ICD::power(bool reset) -> void {
vcounter = 0;

GameBoy::system.power();
GameBoy::apu.stream->setFrequency(clockFrequency() / 5.0 / 2.0);
}

#endif
3 changes: 2 additions & 1 deletion higan/sfc/coprocessor/icd/icd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

struct ICD : Platform, GameBoy::SuperGameBoyInterface, Thread {
//icd.cpp
auto clockFrequency() const -> double;

auto load(Node::Peripheral, Node::Peripheral) -> void;
auto unload() -> void;

auto name() const -> string;
auto main() -> void;
auto power(bool reset = false) -> void;

Expand Down
10 changes: 6 additions & 4 deletions higan/sfc/coprocessor/icd/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ auto ICD::writeIO(uint24 address, uint8 data) -> void {
}
auto frequency = system.cpuFrequency();
switch(data.bit(0,1)) {
case 0: Thread::setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
case 1: Thread::setFrequency(frequency / 5); break; //normal
case 2: Thread::setFrequency(frequency / 7); break; //slow
case 3: Thread::setFrequency(frequency / 9); break; //very slow
case 0: frequency /= 4; break; //fast (glitchy, even on real hardware)
case 1: frequency /= 5; break; //normal
case 2: frequency /= 7; break; //slow
case 3: frequency /= 9; break; //very slow
}
Thread::setFrequency(frequency);
GameBoy::apu.stream->setFrequency(frequency / 2.0);
r6003 = data;
return;
}
Expand Down
61 changes: 32 additions & 29 deletions higan/target-higan/emulator/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,38 +150,41 @@ auto Emulator::video(higan::Node::Screen node, const uint32_t* data, uint pitch,
auto Emulator::audio(higan::Node::Stream) -> void {
if(!streams) return; //should never occur

//wait until every stream has pending samples to be mixed
for(auto stream : streams) {
if(!stream->pending()) return;
}

//mix all streams together
double samples[2] = {0.0, 0.0};
for(auto& stream : streams) {
double buffer[2];
uint channels = stream->read(buffer);
if(channels == 1) {
//monaural -> stereo mixing
samples[0] += buffer[0];
samples[1] += buffer[0];
} else {
//stereo mixing
samples[0] += buffer[0];
samples[1] += buffer[1];
//process all pending frames (there may be more than one waiting)
while(true) {
//only process a frame if all streams have at least one pending frame
for(auto& stream : streams) {
if(!stream->pending()) return;
}
}

//apply volume, balance, and clamping to the output samples
double volume = !settings.audio.mute ? settings.audio.volume : 0.0;
double balance = settings.audio.balance;
for(uint c : range(2)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
if(balance < 0.0) samples[1] *= 1.0 + balance;
if(balance > 0.0) samples[0] *= 1.0 - balance;
}
//mix all frames together
double samples[2] = {0.0, 0.0};
for(auto& stream : streams) {
double buffer[2];
uint channels = stream->read(buffer);
if(channels == 1) {
//monaural -> stereo mixing
samples[0] += buffer[0];
samples[1] += buffer[0];
} else {
//stereo mixing
samples[0] += buffer[0];
samples[1] += buffer[1];
}
}

//send samples to the audio output device
audioInstance.output(samples);
//apply volume, balance, and clamping to the output frame
double volume = !settings.audio.mute ? settings.audio.volume : 0.0;
double balance = settings.audio.balance;
for(uint c : range(2)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
if(balance < 0.0) samples[1] *= 1.0 + balance;
if(balance > 0.0) samples[0] *= 1.0 - balance;
}

//send frame to the audio output device
audioInstance.output(samples);
}
}

auto Emulator::input(higan::Node::Input input) -> void {
Expand Down
19 changes: 19 additions & 0 deletions higan/target-higan/menus/help.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
HelpMenu::HelpMenu(MenuBar* parent) : Menu(parent) {
setText("Help");
webpage.setIcon(Icon::Application::Browser).setText("Webpage ...").onActivate([&] {
invoke("https://byuu.org/higan");
});
userGuide.setIcon(Icon::Application::Browser).setText("User Guide ...").onActivate([&] {
invoke("https://byuu.org/higan/user-guide");
});
about.setIcon(Icon::Prompt::Question).setText("About ...").onActivate([&] {
AboutDialog()
.setLogo(Resource::Logo)
.setVersion(higan::Version)
.setAuthor(higan::Author)
.setLicense(higan::License)
.setWebsite(higan::Website)
.setAlignment(program, {0.5f, program.panelLayout.visible() ? 0.32f : 0.5f})
.show();
});
}
1 change: 1 addition & 0 deletions higan/target-higan/menus/menus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
#include "system.cpp"
#include "settings.cpp"
#include "tools.cpp"
#include "help.cpp"
3 changes: 2 additions & 1 deletion higan/target-higan/menus/menus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ struct ToolsMenu : Menu {

struct HelpMenu : Menu {
HelpMenu(MenuBar*);
MenuItem documentation{this};
MenuItem webpage{this};
MenuItem userGuide{this};
MenuSeparator separator{this};
MenuItem about{this};
};
19 changes: 0 additions & 19 deletions higan/target-higan/menus/tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,3 @@ ToolsMenu::ToolsMenu(MenuBar* parent) : Menu(parent) {

pauseEmulation.setText("Pause Emulation");
}

//

HelpMenu::HelpMenu(MenuBar* parent) : Menu(parent) {
setText("Help");
documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] {
invoke("https://doc.byuu.org/higan");
});
about.setIcon(Icon::Prompt::Question).setText("About ...").onActivate([&] {
AboutDialog()
.setLogo(Resource::Logo)
.setVersion(higan::Version)
.setAuthor(higan::Author)
.setLicense(higan::License)
.setWebsite(higan::Website)
.setAlignment(program, {0.5f, program.panelLayout.visible() ? 0.32f : 0.5f})
.show();
});
}
16 changes: 0 additions & 16 deletions hiro/windows/widget/label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,6 @@ auto pLabel::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> ma
return msg == WM_ERASEBKGND;
}

if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
switch(msg) {
case WM_LBUTTONDOWN: self().doMousePress(Mouse::Button::Left); break;
case WM_MBUTTONDOWN: self().doMousePress(Mouse::Button::Middle); break;
case WM_RBUTTONDOWN: self().doMousePress(Mouse::Button::Right); break;
}
}

if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
switch(msg) {
case WM_LBUTTONUP: self().doMouseRelease(Mouse::Button::Left); break;
case WM_MBUTTONUP: self().doMouseRelease(Mouse::Button::Middle); break;
case WM_RBUTTONUP: self().doMouseRelease(Mouse::Button::Right); break;
}
}

return pWidget::windowProc(hwnd, msg, wparam, lparam);
}

Expand Down
2 changes: 1 addition & 1 deletion icarus/icarus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ using namespace hiro;

namespace icarus {
static const string Name = "icarus";
static const string Version = "106.206";
static const string Version = "106.234";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";
Expand Down
3 changes: 3 additions & 0 deletions nall/chrono.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ inline auto timestamp() -> uint64_t {
inline auto timestamp(const string& datetime) -> uint64_t {
static const uint monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint64_t timestamp = 0;
if(datetime.match("??????????")) {
return datetime.natural();
}
if(datetime.match("????*")) {
uint year = datetime.slice(0, 4).natural();
if(year < 1970 || year > 2199) return 0;
Expand Down
55 changes: 29 additions & 26 deletions nall/dsp/resampler/cubic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
namespace nall::DSP::Resampler {

struct Cubic {
inline auto inputFrequency() const -> double { return _inputFrequency; }
inline auto outputFrequency() const -> double { return _outputFrequency; }

inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
inline auto setInputFrequency(double inputFrequency) -> void;
inline auto pending() const -> bool;
Expand All @@ -15,41 +18,41 @@ struct Cubic {
inline auto serialize(serializer&) -> void;

private:
double inputFrequency;
double outputFrequency;
double _inputFrequency;
double _outputFrequency;

double ratio;
double fraction;
double history[4];
queue<double> samples;
double _ratio;
double _fraction;
double _history[4];
queue<double> _samples;
};

auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void {
this->inputFrequency = inputFrequency;
this->outputFrequency = outputFrequency ? outputFrequency : this->inputFrequency;
_inputFrequency = inputFrequency;
_outputFrequency = outputFrequency ? outputFrequency : _inputFrequency;

ratio = inputFrequency / outputFrequency;
fraction = 0.0;
for(auto& sample : history) sample = 0.0;
samples.resize(queueSize ? queueSize : this->outputFrequency * 0.02); //default to 20ms max queue size
_ratio = _inputFrequency / _outputFrequency;
_fraction = 0.0;
for(auto& sample : _history) sample = 0.0;
_samples.resize(queueSize ? queueSize : _outputFrequency * 0.02); //default to 20ms max queue size
}

auto Cubic::setInputFrequency(double inputFrequency) -> void {
this->inputFrequency = inputFrequency;
ratio = inputFrequency / outputFrequency;
_inputFrequency = inputFrequency;
_ratio = _inputFrequency / _outputFrequency;
}

auto Cubic::pending() const -> bool {
return samples.pending();
return _samples.pending();
}

auto Cubic::read() -> double {
return samples.read();
return _samples.read();
}

auto Cubic::write(double sample) -> void {
auto& mu = fraction;
auto& s = history;
auto& mu = _fraction;
auto& s = _history;

s[0] = s[1];
s[1] = s[2];
Expand All @@ -62,20 +65,20 @@ auto Cubic::write(double sample) -> void {
double C = s[2] - s[0];
double D = s[1];

samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
mu += ratio;
_samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
mu += _ratio;
}

mu -= 1.0;
}

auto Cubic::serialize(serializer& s) -> void {
s.real(inputFrequency);
s.real(outputFrequency);
s.real(ratio);
s.real(fraction);
s.array(history);
samples.serialize(s);
s.real(_inputFrequency);
s.real(_outputFrequency);
s.real(_ratio);
s.real(_fraction);
s.array(_history);
_samples.serialize(s);
}

}
Loading

0 comments on commit 9100e15

Please sign in to comment.