summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2019-03-23 17:18:10 -0700
committerMichael Pavone <pavone@retrodev.com>2019-03-23 17:18:10 -0700
commit1e9fd28a1ee92dce1bd9e6e7cdeb8fed880948d9 (patch)
tree154c06b129874b3a08d2b044ac5ac7f25b38f4a1
parenta2eed4d72417b2d2831310c93c142bc45beece39 (diff)
Configurable gain for overall output and individual components
-rw-r--r--genesis.c12
-rw-r--r--nuklear_ui/blastem_nuklear.c18
-rw-r--r--render.h1
-rwxr-xr-xrender_sdl.c134
-rw-r--r--sms.c9
5 files changed, 116 insertions, 58 deletions
diff --git a/genesis.c b/genesis.c
index 426dac1..5d28c5f 100644
--- a/genesis.c
+++ b/genesis.c
@@ -1334,10 +1334,20 @@ static void keyboard_up(system_header *system, uint8_t scancode)
io_keyboard_up(&gen->io, scancode);
}
+static void set_gain_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);
+}
+
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);
}
genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t system_opts, uint8_t force_region)
@@ -1391,6 +1401,8 @@ 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);
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 4ab8586..9bb1711 100644
--- a/nuklear_ui/blastem_nuklear.c
+++ b/nuklear_ui/blastem_nuklear.c
@@ -1438,6 +1438,21 @@ void settings_int_property(struct nk_context *context, char *label, char *name,
}
}
+void settings_float_property(struct nk_context *context, char *label, char *name, char *path, float def, float min, float max, float step)
+{
+ char *curstr = tern_find_path(config, path, TVAL_PTR).ptrval;
+ float curval = curstr ? atof(curstr) : def;
+ nk_label(context, label, NK_TEXT_LEFT);
+ float val = curval;
+ nk_property_float(context, name, min, &val, max, step, step);
+ if (val != curval) {
+ char buffer[64];
+ sprintf(buffer, "%f", val);
+ config_dirty = 1;
+ config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR);
+ }
+}
+
typedef struct {
char *fragment;
char *vertex;
@@ -1672,6 +1687,9 @@ void view_audio_settings(struct nk_context *context)
selected_rate = settings_dropdown(context, "Rate in Hz", rates, num_rates, selected_rate, "audio\0rate\0");
selected_size = settings_dropdown(context, "Buffer Samples", sizes, num_sizes, selected_size, "audio\0buffer\0");
settings_int_input(context, "Lowpass Cutoff Hz", "audio\0lowpass_cutoff\0", "3390");
+ 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);
if (nk_button_label(context, "Back")) {
pop_view();
}
diff --git a/render.h b/render.h
index 06ac26c..62d857e 100644
--- a/render.h
+++ b/render.h
@@ -131,6 +131,7 @@ uint32_t render_elapsed_ms(void);
void render_sleep_ms(uint32_t delay);
uint8_t render_has_gl(void);
audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels);
+void render_audio_source_gaindb(audio_source *src, float gain);
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);
diff --git a/render_sdl.c b/render_sdl.c
index 30894b1..7f24b0d 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -61,6 +61,7 @@ struct audio_source {
double dt;
uint64_t buffer_fraction;
uint64_t buffer_inc;
+ float gain_mult;
uint32_t buffer_pos;
uint32_t read_start;
uint32_t read_end;
@@ -78,77 +79,72 @@ static uint8_t num_audio_sources;
static uint8_t num_inactive_audio_sources;
static uint8_t sync_to_audio;
static uint32_t min_buffered;
+static float overall_gain_mult, *mix_buf;
+static int sample_size;
-typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len);
+typedef void (*conv_func)(float *samples, void *vstream, int sample_count);
-static int32_t mix_s16(audio_source *audio, void *vstream, int len)
+static void convert_null(float *samples, void *vstream, int sample_count)
+{
+ memset(vstream, 0, sample_count * sample_size);
+}
+
+static void convert_s16(float *samples, void *vstream, int sample_count)
{
- int samples = len/(sizeof(int16_t)*2);
int16_t *stream = vstream;
- int16_t *end = stream + output_channels*samples;
- int16_t *src = audio->front;
- uint32_t i = audio->read_start;
- uint32_t i_end = audio->read_end;
- int16_t *cur = stream;
- size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1;
- if (audio->num_channels == 1) {
- while (cur < end && i != i_end)
- {
- *cur += src[i];
- cur += first_add;
- *cur += src[i++];
- cur += second_add;
- i &= audio->mask;
- }
- } else {
- while (cur < end && i != i_end)
- {
- *cur += src[i++];
- cur += first_add;
- *cur += src[i++];
- cur += second_add;
- i &= audio->mask;
+ for (int16_t *end = stream + sample_count; stream < end; stream++, samples++)
+ {
+ float sample = *samples;
+ int16_t out_sample;
+ if (sample >= 1.0f) {
+ out_sample = 0x7FFF;
+ } else if (sample <= -1.0f) {
+ out_sample = -0x8000;
+ } else {
+ out_sample = sample * 0x7FFF;
}
+ *stream = out_sample;
}
-
- if (cur != end) {
- debug_message("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
- }
- if (!sync_to_audio) {
- audio->read_start = i;
- }
- if (cur != end) {
- return (cur-end)/2;
- } else {
- return ((i_end - i) & audio->mask) / audio->num_channels;
+}
+
+static void clamp_f32(float *samples, void *vstream, int sample_count)
+{
+ for (; sample_count > 0; sample_count--, samples++)
+ {
+ float sample = *samples;
+ if (sample > 1.0f) {
+ sample = 1.0f;
+ } else if (sample < -1.0f) {
+ sample = -1.0f;
+ }
+ *samples = sample;
}
}
-static int32_t mix_f32(audio_source *audio, void *vstream, int len)
+static int32_t mix_f32(audio_source *audio, float *stream, int samples)
{
- int samples = len/(sizeof(float)*2);
- float *stream = vstream;
- float *end = stream + 2*samples;
+ float *end = stream + samples;
int16_t *src = audio->front;
uint32_t i = audio->read_start;
uint32_t i_end = audio->read_end;
float *cur = stream;
+ float gain_mult = audio->gain_mult * overall_gain_mult;
size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1;
if (audio->num_channels == 1) {
while (cur < end && i != i_end)
{
- *cur += ((float)src[i]) / 0x7FFF;
+ *cur += gain_mult * ((float)src[i]) / 0x7FFF;
cur += first_add;
- *cur += ((float)src[i++]) / 0x7FFF;
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
cur += second_add;
i &= audio->mask;
}
} else {
while(cur < end && i != i_end)
{
- *cur += ((float)src[i++]) / 0x7FFF;
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
cur += first_add;
- *cur += ((float)src[i++]) / 0x7FFF;
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
cur += second_add;
i &= audio->mask;
}
@@ -164,17 +160,15 @@ static int32_t mix_f32(audio_source *audio, void *vstream, int len)
}
}
-static int32_t mix_null(audio_source *audio, void *vstream, int len)
-{
- return 0;
-}
-
-static mix_func mix;
+static conv_func convert;
static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
{
uint8_t num_populated;
- memset(byte_stream, 0, len);
+ float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream;
+
+ int samples = len / sample_size;
+ memset(mix_dest, 0, samples * sizeof(float));
SDL_LockMutex(audio_mutex);
do {
num_populated = 0;
@@ -192,12 +186,13 @@ static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
if (!quitting) {
for (uint8_t i = 0; i < num_audio_sources; i++)
{
- mix(audio_sources[i], byte_stream, len);
+ mix_f32(audio_sources[i], mix_dest, samples);
audio_sources[i]->front_populated = 0;
SDL_CondSignal(audio_sources[i]->cond);
}
}
SDL_UnlockMutex(audio_mutex);
+ convert(mix_dest, byte_stream, samples);
}
#define NO_LAST_BUFFERED -2000000000
@@ -210,21 +205,24 @@ static int32_t cur_min_buffered;
static uint32_t min_remaining_buffer;
static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len)
{
- memset(byte_stream, 0, len);
if (cur_min_buffered < 0) {
//underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet
return;
}
cur_min_buffered = 0x7FFFFFFF;
min_remaining_buffer = 0xFFFFFFFF;
+ float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream;
+ int samples = len / sample_size;
+ memset(mix_dest, 0, samples * sizeof(float));
for (uint8_t i = 0; i < num_audio_sources; i++)
{
- int32_t buffered = mix(audio_sources[i], byte_stream, len);
+ int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples);
cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered;
uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered;
min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer;
}
+ convert(mix_dest, byte_stream, samples);
}
static void lock_audio()
@@ -250,6 +248,10 @@ static void render_close_audio()
SDL_LockMutex(audio_mutex);
quitting = 1;
SDL_CondSignal(audio_ready);
+ if (mix_buf) {
+ free(mix_buf);
+ mix_buf = NULL;
+ }
SDL_UnlockMutex(audio_mutex);
SDL_CloseAudio();
}
@@ -298,6 +300,16 @@ audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider
return ret;
}
+static float db_to_mult(float gain)
+{
+ return powf(10.0f, gain/20.0f);
+}
+
+void render_audio_source_gaindb(audio_source *src, float gain)
+{
+ src->gain_mult = db_to_mult(gain);
+}
+
void render_pause_source(audio_source *src)
{
uint8_t need_pause = 0;
@@ -1044,17 +1056,23 @@ static void init_audio()
sample_rate = actual.freq;
output_channels = actual.channels;
debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples);
+ sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8;
if (actual.format == AUDIO_S16SYS) {
debug_message("signed 16-bit int format");
- mix = mix_s16;
+ convert = convert_s16;
+ mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
} else if (actual.format == AUDIO_F32SYS) {
debug_message("32-bit float format");
- mix = mix_f32;
+ convert = clamp_f32;
+ mix_buf = NULL;
} else {
debug_message("unsupported format %X\n", actual.format);
warning("Unsupported audio sample format: %X\n", actual.format);
- mix = mix_null;
+ convert = convert_null;
+ mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
}
+ char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval;
+ overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f);
}
void window_setup(void)
diff --git a/sms.c b/sms.c
index 1f9b888..cc33044 100644
--- a/sms.c
+++ b/sms.c
@@ -569,6 +569,13 @@ static void keyboard_up(system_header *system, uint8_t scancode)
io_keyboard_up(&sms->io, scancode);
}
+static void set_gain_config(sms_context *sms)
+{
+ char *config_gain;
+ config_gain = tern_find_path(config, "audio\0psg_gain\0", TVAL_PTR).ptrval;
+ render_audio_source_gaindb(sms->psg->audio, config_gain ? atof(config_gain) : 0.0f);
+}
+
static void config_updated(system_header *system)
{
sms_context *sms = (sms_context *)system;
@@ -620,6 +627,8 @@ sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t for
sms->psg = malloc(sizeof(psg_context));
psg_init(sms->psg, sms->master_clock, 15*16);
+ set_gain_config(sms);
+
sms->vdp = init_vdp_context(0);
sms->vdp->system = &sms->header;