summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2018-03-30 00:37:08 -0700
committerMichael Pavone <pavone@retrodev.com>2018-03-30 00:37:08 -0700
commit6409b9654c78328d03c4ced4f4b651a303497873 (patch)
tree205d01d6118297ffa1570f3cacb4306b7ee7cdf8
parent431054f4686cd2a9fffa6f64751f81cdf0b39a89 (diff)
More audio refactoring in preparation for allowing proper sync to video with dynamic audio rate control
-rw-r--r--config.c7
-rw-r--r--config.h1
-rw-r--r--genesis.c7
-rw-r--r--psg.c42
-rw-r--r--psg.h11
-rw-r--r--render.h8
-rwxr-xr-xrender_sdl.c111
-rw-r--r--sms.c5
-rw-r--r--ym2612.c49
-rw-r--r--ym2612.h10
10 files changed, 121 insertions, 130 deletions
diff --git a/config.c b/config.c
index 16e149b..9595313 100644
--- a/config.c
+++ b/config.c
@@ -276,3 +276,10 @@ char **get_extension_list(tern_node *config, uint32_t *num_exts_out)
*num_exts_out = num_exts;
return ext_list;
}
+
+#define DEFAULT_LOWPASS_CUTOFF 3390
+uint32_t get_lowpass_cutoff(tern_node *config)
+{
+ char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
+ return lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
+}
diff --git a/config.h b/config.h
index 5122577..d72051c 100644
--- a/config.h
+++ b/config.h
@@ -14,6 +14,7 @@ char *serialize_config(tern_node *config, uint32_t *size_out);
uint8_t serialize_config_file(tern_node *config, char *path);
void persist_config(tern_node *config);
char **get_extension_list(tern_node *config, uint32_t *num_exts_out);
+uint32_t get_lowpass_cutoff(tern_node *config);
#endif //CONFIG_H_
diff --git a/genesis.c b/genesis.c
index e4f4a8f..7dc84b1 100644
--- a/genesis.c
+++ b/genesis.c
@@ -1219,15 +1219,12 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on
gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL;
gen->int_latency_prev1 = MCLKS_PER_68K * 32;
gen->int_latency_prev2 = MCLKS_PER_68K * 16;
-
- char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
- uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
gen->ym = malloc(sizeof(ym2612_context));
- ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), system_opts, lowpass_cutoff);
+ ym_init(gen->ym, gen->master_clock, MCLKS_PER_YM, system_opts);
gen->psg = malloc(sizeof(psg_context));
- psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff);
+ psg_init(gen->psg, gen->master_clock, MCLKS_PER_PSG);
gen->zram = calloc(1, Z80_RAM_BYTES);
z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES);
diff --git a/psg.c b/psg.c
index 71c925a..a788487 100644
--- a/psg.c
+++ b/psg.c
@@ -10,19 +10,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
-void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame, uint32_t lowpass_cutoff)
+void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div)
{
memset(context, 0, sizeof(*context));
- context->audio = render_audio_source(1);
- context->audio_buffer = render_audio_source_buffer(context->audio);
+ context->audio = render_audio_source(master_clock, clock_div, 1);
context->clock_inc = clock_div;
- context->sample_rate = sample_rate;
- context->samples_frame = samples_frame;
- double rc = (1.0 / (double)lowpass_cutoff) / (2.0 * M_PI);
- double dt = 1.0 / ((double)master_clock / (double)clock_div);
- double alpha = dt / (dt + rc);
- context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
- psg_adjust_master_clock(context, master_clock);
for (int i = 0; i < 4; i++) {
context->volume[i] = 0xF;
}
@@ -34,12 +26,9 @@ void psg_free(psg_context *context)
free(context);
}
-#define BUFFER_INC_RES 0x40000000UL
-
void psg_adjust_master_clock(psg_context * context, uint32_t master_clock)
{
- uint64_t old_inc = context->buffer_inc;
- context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc;
+ render_audio_adjust_clock(context->audio, master_clock, context->clock_inc);
}
void psg_write(psg_context * context, uint8_t value)
@@ -117,34 +106,19 @@ void psg_run(psg_context * context, uint32_t cycles)
}
}
- context->last_sample = context->accum;
- context->accum = 0;
+ int16_t accum = 0;
for (int i = 0; i < 3; i++) {
if (context->output_state[i]) {
- context->accum += volume_table[context->volume[i]];
+ accum += volume_table[context->volume[i]];
}
}
if (context->noise_out) {
- context->accum += volume_table[context->volume[3]];
+ accum += volume_table[context->volume[3]];
}
- int32_t tmp = context->accum * context->lowpass_alpha + context->last_sample * (0x10000 - context->lowpass_alpha);
- context->accum = tmp >> 16;
+
+ render_put_mono_sample(context->audio, accum);
- context->buffer_fraction += context->buffer_inc;
- while (context->buffer_fraction >= BUFFER_INC_RES) {
- context->buffer_fraction -= BUFFER_INC_RES;
- int32_t tmp = context->last_sample * ((context->buffer_fraction << 16) / context->buffer_inc);
- tmp += context->accum * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
- context->audio_buffer[context->buffer_pos++] = tmp >> 16;
-
- if (context->buffer_pos == context->samples_frame) {
- if (!headless) {
- context->audio_buffer = render_audio_ready(context->audio);
- context->buffer_pos = 0;
- }
- }
- }
context->cycles += context->clock_inc;
}
}
diff --git a/psg.h b/psg.h
index 4e77f20..c4648e8 100644
--- a/psg.h
+++ b/psg.h
@@ -11,21 +11,12 @@
#include "render.h"
typedef struct {
- int16_t *audio_buffer;
audio_source *audio;
- uint64_t buffer_fraction;
- uint64_t buffer_inc;
- uint32_t buffer_pos;
uint32_t clock_inc;
uint32_t cycles;
- uint32_t sample_rate;
- uint32_t samples_frame;
- int32_t lowpass_alpha;
uint16_t lsfr;
uint16_t counter_load[4];
uint16_t counters[4];
- int16_t accum;
- int16_t last_sample;
uint8_t volume[4];
uint8_t output_state[4];
uint8_t noise_out;
@@ -35,7 +26,7 @@ typedef struct {
} psg_context;
-void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame, uint32_t lowpass_cutoff);
+void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div);
void psg_free(psg_context *context);
void psg_adjust_master_clock(psg_context * context, uint32_t master_clock);
void psg_write(psg_context * context, uint8_t value);
diff --git a/render.h b/render.h
index ac4b62b..de788f1 100644
--- a/render.h
+++ b/render.h
@@ -117,10 +117,10 @@ uint32_t render_overscan_left();
uint32_t render_elapsed_ms(void);
void render_sleep_ms(uint32_t delay);
uint8_t render_has_gl(void);
-audio_source *render_audio_source(uint8_t channels);
-int16_t *render_audio_source_buffer(audio_source *src);
-int16_t *render_audio_ready(audio_source *src);
-void render_reset_sources(void);
+audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels);
+void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider);
+void render_put_mono_sample(audio_source *src, int16_t value);
+void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right);
void render_pause_source(audio_source *src);
void render_resume_source(audio_source *src);
void render_free_source(audio_source *src);
diff --git a/render_sdl.c b/render_sdl.c
index 0f3fb76..f9001d7 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -15,6 +15,7 @@
#include "util.h"
#include "ppm.h"
#include "png.h"
+#include "config.h"
#ifndef DISABLE_OPENGL
#include <GL/glew.h>
@@ -50,6 +51,12 @@ struct audio_source {
SDL_cond *cond;
int16_t *front;
int16_t *back;
+ uint64_t buffer_fraction;
+ uint64_t buffer_inc;
+ uint32_t buffer_pos;
+ uint32_t lowpass_alpha;
+ int16_t last_left;
+ int16_t last_right;
uint8_t num_channels;
uint8_t front_populated;
};
@@ -161,7 +168,14 @@ static void render_close_audio()
SDL_CloseAudio();
}
-audio_source *render_audio_source(uint8_t channels)
+#define BUFFER_INC_RES 0x40000000UL
+
+void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
+{
+ src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
+}
+
+audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
{
audio_source *ret = NULL;
SDL_LockMutex(audio_mutex);
@@ -177,6 +191,16 @@ audio_source *render_audio_source(uint8_t channels)
SDL_UnlockMutex(audio_mutex);
if (!ret) {
fatal_error("Too many audio sources!");
+ } else {
+ render_audio_adjust_clock(ret, master_clock, sample_divider);
+ double lowpass_cutoff = get_lowpass_cutoff(config);
+ double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
+ double dt = 1.0 / ((double)master_clock / (double)(sample_divider));
+ double alpha = dt / (dt + rc);
+ ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
+ ret->buffer_pos = 0;
+ ret->buffer_fraction = 0;
+ ret->last_left = ret->last_right = 0;
}
return ret;
}
@@ -214,6 +238,71 @@ void render_free_source(audio_source *src)
free(src);
}
+static void do_audio_ready(audio_source *src)
+{
+ SDL_LockMutex(audio_mutex);
+ while (src->front_populated) {
+ SDL_CondWait(src->cond, audio_mutex);
+ }
+ int16_t *tmp = src->front;
+ src->front = src->back;
+ src->back = tmp;
+ src->front_populated = 1;
+ src->buffer_pos = 0;
+ SDL_CondSignal(audio_ready);
+ SDL_UnlockMutex(audio_mutex);
+}
+
+static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current)
+{
+ int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha);
+ current = tmp >> 16;
+ return current;
+}
+
+static void interp_sample(audio_source *src, int16_t last, int16_t current)
+{
+ int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc);
+ tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc));
+ src->back[src->buffer_pos++] = tmp >> 16;
+}
+
+void render_put_mono_sample(audio_source *src, int16_t value)
+{
+ value = lowpass_sample(src, src->last_left, value);
+ src->buffer_fraction += src->buffer_inc;
+ while (src->buffer_fraction > BUFFER_INC_RES)
+ {
+ src->buffer_fraction -= BUFFER_INC_RES;
+ interp_sample(src, src->last_left, value);
+
+ if (src->buffer_pos == buffer_samples) {
+ do_audio_ready(src);
+ }
+ }
+ src->last_left = value;
+}
+
+void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right)
+{
+ left = lowpass_sample(src, src->last_left, left);
+ right = lowpass_sample(src, src->last_right, right);
+ src->buffer_fraction += src->buffer_inc;
+ while (src->buffer_fraction > BUFFER_INC_RES)
+ {
+ src->buffer_fraction -= BUFFER_INC_RES;
+
+ interp_sample(src, src->last_left, left);
+ interp_sample(src, src->last_right, right);
+
+ if (src->buffer_pos == buffer_samples * 2) {
+ do_audio_ready(src);
+ }
+ }
+ src->last_left = left;
+ src->last_right = left;
+}
+
static SDL_Joystick * joysticks[MAX_JOYSTICKS];
static int joystick_sdl_index[MAX_JOYSTICKS];
@@ -1300,26 +1389,6 @@ void render_toggle_fullscreen()
in_toggle = 0;
}
-int16_t *render_audio_source_buffer(audio_source *src)
-{
- return src->back;
-}
-
-int16_t *render_audio_ready(audio_source *src)
-{
- SDL_LockMutex(audio_mutex);
- while (src->front_populated) {
- SDL_CondWait(src->cond, audio_mutex);
- }
- int16_t *tmp = src->front;
- src->front = src->back;
- src->back = tmp;
- src->front_populated = 1;
- SDL_CondSignal(audio_ready);
- SDL_UnlockMutex(audio_mutex);
- return src->back;
-}
-
uint32_t render_audio_buffer()
{
return buffer_samples;
diff --git a/sms.c b/sms.c
index c0fb1e3..b384e8b 100644
--- a/sms.c
+++ b/sms.c
@@ -513,14 +513,11 @@ sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t for
sms->bank_regs[3] = 0x8000 >> 14;
}
- char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
- uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390;
-
//TODO: Detect region and pick master clock based off of that
sms->normal_clock = sms->master_clock = 53693175;
sms->psg = malloc(sizeof(psg_context));
- psg_init(sms->psg, render_sample_rate(), sms->master_clock, 15*16, render_audio_buffer(), lowpass_cutoff);
+ psg_init(sms->psg, sms->master_clock, 15*16);
sms->vdp = malloc(sizeof(vdp_context));
init_vdp_context(sms->vdp, 0);
diff --git a/ym2612.c b/ym2612.c
index 11f4e19..15482fc 100644
--- a/ym2612.c
+++ b/ym2612.c
@@ -116,12 +116,10 @@ static void ym_finalize_log()
}
log_context = NULL;
}
-#define BUFFER_INC_RES 0x40000000UL
void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock)
{
- uint64_t old_inc = context->buffer_inc;
- context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc * NUM_OPERATORS;
+ render_audio_adjust_clock(context->audio, master_clock, context->clock_inc * NUM_OPERATORS);
}
#ifdef __ANDROID__
@@ -163,23 +161,13 @@ void ym_reset(ym2612_context *context)
}
}
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options, uint32_t lowpass_cutoff)
+void ym_init(ym2612_context * context, uint32_t master_clock, uint32_t clock_div, uint32_t options)
{
static uint8_t registered_finalize;
dfopen(debug_file, "ym_debug.txt", "w");
memset(context, 0, sizeof(*context));
- context->audio = render_audio_source(2);
- context->audio_buffer = render_audio_source_buffer(context->audio);
- context->sample_rate = sample_rate;
context->clock_inc = clock_div * 6;
- ym_adjust_master_clock(context, master_clock);
-
- double rc = (1.0 / (double)lowpass_cutoff) / (2.0 * M_PI);
- double dt = 1.0 / ((double)master_clock / (double)(context->clock_inc * NUM_OPERATORS));
- double alpha = dt / (dt + rc);
- context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
-
- context->sample_limit = sample_limit*2;
+ context->audio = render_audio_source(master_clock, context->clock_inc * NUM_OPERATORS, 2);
//some games seem to expect that the LR flags start out as 1
for (int i = 0; i < NUM_CHANNELS; i++) {
@@ -191,7 +179,7 @@ void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clo
fprintf(stderr, "Failed to open WAVE log file %s for writing\n", fname);
continue;
}
- if (!wave_init(f, sample_rate, 16, 1)) {
+ if (!wave_init(f, master_clock / (context->clock_inc * NUM_OPERATORS), 16, 1)) {
fclose(f);
context->channels[i].logfile = NULL;
}
@@ -604,7 +592,6 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
if (context->current_op == NUM_OPERATORS) {
context->current_op = 0;
- context->buffer_fraction += context->buffer_inc;
int16_t left = 0, right = 0;
for (int i = 0; i < NUM_CHANNELS; i++) {
int16_t value = context->channels[i].output;
@@ -618,7 +605,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
value |= 0xC000;
}
}
- if (context->channels[i].logfile && context->buffer_fraction > BUFFER_INC_RES) {
+ if (context->channels[i].logfile) {
fwrite(&value, sizeof(value), 1, context->channels[i].logfile);
}
if (context->channels[i].lr & 0x80) {
@@ -628,31 +615,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
right += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER;
}
}
- int32_t tmp = left * context->lowpass_alpha + context->last_left * (0x10000 - context->lowpass_alpha);
- left = tmp >> 16;
- tmp = right * context->lowpass_alpha + context->last_right * (0x10000 - context->lowpass_alpha);
- right = tmp >> 16;
- while (context->buffer_fraction > BUFFER_INC_RES) {
- context->buffer_fraction -= BUFFER_INC_RES;
-
- int64_t tmp = context->last_left * ((context->buffer_fraction << 16) / context->buffer_inc);
- tmp += left * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
- context->audio_buffer[context->buffer_pos] = tmp >> 16;
-
- tmp = context->last_right * ((context->buffer_fraction << 16) / context->buffer_inc);
- tmp += right * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
- context->audio_buffer[context->buffer_pos+1] = tmp >> 16;
-
- context->buffer_pos += 2;
- if (context->buffer_pos == context->sample_limit) {
- if (!headless) {
- context->audio_buffer = render_audio_ready(context->audio);
- context->buffer_pos = 0;
- }
- }
- }
- context->last_left = left;
- context->last_right = right;
+ render_put_stereo_sample(context->audio, left, right);
}
}
diff --git a/ym2612.h b/ym2612.h
index 8bda299..1b12285 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -63,14 +63,8 @@ typedef struct {
#define YM_PART2_REGS (YM_REG_END-YM_PART2_START)
typedef struct {
- int16_t *audio_buffer;
audio_source *audio;
- uint64_t buffer_fraction;
- uint64_t buffer_inc;
uint32_t clock_inc;
- uint32_t buffer_pos;
- uint32_t sample_rate;
- uint32_t sample_limit;
uint32_t current_cycle;
//TODO: Condense the next two fields into one
uint32_t write_cycle;
@@ -82,8 +76,6 @@ typedef struct {
uint16_t timer_a_load;
uint16_t env_counter;
ym_supp ch3_supp[3];
- int16_t last_left;
- int16_t last_right;
uint8_t timer_b;
uint8_t sub_timer_b;
uint8_t timer_b_load;
@@ -132,7 +124,7 @@ enum {
REG_LR_AMS_PMS = 0xB4
};
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options, uint32_t lowpass_cutoff);
+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_adjust_master_clock(ym2612_context * context, uint32_t master_clock);