summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--psg.c1
-rw-r--r--psg.h2
-rw-r--r--render.h13
-rw-r--r--render_audio.c375
-rw-r--r--render_audio.h57
-rwxr-xr-xrender_sdl.c389
-rw-r--r--ym2612.h2
8 files changed, 483 insertions, 358 deletions
diff --git a/Makefile b/Makefile
index 5ef0f62..b38b31b 100644
--- a/Makefile
+++ b/Makefile
@@ -197,7 +197,7 @@ endif
AUDIOOBJS=ym2612.o psg.o wave.o
CONFIGOBJS=config.o tern.o util.o paths.o
NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o
-RENDEROBJS=ppm.o controller_info.o
+RENDEROBJS=ppm.o controller_info.o render_audio.o
ifdef USE_FBDEV
RENDEROBJS+= render_fbdev.o
else
diff --git a/psg.c b/psg.c
index a788487..ccf0779 100644
--- a/psg.c
+++ b/psg.c
@@ -4,7 +4,6 @@
BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
*/
#include "psg.h"
-#include "render.h"
#include "blastem.h"
#include <string.h>
#include <stdlib.h>
diff --git a/psg.h b/psg.h
index c4648e8..cad590d 100644
--- a/psg.h
+++ b/psg.h
@@ -8,7 +8,7 @@
#include <stdint.h>
#include "serialize.h"
-#include "render.h"
+#include "render_audio.h"
typedef struct {
audio_source *audio;
diff --git a/render.h b/render.h
index 5083b77..e6706f8 100644
--- a/render.h
+++ b/render.h
@@ -92,7 +92,6 @@ typedef enum {
#define RENDER_NOT_MAPPED -2
#define RENDER_NOT_PLUGGED_IN -3
-typedef struct audio_source audio_source;
typedef void (*drop_handler)(const char *filename);
typedef void (*window_close_handler)(uint8_t which);
typedef void (*ui_render_fun)(void);
@@ -109,9 +108,7 @@ void render_init(int width, int height, char * title, uint8_t fullscreen);
void render_set_video_standard(vid_std std);
void render_toggle_fullscreen();
void render_update_caption(char *title);
-void render_wait_quit(vdp_context * context);
-uint32_t render_audio_buffer();
-uint32_t render_sample_rate();
+void render_wait_quit(void);
void process_events();
int render_width();
int render_height();
@@ -133,14 +130,6 @@ 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(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);
-void render_pause_source(audio_source *src);
-void render_resume_source(audio_source *src);
-void render_free_source(audio_source *src);
void render_config_updated(void);
void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create);
void render_set_ui_render_fun(ui_render_fun);
diff --git a/render_audio.c b/render_audio.c
new file mode 100644
index 0000000..39a9ccd
--- /dev/null
+++ b/render_audio.c
@@ -0,0 +1,375 @@
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "render_audio.h"
+#include "util.h"
+#include "config.h"
+#include "blastem.h"
+
+static uint8_t output_channels;
+static uint32_t buffer_samples, sample_rate;
+static uint32_t missing_count;
+
+static audio_source *audio_sources[8];
+static audio_source *inactive_audio_sources[8];
+static uint8_t num_audio_sources;
+static uint8_t num_inactive_audio_sources;
+
+static float overall_gain_mult, *mix_buf;
+static int sample_size;
+
+typedef void (*conv_func)(float *samples, void *vstream, int sample_count);
+
+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)
+{
+ int16_t *stream = vstream;
+ 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;
+ }
+}
+
+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, float *stream, int 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 += gain_mult * ((float)src[i]) / 0x7FFF;
+ cur += first_add;
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
+ cur += second_add;
+ i &= audio->mask;
+ }
+ } else {
+ while(cur < end && i != i_end)
+ {
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
+ cur += first_add;
+ *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
+ cur += second_add;
+ i &= audio->mask;
+ }
+ }
+ if (!render_is_audio_sync()) {
+ audio->read_start = i;
+ }
+ 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);
+ return (cur-end)/2;
+ } else {
+ return ((i_end - i) & audio->mask) / audio->num_channels;
+ }
+}
+
+static conv_func convert;
+
+
+int mix_and_convert(unsigned char *byte_stream, int len, int *min_remaining_out)
+{
+ int samples = len / sample_size;
+ float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream;
+ memset(mix_dest, 0, samples * sizeof(float));
+ int min_buffered = INT_MAX;
+ int min_remaining_buffer = INT_MAX;
+ for (uint8_t i = 0; i < num_audio_sources; i++)
+ {
+ int buffered = mix_f32(audio_sources[i], mix_dest, samples);
+ int remaining = (audio_sources[i]->mask + 1) / audio_sources[i]->num_channels - buffered;
+ min_buffered = buffered < min_buffered ? buffered : min_buffered;
+ min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer;
+ audio_sources[i]->front_populated = 0;
+ render_buffer_consumed(audio_sources[i]);
+ }
+ convert(mix_dest, byte_stream, samples);
+ if (min_remaining_out) {
+ *min_remaining_out = min_remaining_buffer;
+ }
+ return min_buffered;
+}
+
+uint8_t all_sources_ready(void)
+{
+ uint8_t num_populated = 0;
+ num_populated = 0;
+ for (uint8_t i = 0; i < num_audio_sources; i++)
+ {
+ if (audio_sources[i]->front_populated) {
+ num_populated++;
+ }
+ }
+ return num_populated == num_audio_sources;
+}
+
+#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;
+}
+
+void render_audio_adjust_speed(float adjust_ratio)
+{
+ for (uint8_t i = 0; i < num_audio_sources; i++)
+ {
+ audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5;
+ }
+}
+
+audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
+{
+ audio_source *ret = NULL;
+ uint32_t alloc_size = render_is_audio_sync() ? channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * channels);
+ render_lock_audio();
+ if (num_audio_sources < 8) {
+ ret = calloc(1, sizeof(audio_source));
+ ret->back = malloc(alloc_size * sizeof(int16_t));
+ ret->front = render_is_audio_sync() ? malloc(alloc_size * sizeof(int16_t)) : ret->back;
+ ret->front_populated = 0;
+ ret->opaque = render_new_audio_opaque();
+ ret->num_channels = channels;
+ audio_sources[num_audio_sources++] = ret;
+ }
+ render_unlock_audio();
+ 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);
+ ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
+ double alpha = ret->dt / (ret->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;
+ ret->read_start = 0;
+ ret->read_end = render_is_audio_sync() ? buffer_samples * channels : 0;
+ ret->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1;
+ ret->gain_mult = 1.0f;
+ }
+ render_audio_created(ret);
+
+ 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 found = 0, remaining_sources;
+ render_lock_audio();
+ for (uint8_t i = 0; i < num_audio_sources; i++)
+ {
+ if (audio_sources[i] == src) {
+ audio_sources[i] = audio_sources[--num_audio_sources];
+ found = 1;
+ remaining_sources = num_audio_sources;
+ break;
+ }
+ }
+
+ render_unlock_audio();
+ if (found) {
+ render_source_paused(src, remaining_sources);
+ }
+ inactive_audio_sources[num_inactive_audio_sources++] = src;
+}
+
+void render_resume_source(audio_source *src)
+{
+ render_lock_audio();
+ if (num_audio_sources < 8) {
+ audio_sources[num_audio_sources++] = src;
+ }
+ render_unlock_audio();
+ for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
+ {
+ if (inactive_audio_sources[i] == src) {
+ inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
+ }
+ }
+ render_source_resumed(src);
+}
+
+void render_free_source(audio_source *src)
+{
+ render_pause_source(src);
+
+ free(src->front);
+ if (render_is_audio_sync()) {
+ free(src->back);
+ render_free_audio_opaque(src->opaque);
+ }
+ free(src);
+ num_inactive_audio_sources--;
+}
+
+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;
+}
+
+static uint32_t sync_samples;
+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;
+ uint32_t base = render_is_audio_sync() ? 0 : src->read_end;
+ while (src->buffer_fraction > BUFFER_INC_RES)
+ {
+ src->buffer_fraction -= BUFFER_INC_RES;
+ interp_sample(src, src->last_left, value);
+
+ if (((src->buffer_pos - base) & src->mask) >= sync_samples) {
+ render_do_audio_ready(src);
+ }
+ src->buffer_pos &= src->mask;
+ }
+ 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;
+ uint32_t base = render_is_audio_sync() ? 0 : src->read_end;
+ 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 - base) & src->mask)/2 >= sync_samples) {
+ render_do_audio_ready(src);
+ }
+ src->buffer_pos &= src->mask;
+ }
+ src->last_left = left;
+ src->last_right = right;
+}
+
+static void update_source(audio_source *src, double rc, uint8_t sync_changed)
+{
+ double alpha = src->dt / (src->dt + rc);
+ int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
+ src->lowpass_alpha = lowpass_alpha;
+ if (sync_changed) {
+ uint32_t alloc_size = render_is_audio_sync() ? src->num_channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * src->num_channels);
+ src->back = realloc(src->back, alloc_size * sizeof(int16_t));
+ if (render_is_audio_sync()) {
+ src->front = malloc(alloc_size * sizeof(int16_t));
+ } else {
+ free(src->front);
+ src->front = src->back;
+ }
+ src->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1;
+ src->read_start = 0;
+ src->read_end = render_is_audio_sync() ? buffer_samples * src->num_channels : 0;
+ src->buffer_pos = 0;
+ }
+}
+
+uint8_t old_audio_sync;
+void render_audio_initialized(render_audio_format format, uint32_t rate, uint8_t channels, uint32_t buffer_size, int sample_size_in)
+{
+ sample_rate = rate;
+ output_channels = channels;
+ buffer_samples = buffer_size;
+ sample_size = sample_size_in;
+ if (mix_buf) {
+ free(mix_buf);
+ mix_buf = NULL;
+ }
+ switch(format)
+ {
+ case RENDER_AUDIO_S16:
+ convert = convert_s16;
+ mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
+ break;
+ case RENDER_AUDIO_FLOAT:
+ convert = clamp_f32;
+ break;
+ case RENDER_AUDIO_UNKNOWN:
+ convert = convert_null;
+ mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
+ break;
+ }
+ uint32_t syncs = render_audio_syncs_per_sec();
+ if (syncs) {
+ sync_samples = rate / syncs;
+ } else {
+ sync_samples = buffer_samples;
+ }
+ 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);
+ uint8_t sync_changed = old_audio_sync != render_is_audio_sync();
+ old_audio_sync = render_is_audio_sync();
+ double lowpass_cutoff = get_lowpass_cutoff(config);
+ double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
+ render_lock_audio();
+ for (uint8_t i = 0; i < num_audio_sources; i++)
+ {
+ update_source(audio_sources[i], rc, sync_changed);
+ }
+ render_unlock_audio();
+ for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
+ {
+ update_source(inactive_audio_sources[i], rc, sync_changed);
+ }
+} \ No newline at end of file
diff --git a/render_audio.h b/render_audio.h
new file mode 100644
index 0000000..2facc34
--- /dev/null
+++ b/render_audio.h
@@ -0,0 +1,57 @@
+#ifndef RENDER_AUDIO_H_
+#define RENDER_AUDIO_H_
+
+#include <stdint.h>
+typedef enum {
+ RENDER_AUDIO_S16,
+ RENDER_AUDIO_FLOAT,
+ RENDER_AUDIO_UNKNOWN
+} render_audio_format;
+
+typedef struct {
+ void *opaque;
+ int16_t *front;
+ int16_t *back;
+ 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;
+ uint32_t lowpass_alpha;
+ uint32_t mask;
+ int16_t last_left;
+ int16_t last_right;
+ uint8_t num_channels;
+ uint8_t front_populated;
+} audio_source;
+
+//public interface
+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);
+void render_pause_source(audio_source *src);
+void render_resume_source(audio_source *src);
+void render_free_source(audio_source *src);
+//interface for render backends
+void render_audio_initialized(render_audio_format format, uint32_t rate, uint8_t channels, uint32_t buffer_size, int sample_size);
+int mix_and_convert(unsigned char *byte_stream, int len, int *min_remaining_out);
+uint8_t all_sources_ready(void);
+void render_audio_adjust_speed(float adjust_ratio);
+//to be implemented by render backend
+uint8_t render_is_audio_sync(void);
+void render_buffer_consumed(audio_source *src);
+void *render_new_audio_opaque(void);
+void render_free_audio_opaque(void *opaque);
+void render_lock_audio(void);
+void render_unlock_audio(void);
+uint32_t render_min_buffered(void);
+uint32_t render_audio_syncs_per_sec(void);
+void render_audio_created(audio_source *src);
+void render_do_audio_ready(audio_source *src);
+void render_source_paused(audio_source *src, uint8_t remaining_sources);
+void render_source_resumed(audio_source *src);
+#endif //RENDER_AUDIO_H_
diff --git a/render_sdl.c b/render_sdl.c
index c23944e..bf08f37 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -46,151 +46,41 @@ static uint8_t scanlines = 0;
static uint32_t last_frame = 0;
-static uint8_t output_channels;
-static uint32_t buffer_samples, sample_rate;
-static uint32_t missing_count;
-
static SDL_mutex * audio_mutex;
static SDL_cond * audio_ready;
static uint8_t quitting = 0;
-struct audio_source {
- SDL_cond *cond;
- int16_t *front;
- int16_t *back;
- 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;
- uint32_t lowpass_alpha;
- uint32_t mask;
- int16_t last_left;
- int16_t last_right;
- uint8_t num_channels;
- uint8_t front_populated;
-};
-
-static audio_source *audio_sources[8];
-static audio_source *inactive_audio_sources[8];
-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 void (*conv_func)(float *samples, void *vstream, int sample_count);
-static void convert_null(float *samples, void *vstream, int sample_count)
+uint32_t render_min_buffered(void)
{
- memset(vstream, 0, sample_count * sample_size);
+ return min_buffered;
}
-static void convert_s16(float *samples, void *vstream, int sample_count)
+uint8_t render_is_audio_sync(void)
{
- int16_t *stream = vstream;
- 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;
- }
+ return sync_to_audio;
}
-static void clamp_f32(float *samples, void *vstream, int sample_count)
+void render_buffer_consumed(audio_source *src)
{
- 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;
- }
+ SDL_CondSignal(src->opaque);
}
-static int32_t mix_f32(audio_source *audio, float *stream, int 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 += gain_mult * ((float)src[i]) / 0x7FFF;
- cur += first_add;
- *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
- cur += second_add;
- i &= audio->mask;
- }
- } else {
- while(cur < end && i != i_end)
- {
- *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
- cur += first_add;
- *cur += gain_mult * ((float)src[i++]) / 0x7FFF;
- cur += second_add;
- i &= audio->mask;
- }
- }
- if (!sync_to_audio) {
- audio->read_start = i;
- }
- 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);
- return (cur-end)/2;
- } else {
- return ((i_end - i) & audio->mask) / audio->num_channels;
- }
-}
-
-static conv_func convert;
-
static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
{
- uint8_t num_populated;
SDL_LockMutex(audio_mutex);
+ uint8_t all_ready;
do {
- num_populated = 0;
- for (uint8_t i = 0; i < num_audio_sources; i++)
- {
- if (audio_sources[i]->front_populated) {
- num_populated++;
- }
- }
- if (!quitting && num_populated < num_audio_sources) {
- fflush(stdout);
+ all_ready = all_sources_ready();
+ if (!quitting && !all_ready) {
SDL_CondWait(audio_ready, audio_mutex);
}
- } while(!quitting && num_populated < num_audio_sources);
- int samples = len / sample_size;
- float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream;
- memset(mix_dest, 0, samples * sizeof(float));
+ } while(!quitting && !all_ready);
if (!quitting) {
- for (uint8_t i = 0; i < num_audio_sources; i++)
- {
- mix_f32(audio_sources[i], mix_dest, samples);
- audio_sources[i]->front_populated = 0;
- SDL_CondSignal(audio_sources[i]->cond);
- }
+ mix_and_convert(byte_stream, len, NULL);
}
- convert(mix_dest, byte_stream, samples);
SDL_UnlockMutex(audio_mutex);
}
@@ -208,23 +98,10 @@ static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len)
//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_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);
+ cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer);
}
-static void lock_audio()
+void render_lock_audio()
{
if (sync_to_audio) {
SDL_LockMutex(audio_mutex);
@@ -233,7 +110,7 @@ static void lock_audio()
}
}
-static void unlock_audio()
+void render_unlock_audio()
{
if (sync_to_audio) {
SDL_UnlockMutex(audio_mutex);
@@ -249,127 +126,55 @@ static void render_close_audio()
SDL_CondSignal(audio_ready);
SDL_UnlockMutex(audio_mutex);
SDL_CloseAudio();
+ /*
+ FIXME: move this to render_audio.c
if (mix_buf) {
free(mix_buf);
mix_buf = NULL;
}
+ */
}
-#define BUFFER_INC_RES 0x40000000UL
-
-void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
+void *render_new_audio_opaque(void)
{
- src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
+ return SDL_CreateCond();
}
-audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
+void render_free_audio_opaque(void *opaque)
{
- audio_source *ret = NULL;
- uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels);
- lock_audio();
- if (num_audio_sources < 8) {
- ret = malloc(sizeof(audio_source));
- ret->back = malloc(alloc_size * sizeof(int16_t));
- ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back;
- ret->front_populated = 0;
- ret->cond = SDL_CreateCond();
- ret->num_channels = channels;
- audio_sources[num_audio_sources++] = ret;
- }
- unlock_audio();
- 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);
- ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
- double alpha = ret->dt / (ret->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;
- ret->read_start = 0;
- ret->read_end = sync_to_audio ? buffer_samples * channels : 0;
- ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1;
- ret->gain_mult = 1.0f;
- }
- if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
- SDL_PauseAudio(0);
- }
- 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);
+ SDL_DestroyCond(opaque);
}
-void render_pause_source(audio_source *src)
+void render_audio_created(audio_source *source)
{
- uint8_t need_pause = 0;
- lock_audio();
- for (uint8_t i = 0; i < num_audio_sources; i++)
- {
- if (audio_sources[i] == src) {
- audio_sources[i] = audio_sources[--num_audio_sources];
- if (sync_to_audio) {
- SDL_CondSignal(audio_ready);
- }
- break;
- }
- }
- if (!num_audio_sources) {
- need_pause = 1;
- }
- unlock_audio();
- if (need_pause) {
- SDL_PauseAudio(1);
+ if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
+ SDL_PauseAudio(0);
}
- inactive_audio_sources[num_inactive_audio_sources++] = src;
}
-void render_resume_source(audio_source *src)
+void render_source_paused(audio_source *src, uint8_t remaining_sources)
{
- lock_audio();
- if (num_audio_sources < 8) {
- audio_sources[num_audio_sources++] = src;
- }
- unlock_audio();
- for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
- {
- if (inactive_audio_sources[i] == src) {
- inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
- }
- }
if (sync_to_audio) {
+ SDL_CondSignal(audio_ready);
+ }
+ if (!remaining_sources) {
SDL_PauseAudio(0);
}
}
-void render_free_source(audio_source *src)
+void render_source_resumed(audio_source *src)
{
- render_pause_source(src);
-
- free(src->front);
if (sync_to_audio) {
- free(src->back);
- SDL_DestroyCond(src->cond);
+ SDL_PauseAudio(0);
}
- free(src);
}
-static uint32_t sync_samples;
-static void do_audio_ready(audio_source *src)
+
+void render_do_audio_ready(audio_source *src)
{
if (sync_to_audio) {
SDL_LockMutex(audio_mutex);
while (src->front_populated) {
- SDL_CondWait(src->cond, audio_mutex);
+ SDL_CondWait(src->opaque, audio_mutex);
}
int16_t *tmp = src->front;
src->front = src->back;
@@ -390,60 +195,6 @@ static void do_audio_ready(audio_source *src)
}
}
-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;
- uint32_t base = sync_to_audio ? 0 : src->read_end;
- while (src->buffer_fraction > BUFFER_INC_RES)
- {
- src->buffer_fraction -= BUFFER_INC_RES;
- interp_sample(src, src->last_left, value);
-
- if (((src->buffer_pos - base) & src->mask) >= sync_samples) {
- do_audio_ready(src);
- }
- src->buffer_pos &= src->mask;
- }
- 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;
- uint32_t base = sync_to_audio ? 0 : src->read_end;
- 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 - base) & src->mask)/2 >= sync_samples) {
- do_audio_ready(src);
- }
- src->buffer_pos &= src->mask;
- }
- src->last_left = left;
- src->last_right = right;
-}
-
static SDL_Joystick * joysticks[MAX_JOYSTICKS];
static int joystick_sdl_index[MAX_JOYSTICKS];
static uint8_t joystick_index_locked[MAX_JOYSTICKS];
@@ -1113,6 +864,7 @@ static int source_frame;
static int source_frame_count;
static int frame_repeat[60];
+static uint32_t sample_rate;
static void init_audio()
{
SDL_AudioSpec desired, actual;
@@ -1137,27 +889,20 @@ static void init_audio()
if (SDL_OpenAudio(&desired, &actual) < 0) {
fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
}
- buffer_samples = actual.samples;
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;
+ render_audio_format format = RENDER_AUDIO_UNKNOWN;
if (actual.format == AUDIO_S16SYS) {
debug_message("signed 16-bit int format\n");
- convert = convert_s16;
- mix_buf = calloc(output_channels * buffer_samples, sizeof(float));
+ format = RENDER_AUDIO_S16;
} else if (actual.format == AUDIO_F32SYS) {
debug_message("32-bit float format\n");
- convert = clamp_f32;
- mix_buf = NULL;
+ format = RENDER_AUDIO_FLOAT;
} else {
debug_message("unsupported format %X\n", actual.format);
warning("Unsupported audio sample format: %X\n", actual.format);
- 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);
+ render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8);
}
void window_setup(void)
@@ -1352,26 +1097,6 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
}
#include<unistd.h>
static int in_toggle;
-static void update_source(audio_source *src, double rc, uint8_t sync_changed)
-{
- double alpha = src->dt / (src->dt + rc);
- int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
- src->lowpass_alpha = lowpass_alpha;
- if (sync_changed) {
- uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels);
- src->back = realloc(src->back, alloc_size * sizeof(int16_t));
- if (sync_to_audio) {
- src->front = malloc(alloc_size * sizeof(int16_t));
- } else {
- free(src->front);
- src->front = src->back;
- }
- src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1;
- src->read_start = 0;
- src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0;
- src->buffer_pos = 0;
- }
-}
void render_config_updated(void)
{
@@ -1438,18 +1163,6 @@ void render_config_updated(void)
init_audio();
render_set_video_standard(video_standard);
- double lowpass_cutoff = get_lowpass_cutoff(config);
- double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
- lock_audio();
- for (uint8_t i = 0; i < num_audio_sources; i++)
- {
- update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio);
- }
- unlock_audio();
- for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
- {
- update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio);
- }
drain_events();
in_toggle = 0;
if (!was_paused) {
@@ -1462,6 +1175,12 @@ SDL_Window *render_get_window(void)
return main_window;
}
+uint32_t render_audio_syncs_per_sec(void)
+{
+ //sync samples with audio thread approximately every 8 lines when doing sync to video
+ return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8;
+}
+
void render_set_video_standard(vid_std std)
{
video_standard = std;
@@ -1491,8 +1210,6 @@ void render_set_video_standard(vid_std std)
}
source_frame = 0;
source_frame_count = frame_repeat[0];
- //sync samples with audio thread approximately every 8 lines
- sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (std == VID_PAL ? 313 : 262));
max_repeat++;
min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999;
//min_buffered *= buffer_samples;
@@ -1808,10 +1525,8 @@ void render_framebuffer_updated(uint8_t which, int width)
}
if (adjust_ratio != 0.0f) {
average_change = 0;
- for (uint8_t i = 0; i < num_audio_sources; i++)
- {
- audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5;
- }
+ render_audio_adjust_speed(adjust_ratio);
+
}
while (source_frame_count > 0)
{
@@ -1909,7 +1624,7 @@ uint32_t render_overscan_top()
return overscan_top[video_standard];
}
-void render_wait_quit(vdp_context * context)
+void render_wait_quit(void)
{
SDL_Event event;
while(SDL_WaitEvent(&event)) {
@@ -2077,16 +1792,6 @@ void render_toggle_fullscreen()
need_ui_fb_resize = 1;
}
-uint32_t render_audio_buffer()
-{
- return buffer_samples;
-}
-
-uint32_t render_sample_rate()
-{
- return sample_rate;
-}
-
void render_errorbox(char *title, char *message)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL);
diff --git a/ym2612.h b/ym2612.h
index 683931a..81f678f 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -9,7 +9,7 @@
#include <stdint.h>
#include <stdio.h>
#include "serialize.h"
-#include "render.h"
+#include "render_audio.h"
#define NUM_PART_REGS (0xB7-0x30)
#define NUM_CHANNELS 6