diff --git a/Cocoa/Document.m b/Cocoa/Document.m
index ad443b6..0beceab 100644
--- a/Cocoa/Document.m
+++ b/Cocoa/Document.m
@@ -287,6 +287,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
GB_set_light_temperature(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]);
+ GB_set_interference_volume(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"]);
GB_set_border_mode(&gb, (GB_border_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]);
[self updatePalette];
GB_set_rgb_encode_callback(&gb, rgbEncode);
@@ -695,6 +696,11 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
name:@"GBLightTemperatureChanged"
object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(updateInterferenceVolume)
+ name:@"GBInterferenceVolumeChanged"
+ object:nil];
+
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateFrameBlendingMode)
name:@"GBFrameBlendingModeChanged"
@@ -1848,6 +1854,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
}
}
+- (void) updateInterferenceVolume
+{
+ if (GB_is_inited(&gb)) {
+ GB_set_interference_volume(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"]);
+ }
+}
- (void) updateFrameBlendingMode
{
diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h
index f0b7506..43a8f1d 100644
--- a/Cocoa/GBPreferencesWindow.h
+++ b/Cocoa/GBPreferencesWindow.h
@@ -18,6 +18,7 @@
@property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
@property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton;
@property (weak) IBOutlet NSSlider *temperatureSlider;
+@property (weak) IBOutlet NSSlider *interferenceSlider;
@property (weak) IBOutlet NSPopUpButton *dmgPopupButton;
@property (weak) IBOutlet NSPopUpButton *sgbPopupButton;
@property (weak) IBOutlet NSPopUpButton *cgbPopupButton;
diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m
index 6edc54e..bd3a4a8 100644
--- a/Cocoa/GBPreferencesWindow.m
+++ b/Cocoa/GBPreferencesWindow.m
@@ -27,6 +27,7 @@
NSPopUpButton *_preferredJoypadButton;
NSPopUpButton *_rumbleModePopupButton;
NSSlider *_temperatureSlider;
+ NSSlider *_interferenceSlider;
}
+ (NSArray *)filterList
@@ -108,6 +109,18 @@
{
return _temperatureSlider;
}
+
+- (void)setInterferenceSlider:(NSSlider *)interferenceSlider
+{
+ _interferenceSlider = interferenceSlider;
+ [interferenceSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"] * 256];
+}
+
+- (NSSlider *)interferenceSlider
+{
+ return _interferenceSlider;
+}
+
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
{
_frameBlendingModePopupButton = frameBlendingModePopupButton;
@@ -303,6 +316,14 @@
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil];
}
+- (IBAction)volumeTemperatureChanged:(id)sender
+{
+ [[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
+ forKey:@"GBInterferenceVolume"];
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"GBInterferenceVolumeChanged" object:nil];
+
+}
+
- (IBAction)franeBlendingModeChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib
index 7ca5f28..248cfce 100644
--- a/Cocoa/Preferences.xib
+++ b/Cocoa/Preferences.xib
@@ -73,6 +73,7 @@
+
@@ -174,7 +175,7 @@
-
+
@@ -264,7 +265,7 @@
-
+
@@ -446,11 +447,11 @@
-
+
-
+
@@ -470,7 +471,7 @@
-
+
@@ -478,8 +479,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/Core/apu.c b/Core/apu.c
index e37b265..3abca7d 100644
--- a/Core/apu.c
+++ b/Core/apu.c
@@ -137,6 +137,45 @@ static double smooth(double x)
return 3*x*x - 2*x*x*x;
}
+static signed interference(GB_gameboy_t *gb)
+{
+ /* These aren't scientifically measured, but based on ear based on several recordings */
+ signed ret = 0;
+ if (gb->halted) {
+ if (gb->model != GB_MODEL_AGB) {
+ ret -= MAX_CH_AMP / 5;
+ }
+ else {
+ ret -= MAX_CH_AMP / 12;
+ }
+ }
+ if (gb->io_registers[GB_IO_LCDC] & 0x80) {
+ ret += MAX_CH_AMP / 7;
+ if ((gb->io_registers[GB_IO_STAT] & 3) == 3 && gb->model != GB_MODEL_AGB) {
+ ret += MAX_CH_AMP / 14;
+ }
+ else if ((gb->io_registers[GB_IO_STAT] & 3) == 1) {
+ ret -= MAX_CH_AMP / 7;
+ }
+ }
+
+ if (gb->apu.global_enable) {
+ ret += MAX_CH_AMP / 10;
+ }
+
+ if (GB_is_cgb(gb) && gb->model < GB_MODEL_AGB && (gb->io_registers[GB_IO_RP] & 1)) {
+ ret += MAX_CH_AMP / 10;
+ }
+
+ if (!GB_is_cgb(gb)) {
+ ret /= 4;
+ }
+
+ ret += rand() % (MAX_CH_AMP / 12);
+
+ return ret;
+}
+
static void render(GB_gameboy_t *gb)
{
GB_sample_t output = {0, 0};
@@ -226,6 +265,17 @@ static void render(GB_gameboy_t *gb)
}
+
+ if (gb->apu_output.interference_volume) {
+ signed interference_bias = interference(gb);
+ int16_t interference_sample = (interference_bias - gb->apu_output.interference_highpass);
+ gb->apu_output.interference_highpass = gb->apu_output.interference_highpass * gb->apu_output.highpass_rate +
+ (1 - gb->apu_output.highpass_rate) * interference_sample;
+ interference_bias *= gb->apu_output.interference_volume;
+
+ filtered_output.left = MAX(MIN(filtered_output.left + interference_bias, 0x7FFF), -0x8000);
+ filtered_output.right = MAX(MIN(filtered_output.right + interference_bias, 0x7FFF), -0x8000);
+ }
assert(gb->apu_output.sample_callback);
gb->apu_output.sample_callback(gb, &filtered_output);
}
@@ -1122,3 +1172,8 @@ void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
}
}
+
+void GB_set_interference_volume(GB_gameboy_t *gb, double volume)
+{
+ gb->apu_output.interference_volume = volume;
+}
diff --git a/Core/apu.h b/Core/apu.h
index 9d5fc80..69cea16 100644
--- a/Core/apu.h
+++ b/Core/apu.h
@@ -154,12 +154,16 @@ typedef struct {
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
+ double interference_volume;
+ double interference_highpass;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
+void GB_set_interference_volume(GB_gameboy_t *gb, double volume);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
+
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
diff --git a/SDL/gui.c b/SDL/gui.c
index a21526d..0846664 100644
--- a/SDL/gui.c
+++ b/SDL/gui.c
@@ -789,7 +789,7 @@ static const struct menu_item graphics_menu[] = {
{"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards},
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
- {"Ambient Light:", decrease_color_temperature, current_color_temperature, increase_color_temperature},
+ {"Ambient Light Temp.:", decrease_color_temperature, current_color_temperature, increase_color_temperature},
{"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards},
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
{"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards},
@@ -852,9 +852,33 @@ void decrease_volume(unsigned index)
}
}
+const char *interference_volume_string(unsigned index)
+{
+ static char ret[5];
+ sprintf(ret, "%d%%", configuration.interference_volume);
+ return ret;
+}
+
+void increase_interference_volume(unsigned index)
+{
+ configuration.interference_volume += 5;
+ if (configuration.interference_volume > 100) {
+ configuration.interference_volume = 100;
+ }
+}
+
+void decrease_interference_volume(unsigned index)
+{
+ configuration.interference_volume -= 5;
+ if (configuration.interference_volume > 100) {
+ configuration.interference_volume = 0;
+ }
+}
+
static const struct menu_item audio_menu[] = {
{"Highpass Filter:", cycle_highpass_filter, highpass_filter_string, cycle_highpass_filter_backwards},
{"Volume:", increase_volume, volume_string, decrease_volume},
+ {"Interference Volume:", increase_interference_volume, interference_volume_string, decrease_interference_volume},
{"Back", return_to_root_menu},
{NULL,}
};
diff --git a/SDL/gui.h b/SDL/gui.h
index b507237..8d69ec3 100644
--- a/SDL/gui.h
+++ b/SDL/gui.h
@@ -115,6 +115,7 @@ typedef struct {
unsigned padding;
uint8_t color_temperature;
char bootrom_path[4096];
+ uint8_t interference_volume;
} configuration_t;
extern configuration_t configuration;
diff --git a/SDL/main.c b/SDL/main.c
index 06cde73..63a61c9 100644
--- a/SDL/main.c
+++ b/SDL/main.c
@@ -121,6 +121,7 @@ static void open_menu(void)
}
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0);
+ GB_set_interference_volume(&gb, configuration.interference_volume / 100.0);
GB_set_border_mode(&gb, configuration.border_mode);
update_palette();
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
@@ -505,6 +506,7 @@ restart:
GB_set_sample_rate(&gb, GB_audio_get_frequency());
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0);
+ GB_set_interference_volume(&gb, configuration.interference_volume / 100.0);
update_palette();
if ((unsigned)configuration.border_mode <= GB_BORDER_ALWAYS) {
GB_set_border_mode(&gb, configuration.border_mode);