diff --git a/bsnes/nall/dsp.hpp b/bsnes/nall/dsp.hpp new file mode 100755 index 00000000..52e43327 --- /dev/null +++ b/bsnes/nall/dsp.hpp @@ -0,0 +1,6 @@ +#ifndef NALL_DSP_HPP +#define NALL_DSP_HPP + +#include + +#endif diff --git a/bsnes/nall/dsp/core.hpp b/bsnes/nall/dsp/core.hpp new file mode 100755 index 00000000..20cfdfdc --- /dev/null +++ b/bsnes/nall/dsp/core.hpp @@ -0,0 +1,226 @@ +#ifndef NALL_DSP_EFFECT_HPP +#define NALL_DSP_EFFECT_HPP + +#include + +namespace nall { + +struct dsp { + inline void set_precision(unsigned precision); + inline void set_frequency(double frequency); + inline void set_volume(double volume); + inline void set_balance(double balance); + inline void set_echo(double echo); + + inline void set_resampler_frequency(double frequency); + + inline void sample(signed lchannel, signed rchannel); + inline bool pending(); + inline void read(signed &lchannel, signed &rchannel); + + inline signed clamp(const unsigned bits, const signed x); + inline void clear(); + inline dsp(); + inline ~dsp(); + +protected: + struct Settings { + unsigned precision; + double frequency; + double volume; + double balance; + double echo; + //internal + double intensity; + } settings; + + struct Resampler { + double frequency; + //internal + double fraction; + double step; + } resampler; + + inline void resampler_run(); + + struct Buffer { + double *sample[2]; + uint16_t rdoffset; + uint16_t wroffset; + inline double& operator()(bool channel, signed offset) { return sample[channel][(uint16_t)(rdoffset + offset)]; } + inline void write(bool channel, signed offset, double data) { sample[channel][(uint16_t)(wroffset + offset)] = data; } + } buffer; + + struct Output { + double *sample[2]; + uint16_t rdoffset; + uint16_t wroffset; + inline double& operator()(bool channel, signed offset) { return sample[channel][(uint16_t)(rdoffset + offset)]; } + inline void write(bool channel, signed offset, double data) { sample[channel][(uint16_t)(wroffset + offset)] = data; } + } output; + + inline void adjust_volume(); + inline void adjust_balance(); + inline void adjust_echo(); +}; + +void dsp::set_precision(unsigned precision) { + settings.precision = precision; + settings.intensity = 1 << (settings.precision - 1); +} + +void dsp::set_frequency(double frequency) { + settings.frequency = frequency; + resampler.fraction = 0; + resampler.step = settings.frequency / resampler.frequency; +} + +void dsp::set_volume(double volume) { + settings.volume = volume; +} + +void dsp::set_balance(double balance) { + settings.balance = balance; +} + +void dsp::set_echo(double echo) { + settings.echo = echo; +} + +void dsp::set_resampler_frequency(double frequency) { + resampler.frequency = frequency; + resampler.fraction = 0; + resampler.step = settings.frequency / resampler.frequency; +} + +void dsp::sample(signed lchannel, signed rchannel) { + buffer.write(0, 0, (double)lchannel / settings.intensity); + buffer.write(1, 0, (double)rchannel / settings.intensity); + buffer.wroffset++; + +#ifdef DSP_NO_RESAMPLER + output.write(0, 0, buffer(0, 0)); + output.write(1, 0, buffer(1, 0)); + output.wroffset++; + buffer.rdoffset++; +#else + resampler_run(); +#endif +} + +bool dsp::pending() { + return output.rdoffset != output.wroffset; +} + +void dsp::read(signed &lchannel, signed &rchannel) { + adjust_volume(); + adjust_balance(); + adjust_echo(); + + lchannel = clamp(settings.precision, output(0, 0) * settings.intensity); + rchannel = clamp(settings.precision, output(1, 0) * settings.intensity); + output.rdoffset++; +} + +void dsp::resampler_run() { + //4-tap hermite + while(resampler.fraction <= 1.0) { + for(unsigned n = 0; n < 2; n++) { + double a = buffer(n, -3); + double b = buffer(n, -2); + double c = buffer(n, -1); + double d = buffer(n, -0); + + const double tension = 0.0; //-1 = low, 0 = normal, +1 = high + const double bias = 0.0; //-1 = left, 0 = even, +1 = right + + double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu1 = resampler.fraction; + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0; + m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0; + m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0; + m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + double result = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); + output.write(n, 0, result); + } + + output.wroffset++; + resampler.fraction += resampler.step; + } + + buffer.rdoffset++; + resampler.fraction -= 1.0; +} + +void dsp::adjust_volume() { + output(0, 0) *= settings.volume; + output(1, 0) *= settings.volume; +} + +void dsp::adjust_balance() { + if(settings.balance < 0.0) output(1, 0) *= 1.0 + settings.balance; + if(settings.balance > 0.0) output(0, 0) *= 1.0 - settings.balance; +} + +void dsp::adjust_echo() { + //... +} + +signed dsp::clamp(const unsigned bits, const signed x) { + const signed b = 1U << (bits - 1); + const signed m = (1U << (bits - 1)) - 1; + return (x > m) ? m : (x < -b) ? -b : x; +} + +void dsp::clear() { + resampler.fraction = 0.0; + resampler.step = 1.0; + for(unsigned n = 0; n < 65536; n++) { + buffer.sample[0][n] = 0.0; + buffer.sample[1][n] = 0.0; + output.sample[0][n] = 0.0; + output.sample[1][n] = 0.0; + } + buffer.rdoffset = 0; + buffer.wroffset = 0; + output.rdoffset = 0; + output.wroffset = 0; +} + +dsp::dsp() { + settings.precision = 16; + settings.frequency = 44100.0; + settings.volume = 1.0; + settings.balance = 0.0; + settings.echo = 0.0; + settings.intensity = 1 << (settings.precision - 1); + resampler.frequency = 44100.0; + + buffer.sample[0] = new double[65536]; + buffer.sample[1] = new double[65536]; + output.sample[0] = new double[65536]; + output.sample[1] = new double[65536]; + + clear(); +} + +dsp::~dsp() { + delete[] buffer.sample[0]; + delete[] buffer.sample[1]; + delete[] output.sample[0]; + delete[] output.sample[1]; +} + +} + +#endif diff --git a/bsnes/nall/file.hpp b/bsnes/nall/file.hpp index 4b9f76b4..40453448 100755 --- a/bsnes/nall/file.hpp +++ b/bsnes/nall/file.hpp @@ -32,6 +32,10 @@ namespace nall { return true; } + static bool read(const string &filename, const uint8_t *&data, unsigned &size) { + return file::read(filename, (uint8_t*&)data, size); + } + static bool write(const string &filename, const uint8_t *data, unsigned size) { file fp; if(fp.open(filename, mode::write) == false) return false; diff --git a/bsnes/nall/ips.hpp b/bsnes/nall/ips.hpp new file mode 100755 index 00000000..87c7de25 --- /dev/null +++ b/bsnes/nall/ips.hpp @@ -0,0 +1,110 @@ +#ifndef NALL_IPS_HPP +#define NALL_IPS_HPP + +#include +#include +#include + +namespace nall { + +struct ips { + inline bool apply(); + inline void source(const uint8_t *data, unsigned size); + inline void modify(const uint8_t *data, unsigned size); + inline bool source(const string &filename); + inline bool modify(const string &filename); + inline ips(); + inline ~ips(); + + uint8_t *data; + unsigned size; + const uint8_t *sourceData; + unsigned sourceSize; + const uint8_t *modifyData; + unsigned modifySize; +}; + +bool ips::apply() { + if(modifySize < 8) return false; + if(modifyData[0] != 'P') return false; + if(modifyData[1] != 'A') return false; + if(modifyData[2] != 'T') return false; + if(modifyData[3] != 'C') return false; + if(modifyData[4] != 'H') return false; + + if(data) delete[] data; + data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding + size = sourceSize; + memcpy(data, sourceData, sourceSize); + unsigned offset = 5; + + while(true) { + unsigned address, length; + + if(offset > modifySize - 3) break; + address = modifyData[offset++] << 16; + address |= modifyData[offset++] << 8; + address |= modifyData[offset++] << 0; + + if(address == 0x454f46) { //EOF + if(offset == modifySize) return true; + if(offset == modifySize - 3) { + size = modifyData[offset++] << 16; + size |= modifyData[offset++] << 8; + size |= modifyData[offset++] << 0; + return true; + } + } + + if(offset > modifySize - 2) break; + length = modifyData[offset++] << 8; + length |= modifyData[offset++] << 0; + + if(length) { //Copy + if(offset > modifySize - length) break; + while(length--) data[address++] = modifyData[offset++]; + } else { //RLE + if(offset > modifySize - 3) break; + length = modifyData[offset++] << 8; + length |= modifyData[offset++] << 0; + if(length == 0) break; //illegal + while(length--) data[address++] = modifyData[offset]; + offset++; + } + + size = max(size, address); + } + + delete[] data; + data = 0; + return false; +} + +void ips::source(const uint8_t *data, unsigned size) { + sourceData = data, sourceSize = size; +} + +void ips::modify(const uint8_t *data, unsigned size) { + modifyData = data, modifySize = size; +} + +bool ips::source(const string &filename) { + return file::read(filename, sourceData, sourceSize); +} + +bool ips::modify(const string &filename) { + return file::read(filename, modifyData, modifySize); +} + +ips::ips() : data(0), sourceData(0), modifyData(0) { +} + +ips::~ips() { + if(data) delete[] data; + if(sourceData) delete[] sourceData; + if(modifyData) delete[] modifyData; +} + +} + +#endif diff --git a/bsnes/ruby/audio.hpp b/bsnes/ruby/audio.hpp index aaf43ff0..45ae96ef 100755 --- a/bsnes/ruby/audio.hpp +++ b/bsnes/ruby/audio.hpp @@ -1,9 +1,5 @@ class Audio { public: - static const char *Volume; - static const char *Resample; - static const char *ResampleRatio; - static const char *Handle; static const char *Synchronize; static const char *Frequency; diff --git a/bsnes/ruby/ruby.cpp b/bsnes/ruby/ruby.cpp index 73b53c28..861401d1 100755 --- a/bsnes/ruby/ruby.cpp +++ b/bsnes/ruby/ruby.cpp @@ -161,6 +161,11 @@ VideoInterface::~VideoInterface() { term(); } /* AudioInterface */ +const char *Audio::Handle = "Handle"; +const char *Audio::Synchronize = "Synchronize"; +const char *Audio::Frequency = "Frequency"; +const char *Audio::Latency = "Latency"; + void AudioInterface::driver(const char *driver) { if(p) term(); @@ -269,7 +274,25 @@ const char* AudioInterface::driver_list() { "None"; } -#include "ruby_audio.cpp" +bool AudioInterface::init() { + if(!p) driver(); + return p->init(); +} + +void AudioInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool AudioInterface::cap(const string& name) { return p ? p->cap(name) : false; } +any AudioInterface::get(const string& name) { return p ? p->get(name) : false; } +bool AudioInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; } +void AudioInterface::sample(uint16_t left, uint16_t right) { if(p) p->sample(left, right); } +void AudioInterface::clear() { if(p) p->clear(); } +AudioInterface::AudioInterface() : p(0) {} +AudioInterface::~AudioInterface() { term(); } /* InputInterface */ diff --git a/bsnes/ruby/ruby.hpp b/bsnes/ruby/ruby.hpp index 4b64f880..f6198d02 100755 --- a/bsnes/ruby/ruby.hpp +++ b/bsnes/ruby/ruby.hpp @@ -1,6 +1,6 @@ /* ruby - version: 0.06a (2011-02-27) + version: 0.07 (2011-08-14) license: public domain */ @@ -66,14 +66,6 @@ public: private: Audio *p; - - unsigned volume; - - //resample unit - double hermite(double mu, double a, double b, double c, double d); - bool resample_enabled; - double r_step, r_frac; - int r_left[4], r_right[4]; }; class InputInterface { diff --git a/bsnes/ruby/ruby_audio.cpp b/bsnes/ruby/ruby_audio.cpp deleted file mode 100755 index 4afe1f56..00000000 --- a/bsnes/ruby/ruby_audio.cpp +++ /dev/null @@ -1,133 +0,0 @@ -const char *Audio::Volume = "Volume"; -const char *Audio::Resample = "Resample"; -const char *Audio::ResampleRatio = "ResampleRatio"; - -const char *Audio::Handle = "Handle"; -const char *Audio::Synchronize = "Synchronize"; -const char *Audio::Frequency = "Frequency"; -const char *Audio::Latency = "Latency"; - -bool AudioInterface::init() { - if(!p) driver(); - return p->init(); -} - -void AudioInterface::term() { - if(p) { - delete p; - p = 0; - } -} - -bool AudioInterface::cap(const string& name) { - if(name == Audio::Volume) return true; - if(name == Audio::Resample) return true; - if(name == Audio::ResampleRatio) return true; - - return p ? p->cap(name) : false; -} - -any AudioInterface::get(const string& name) { - if(name == Audio::Volume) return volume; - if(name == Audio::Resample) return resample_enabled; - if(name == Audio::ResampleRatio); - - return p ? p->get(name) : false; -} - -bool AudioInterface::set(const string& name, const any& value) { - if(name == Audio::Volume) { - volume = any_cast(value); - return true; - } - - if(name == Audio::Resample) { - resample_enabled = any_cast(value); - return true; - } - - if(name == Audio::ResampleRatio) { - r_step = any_cast(value); - r_frac = 0; - return true; - } - - return p ? p->set(name, value) : false; -} - -//4-tap hermite interpolation -double AudioInterface::hermite(double mu1, double a, double b, double c, double d) { - const double tension = 0.0; //-1 = low, 0 = normal, 1 = high - const double bias = 0.0; //-1 = left, 0 = even, 1 = right - - double mu2, mu3, m0, m1, a0, a1, a2, a3; - - mu2 = mu1 * mu1; - mu3 = mu2 * mu1; - - m0 = (b - a) * (1 + bias) * (1 - tension) / 2; - m0 += (c - b) * (1 - bias) * (1 - tension) / 2; - m1 = (c - b) * (1 + bias) * (1 - tension) / 2; - m1 += (d - c) * (1 - bias) * (1 - tension) / 2; - - a0 = +2 * mu3 - 3 * mu2 + 1; - a1 = mu3 - 2 * mu2 + mu1; - a2 = mu3 - mu2; - a3 = -2 * mu3 + 3 * mu2; - - return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); -} - -void AudioInterface::sample(uint16_t left, uint16_t right) { - int s_left = (int16_t)left; - int s_right = (int16_t)right; - - if(volume != 100) { - s_left = sclamp<16>((double)s_left * (double)volume / 100.0); - s_right = sclamp<16>((double)s_right * (double)volume / 100.0); - } - - r_left [0] = r_left [1]; - r_left [1] = r_left [2]; - r_left [2] = r_left [3]; - r_left [3] = s_left; - - r_right[0] = r_right[1]; - r_right[1] = r_right[2]; - r_right[2] = r_right[3]; - r_right[3] = s_right; - - if(resample_enabled == false) { - if(p) p->sample(left, right); - return; - } - - while(r_frac <= 1.0) { - int output_left = sclamp<16>(hermite(r_frac, r_left [0], r_left [1], r_left [2], r_left [3])); - int output_right = sclamp<16>(hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3])); - r_frac += r_step; - if(p) p->sample(output_left, output_right); - } - - r_frac -= 1.0; -} - -void AudioInterface::clear() { - r_frac = 0; - r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; - r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; - if(p) p->clear(); -} - -AudioInterface::AudioInterface() { - p = 0; - volume = 100; - resample_enabled = false; - r_step = r_frac = 0; - r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; - r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; -} - -AudioInterface::~AudioInterface() { - term(); -} diff --git a/bsnes/snes/interface/interface.hpp b/bsnes/snes/interface/interface.hpp index 1bd787fd..342956af 100755 --- a/bsnes/snes/interface/interface.hpp +++ b/bsnes/snes/interface/interface.hpp @@ -1,7 +1,7 @@ class Interface { public: virtual void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {} - virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {} + virtual void audio_sample(int16_t l_sample, int16_t r_sample) {} virtual int16_t input_poll(bool port, Input::Device device, unsigned index, unsigned id) { return 0; } virtual void message(const string &text) { print(text, "\n"); } diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 593846eb..85fe968c 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,11 +1,17 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "081.01"; + static const char Version[] = "081.02"; static const unsigned SerializerVersion = 21; } } +/* + bsnes - SNES emulator + author: byuu + license: GPLv2 +*/ + #include #include diff --git a/bsnes/ui-libsnes/libsnes.cpp b/bsnes/ui-libsnes/libsnes.cpp index 16a8bdde..c3cc3b16 100755 --- a/bsnes/ui-libsnes/libsnes.cpp +++ b/bsnes/ui-libsnes/libsnes.cpp @@ -21,7 +21,7 @@ struct Interface : public SNES::Interface { if(pinput_poll) pinput_poll(); } - void audio_sample(uint16_t left, uint16_t right) { + void audio_sample(int16_t left, int16_t right) { if(paudio_sample) return paudio_sample(left, right); } diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index e156ba91..a86c1408 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -59,4 +60,5 @@ private: void saveGeometry(); }; +extern nall::dsp dspaudio; extern Application application; diff --git a/bsnes/ui/config.cpp b/bsnes/ui/config.cpp index 4ce39ac4..853bd5c1 100755 --- a/bsnes/ui/config.cpp +++ b/bsnes/ui/config.cpp @@ -37,6 +37,7 @@ void Configuration::create() { attach(audio.synchronize = true, "audio.synchronize"); attach(audio.mute = false, "audio.mute"); attach(audio.volume = 100, "audio.volume"); + attach(audio.balance = 0, "audio.balance"); attach(audio.latency = 60, "audio.latency"); attach(audio.inputFrequency = 32000, "audio.inputFrequency"); attach(audio.outputFrequency = 44100, "audio.outputFrequency"); diff --git a/bsnes/ui/config.hpp b/bsnes/ui/config.hpp index beb32359..f1c94993 100755 --- a/bsnes/ui/config.hpp +++ b/bsnes/ui/config.hpp @@ -20,6 +20,7 @@ struct Configuration : public configuration { bool synchronize; bool mute; unsigned volume; + unsigned balance; unsigned latency; unsigned inputFrequency; unsigned outputFrequency; diff --git a/bsnes/ui/interface.cpp b/bsnes/ui/interface.cpp index 9a9c1b0c..93519cfa 100755 --- a/bsnes/ui/interface.cpp +++ b/bsnes/ui/interface.cpp @@ -137,9 +137,14 @@ void Interface::video_refresh(const uint16_t *data, bool hires, bool interlace, } } -void Interface::audio_sample(uint16_t left, uint16_t right) { - if(config.audio.mute) left = right = 0; - audio.sample(left, right); +void Interface::audio_sample(int16_t lchannel, int16_t rchannel) { + if(config.audio.mute) lchannel = 0, rchannel = 0; + dspaudio.sample(lchannel, rchannel); + while(dspaudio.pending()) { + signed lsample, rsample; + dspaudio.read(lsample, rsample); + audio.sample(lsample, rsample); + } } int16_t Interface::input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) { diff --git a/bsnes/ui/interface.hpp b/bsnes/ui/interface.hpp index aacf0068..a2a3a96a 100755 --- a/bsnes/ui/interface.hpp +++ b/bsnes/ui/interface.hpp @@ -16,7 +16,7 @@ struct Filter : public library { struct Interface : public SNES::Interface { void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan); - void audio_sample(uint16_t left, uint16_t right); + void audio_sample(int16_t left, int16_t right); int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id); void message(const string &text); diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 30e49cd1..6dfa414b 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -1,6 +1,7 @@ #include "base.hpp" #include "interface.cpp" #include "config.cpp" +nall::dsp dspaudio; Application application; void Application::main(int argc, char **argv) { @@ -96,17 +97,20 @@ void Application::main(int argc, char **argv) { audio.driver(config.audio.driver); audio.set(Audio::Handle, mainWindow.viewport.handle()); audio.set(Audio::Synchronize, config.audio.synchronize); - audio.set(Audio::Volume, config.audio.volume); audio.set(Audio::Latency, config.audio.latency); audio.set(Audio::Frequency, config.audio.outputFrequency); - audio.set(Audio::Resample, true); - audio.set(Audio::ResampleRatio, (double)config.audio.inputFrequency / (double)config.audio.outputFrequency); if(audio.init() == false) { MessageWindow::critical(mainWindow, "Failed to initialize audio."); audio.driver("None"); audio.init(); } + dspaudio.set_precision(16); //16-bit signed audio + dspaudio.set_volume((double)config.audio.volume / 100.0); + dspaudio.set_balance((double)((signed)config.audio.balance - 100) / 100.0); + dspaudio.set_frequency(config.audio.inputFrequency); + dspaudio.set_resampler_frequency(config.audio.outputFrequency); + input.driver(config.input.driver); input.set(Input::Handle, mainWindow.viewport.handle()); if(input.init() == false) { diff --git a/bsnes/ui/settings/audio.cpp b/bsnes/ui/settings/audio.cpp index 457af86c..3a6c0fb8 100755 --- a/bsnes/ui/settings/audio.cpp +++ b/bsnes/ui/settings/audio.cpp @@ -4,10 +4,12 @@ void AudioSettings::create() { title.setText("Audio Settings"); title.setFont(application.titleFont); - volumeLabel.setText("Volume:"); - volumeSlider.setLength(201); frequencyLabel.setText("Frequency:"); frequencySlider.setLength(2001); + volumeLabel.setText("Volume:"); + volumeSlider.setLength(201); + balanceLabel.setText("Balance:"); + balanceSlider.setLength(201); panelLayout.setMargin(5); panelLayout.append(panel, SettingsWindow::PanelWidth, ~0, 5); @@ -15,34 +17,48 @@ void AudioSettings::create() { layout.append(title, ~0, 0, 5); - volumeLayout.append(volumeLabel, 70, 0); - volumeLayout.append(volumeValue, 60, 0); - volumeLayout.append(volumeSlider, ~0, 0); - layout.append(volumeLayout); - frequencyLayout.append(frequencyLabel, 70, 0); frequencyLayout.append(frequencyValue, 60, 0); frequencyLayout.append(frequencySlider, ~0, 0); layout.append(frequencyLayout); + volumeLayout.append(volumeLabel, 70, 0); + volumeLayout.append(volumeValue, 60, 0); + volumeLayout.append(volumeSlider, ~0, 0); + layout.append(volumeLayout); + + balanceLayout.append(balanceLabel, 70, 0); + balanceLayout.append(balanceValue, 60, 0); + balanceLayout.append(balanceSlider, ~0, 0); + layout.append(balanceLayout); + layout.append(spacer, ~0, ~0); settingsWindow.append(panelLayout); + frequencySlider.onChange = [this] { + config.audio.inputFrequency = frequencySlider.position() + 31000; + dspaudio.set_frequency(config.audio.inputFrequency); + frequencyValue.setText({ config.audio.inputFrequency, "hz" }); + }; + volumeSlider.onChange = [this] { config.audio.volume = volumeSlider.position(); - audio.set(Audio::Volume, config.audio.volume); + dspaudio.set_volume((double)config.audio.volume / 100.0); volumeValue.setText({ config.audio.volume, "%" }); }; - frequencySlider.onChange = [this] { - config.audio.inputFrequency = frequencySlider.position() + 31000; - audio.set(Audio::ResampleRatio, (double)config.audio.inputFrequency / (double)config.audio.outputFrequency); - frequencyValue.setText({ config.audio.inputFrequency, "hz" }); + balanceSlider.onChange = [this] { + config.audio.balance = balanceSlider.position(); + dspaudio.set_balance((double)((signed)config.audio.balance - 100) / 100.0); + balanceValue.setText({ (signed)config.audio.balance - 100 }); }; + frequencySlider.setPosition(config.audio.inputFrequency - 31000); + frequencyValue.setText({ config.audio.inputFrequency, "hz" }); + volumeSlider.setPosition(config.audio.volume); volumeValue.setText({ config.audio.volume, "%" }); - frequencySlider.setPosition(config.audio.inputFrequency - 31000); - frequencyValue.setText({ config.audio.inputFrequency, "hz" }); + balanceSlider.setPosition(config.audio.balance); + balanceValue.setText({ (signed)config.audio.balance - 100 }); } diff --git a/bsnes/ui/settings/audio.hpp b/bsnes/ui/settings/audio.hpp index cfefb303..2ea8c053 100755 --- a/bsnes/ui/settings/audio.hpp +++ b/bsnes/ui/settings/audio.hpp @@ -4,15 +4,20 @@ struct AudioSettings { VerticalLayout layout; Label title; + HorizontalLayout frequencyLayout; + Label frequencyLabel; + Label frequencyValue; + HorizontalSlider frequencySlider; + HorizontalLayout volumeLayout; Label volumeLabel; Label volumeValue; HorizontalSlider volumeSlider; - HorizontalLayout frequencyLayout; - Label frequencyLabel; - Label frequencyValue; - HorizontalSlider frequencySlider; + HorizontalLayout balanceLayout; + Label balanceLabel; + Label balanceValue; + HorizontalSlider balanceSlider; Widget spacer;