changeset 1798:5278b6e44fc1

Optionally emulate the offset around zero in the imperfect DAC of a discrete YM2612
author Michael Pavone <pavone@retrodev.com>
date Sun, 24 Mar 2019 19:59:41 -0700
parents 5ff8f0d28188
children ed6c38cd288c
files genesis.c nuklear_ui/blastem_nuklear.c ym2612.c ym2612.h
diffstat 4 files changed, 44 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/genesis.c	Sun Mar 24 13:31:22 2019 -0700
+++ b/genesis.c	Sun Mar 24 19:59:41 2019 -0700
@@ -1334,20 +1334,23 @@
 	io_keyboard_up(&gen->io, scancode);
 }
 
-static void set_gain_config(genesis_context *gen)
+static void set_audio_config(genesis_context *gen)
 {
 	char *config_gain;
 	config_gain = tern_find_path(config, "audio\0psg_gain\0", TVAL_PTR).ptrval;
 	render_audio_source_gaindb(gen->psg->audio, config_gain ? atof(config_gain) : 0.0f);
 	config_gain = tern_find_path(config, "audio\0fm_gain\0", TVAL_PTR).ptrval;
 	render_audio_source_gaindb(gen->ym->audio, config_gain ? atof(config_gain) : 0.0f);
+	
+	char *config_dac = tern_find_path_default(config, "audio\0fm_dac\0", (tern_val){.ptrval="zero_offset"}, TVAL_PTR).ptrval;
+	ym_enable_zero_offset(gen->ym, !strcmp(config_dac, "zero_offset"));
 }
 
 static void config_updated(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
 	setup_io_devices(config, &system->info, &gen->io);
-	set_gain_config(gen);
+	set_audio_config(gen);
 }
 
 genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t system_opts, uint8_t force_region)
@@ -1402,7 +1405,7 @@
 	gen->psg = malloc(sizeof(psg_context));
 	psg_init(gen->psg, gen->master_clock, MCLKS_PER_PSG);
 	
-	set_gain_config(gen);
+	set_audio_config(gen);
 
 	z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES);
 #ifndef NO_Z80
--- a/nuklear_ui/blastem_nuklear.c	Sun Mar 24 13:31:22 2019 -0700
+++ b/nuklear_ui/blastem_nuklear.c	Sun Mar 24 19:59:41 2019 -0700
@@ -1668,13 +1668,24 @@
 		"128",
 		"64"
 	};
+	const char *dac[] = {
+		"zero_offset",
+		"linear"
+	};
+	const char *dac_desc[] = {
+		"Zero Offset",
+		"Linear"
+	};
 	const uint32_t num_rates = sizeof(rates)/sizeof(*rates);
 	const uint32_t num_sizes = sizeof(sizes)/sizeof(*sizes);
+	const uint32_t num_dacs = sizeof(dac)/sizeof(*dac);
 	static int32_t selected_rate = -1;
 	static int32_t selected_size = -1;
-	if (selected_rate < 0 || selected_size < 0) {
+	static int32_t selected_dac = -1;
+	if (selected_rate < 0 || selected_size < 0 || selected_dac < 0) {
 		selected_rate = find_match(rates, num_rates, "autio\0rate\0", "48000");
 		selected_size = find_match(sizes, num_sizes, "audio\0buffer\0", "512");
+		selected_dac = find_match(dac, num_dacs, "audio\0fm_dac\0", "zero_offset");
 	}
 	uint32_t width = render_width();
 	uint32_t height = render_height();
@@ -1690,6 +1701,7 @@
 		settings_float_property(context, "Gain", "Overall", "audio\0gain\0", 0, -30.0f, 30.0f, 0.5f);
 		settings_float_property(context, "", "FM", "audio\0fm_gain\0", 0, -30.0f, 30.0f, 0.5f);
 		settings_float_property(context, "", "PSG", "audio\0psg_gain\0", 0, -30.0f, 30.0f, 0.5f);
+		selected_dac = settings_dropdown_ex(context, "FM DAC", dac, dac_desc, num_dacs, selected_dac, "audio\0fm_dac\0");
 		if (nk_button_label(context, "Back")) {
 			pop_view();
 		}
--- a/ym2612.c	Sun Mar 24 13:31:22 2019 -0700
+++ b/ym2612.c	Sun Mar 24 19:59:41 2019 -0700
@@ -256,6 +256,7 @@
 		}
 	}
 	ym_reset(context);
+	ym_enable_zero_offset(context, 1);
 }
 
 void ym_free(ym2612_context *context)
@@ -267,8 +268,18 @@
 	free(context);
 }
 
-#define YM_VOLUME_MULTIPLIER 2
-#define YM_VOLUME_DIVIDER 3
+void ym_enable_zero_offset(ym2612_context *context, uint8_t enabled)
+{
+	if (enabled) {
+		context->zero_offset = 0x70;
+		context->volume_mult = 79;
+		context->volume_div = 120;
+	} else {
+		context->zero_offset = 0;
+		context->volume_mult = 2;
+		context->volume_div = 3;
+	}
+}
 #define YM_MOD_SHIFT 1
 
 #define CSM_MODE 0x80
@@ -549,7 +560,7 @@
 					if (value & 0x2000) {
 						value |= 0xC000;
 					}
-					dfprintf(debug_file, "channel %d output: %d\n", channel, (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER);
+					dfprintf(debug_file, "channel %d output: %d\n", channel, (value * context->volume_mult) / context->volume_div);
 				}
 			}
 			//puts("operator update done");
@@ -571,14 +582,19 @@
 						value |= 0xC000;
 					}
 				}
+				if (value >= 0) {
+					value += context->zero_offset;
+				} else {
+					value -= context->zero_offset;
+				}
 				if (context->channels[i].logfile) {
 					fwrite(&value, sizeof(value), 1, context->channels[i].logfile);
 				}
 				if (context->channels[i].lr & 0x80) {
-					left += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER;
+					left += (value * context->volume_mult) / context->volume_div;
 				}
 				if (context->channels[i].lr & 0x40) {
-					right += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER;
+					right += (value * context->volume_mult) / context->volume_div;
 				}
 			}
 			render_put_stereo_sample(context->audio, left, right);
--- a/ym2612.h	Sun Mar 24 13:31:22 2019 -0700
+++ b/ym2612.h	Sun Mar 24 19:59:41 2019 -0700
@@ -70,9 +70,11 @@
 	//TODO: Condense the next two fields into one
 	uint32_t    write_cycle;
 	uint32_t    busy_cycles;
-	uint32_t    lowpass_alpha;
+	int32_t     volume_mult;
+	int32_t     volume_div;
 	ym_operator operators[NUM_OPERATORS];
 	ym_channel  channels[NUM_CHANNELS];
+	int16_t     zero_offset;
 	uint16_t    timer_a;
 	uint16_t    timer_a_load;
 	uint16_t    env_counter;
@@ -128,6 +130,7 @@
 void ym_init(ym2612_context * context, uint32_t master_clock, uint32_t clock_div, uint32_t options);
 void ym_reset(ym2612_context *context);
 void ym_free(ym2612_context *context);
+void ym_enable_zero_offset(ym2612_context *context, uint8_t enabled);
 void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock);
 void ym_run(ym2612_context * context, uint32_t to_cycle);
 void ym_address_write_part1(ym2612_context * context, uint8_t address);