From c1597a8f0a63adbf7018a05a35d0c77d7bccd0c3 Mon Sep 17 00:00:00 2001 From: Michael Pavone Date: Sun, 24 Mar 2019 19:59:41 -0700 Subject: Optionally emulate the offset around zero in the imperfect DAC of a discrete YM2612 --- genesis.c | 9 ++++++--- nuklear_ui/blastem_nuklear.c | 14 +++++++++++++- ym2612.c | 26 +++++++++++++++++++++----- ym2612.h | 5 ++++- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/genesis.c b/genesis.c index 5d28c5f..99dce0f 100644 --- a/genesis.c +++ b/genesis.c @@ -1334,20 +1334,23 @@ static void keyboard_up(system_header *system, uint8_t scancode) 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 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on 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 diff --git a/nuklear_ui/blastem_nuklear.c b/nuklear_ui/blastem_nuklear.c index 9bb1711..25b5a85 100644 --- a/nuklear_ui/blastem_nuklear.c +++ b/nuklear_ui/blastem_nuklear.c @@ -1668,13 +1668,24 @@ void view_audio_settings(struct nk_context *context) "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 @@ void view_audio_settings(struct nk_context *context) 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(); } diff --git a/ym2612.c b/ym2612.c index 10cb0a0..71ee0d0 100644 --- a/ym2612.c +++ b/ym2612.c @@ -256,6 +256,7 @@ void ym_init(ym2612_context * context, uint32_t master_clock, uint32_t clock_div } } ym_reset(context); + ym_enable_zero_offset(context, 1); } void ym_free(ym2612_context *context) @@ -267,8 +268,18 @@ void ym_free(ym2612_context *context) 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 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) 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 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) 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); diff --git a/ym2612.h b/ym2612.h index 2781c94..1f34558 100644 --- a/ym2612.h +++ b/ym2612.h @@ -70,9 +70,11 @@ typedef struct { //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 @@ enum { 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); -- cgit v1.2.3