Interference emulation
This commit is contained in:
parent
8e858c1bf1
commit
5c854dbdca
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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])
|
||||
|
@ -73,6 +73,7 @@
|
||||
<outlet property="frameBlendingModePopupButton" destination="lxk-db-Sxv" id="wzt-uo-TE6"/>
|
||||
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
||||
<outlet property="interferenceSlider" destination="FpE-5i-j5L" id="hfH-e8-7cx"/>
|
||||
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
|
||||
<outlet property="preferredJoypadButton" destination="0Az-0R-oNw" id="7JM-tw-BhK"/>
|
||||
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
|
||||
@ -174,7 +175,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lxk-db-Sxv">
|
||||
<rect key="frame" x="32" y="149" width="229" height="22"/>
|
||||
<rect key="frame" x="30" y="149" width="231" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iHP-Yz-fiH" id="aQ6-HN-7Aj">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -264,7 +265,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NuA-mL-AJZ">
|
||||
<rect key="frame" x="32" y="207" width="228" height="24"/>
|
||||
<rect key="frame" x="30" y="207" width="230" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" minValue="-256" maxValue="256" tickMarkPosition="below" numberOfTickMarks="3" sliderType="linear" id="KX7-G9-k0O"/>
|
||||
<connections>
|
||||
@ -446,11 +447,11 @@
|
||||
<point key="canvasLocation" x="-176" y="848"/>
|
||||
</customView>
|
||||
<customView id="Zn1-Y5-RbR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="86"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="134"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T69-6N-dhT">
|
||||
<rect key="frame" x="30" y="17" width="233" height="26"/>
|
||||
<rect key="frame" x="30" y="65" width="233" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled (Keep DC offset)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Fgo-0S-zUG" id="om2-Bn-43B">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -470,7 +471,7 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
||||
<rect key="frame" x="18" y="49" width="256" height="17"/>
|
||||
<rect key="frame" x="18" y="97" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="High-pass filter:" id="YLF-RL-b2D">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -478,8 +479,25 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GPt-9I-QBh">
|
||||
<rect key="frame" x="18" y="43" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Interference volume:" id="I2Q-6U-uIx">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FpE-5i-j5L">
|
||||
<rect key="frame" x="30" y="18" width="232" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="256" tickMarkPosition="below" sliderType="linear" id="Rbx-DU-xYf"/>
|
||||
<connections>
|
||||
<action selector="volumeTemperatureChanged:" target="QvC-M9-y7g" id="HFU-0q-hj1"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="-176" y="890"/>
|
||||
<point key="canvasLocation" x="-176" y="914"/>
|
||||
</customView>
|
||||
<customView id="8TU-6J-NCg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="467"/>
|
||||
|
55
Core/apu.c
55
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
26
SDL/gui.c
26
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,}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user