changeset 1551:ce1f93be0104

Small cleanup to audio interface between emulation code and renderer backend
author Michael Pavone <pavone@retrodev.com>
date Wed, 28 Mar 2018 23:36:08 -0700
parents b525491b4e5b
children 13a82adb185b
files genesis.c psg.c psg.h render.h render_sdl.c sms.c ym2612.c ym2612.h
diffstat 8 files changed, 135 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/genesis.c	Mon Mar 26 23:36:39 2018 -0700
+++ b/genesis.c	Wed Mar 28 23:36:08 2018 -0700
@@ -1052,6 +1052,8 @@
 		}
 	}
 	vdp_release_framebuffer(gen->vdp);
+	render_pause_source(gen->ym->audio);
+	render_pause_source(gen->psg->audio);
 }
 
 static void start_genesis(system_header *system, char *statefile)
@@ -1099,6 +1101,8 @@
 	map_all_bindings(&gen->io);
 	render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC);
 	vdp_reacquire_framebuffer(gen->vdp);
+	render_resume_source(gen->ym->audio);
+	render_resume_source(gen->psg->audio);
 	resume_68k(gen->m68k);
 	handle_reset_requests(gen);
 }
--- a/psg.c	Mon Mar 26 23:36:39 2018 -0700
+++ b/psg.c	Wed Mar 28 23:36:08 2018 -0700
@@ -13,8 +13,8 @@
 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)
 {
 	memset(context, 0, sizeof(*context));
-	context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
-	context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
+	context->audio = render_audio_source(1);
+	context->audio_buffer = render_audio_source_buffer(context->audio);
 	context->clock_inc = clock_div;
 	context->sample_rate = sample_rate;
 	context->samples_frame = samples_frame;
@@ -30,10 +30,7 @@
 
 void psg_free(psg_context *context)
 {
-	free(context->audio_buffer);
-	//TODO: Figure out how to make this 100% safe
-	//audio thread could still be using this
-	free(context->back_buffer);
+	render_free_source(context->audio);
 	free(context);
 }
 
@@ -143,7 +140,8 @@
 			
 			if (context->buffer_pos == context->samples_frame) {
 				if (!headless) {
-					render_wait_psg(context);
+					context->audio_buffer = render_audio_ready(context->audio);
+					context->buffer_pos = 0;
 				}
 			}
 		}
--- a/psg.h	Mon Mar 26 23:36:39 2018 -0700
+++ b/psg.h	Wed Mar 28 23:36:08 2018 -0700
@@ -8,10 +8,11 @@
 
 #include <stdint.h>
 #include "serialize.h"
+#include "render.h"
 
 typedef struct {
 	int16_t  *audio_buffer;
-	int16_t  *back_buffer;
+	audio_source *audio;
 	uint64_t buffer_fraction;
 	uint64_t buffer_inc;
 	uint32_t buffer_pos;
--- a/render.h	Mon Mar 26 23:36:39 2018 -0700
+++ b/render.h	Wed Mar 28 23:36:08 2018 -0700
@@ -70,8 +70,6 @@
 #define FRAMEBUFFER_EVEN 1
 
 #include "vdp.h"
-#include "psg.h"
-#include "ym2612.h"
 
 typedef enum {
 	VID_NTSC,
@@ -85,6 +83,7 @@
 #define RENDER_NOT_MAPPED -2
 #define RENDER_NOT_PLUGGED_IN -3
 
+typedef struct audio_source audio_source;
 typedef void (*drop_handler)(const char *filename);
 
 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
@@ -96,10 +95,6 @@
 void render_toggle_fullscreen();
 void render_update_caption(char *title);
 void render_wait_quit(vdp_context * context);
-void render_wait_psg(psg_context * context);
-void render_wait_ym(ym2612_context * context);
-void render_disable_ym();
-void render_enable_ym();
 uint32_t render_audio_buffer();
 uint32_t render_sample_rate();
 void process_events();
@@ -122,6 +117,13 @@
 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);
+void render_pause_source(audio_source *src);
+void render_resume_source(audio_source *src);
+void render_free_source(audio_source *src);
 
 #endif //RENDER_H_
 
--- a/render_sdl.c	Mon Mar 26 23:36:39 2018 -0700
+++ b/render_sdl.c	Wed Mar 28 23:36:08 2018 -0700
@@ -44,64 +44,61 @@
 
 static SDL_mutex * audio_mutex;
 static SDL_cond * audio_ready;
-static SDL_cond * psg_cond;
-static SDL_cond * ym_cond;
 static uint8_t quitting = 0;
-static uint8_t ym_enabled = 1;
+
+struct audio_source {
+	SDL_cond *cond;
+	int16_t  *front;
+	int16_t  *back;
+	uint8_t  num_channels;
+	uint8_t  front_populated;
+};
+
+static audio_source *audio_sources[8];
+static uint8_t num_audio_sources;
 
 static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
 {
-	//puts("audio_callback");
 	int16_t * stream = (int16_t *)byte_stream;
 	int samples = len/(sizeof(int16_t)*2);
-	int16_t * psg_buf, * ym_buf;
-	uint8_t local_quit;
+	uint8_t num_populated;
+	memset(stream, 0, len);
 	SDL_LockMutex(audio_mutex);
-		psg_buf = NULL;
-		ym_buf = NULL;
 		do {
-			if (!psg_buf) {
-				psg_buf = current_psg;
-				current_psg = NULL;
-				SDL_CondSignal(psg_cond);
+			num_populated = 0;
+			for (uint8_t i = 0; i < num_audio_sources; i++)
+			{
+				if (audio_sources[i]->front_populated) {
+					num_populated++;
+				}
 			}
-			if (ym_enabled && !ym_buf) {
-				ym_buf = current_ym;
-				current_ym = NULL;
-				SDL_CondSignal(ym_cond);
-			}
-			if (!quitting && (!psg_buf || (ym_enabled && !ym_buf))) {
+			if (!quitting && num_populated < num_audio_sources) {
 				SDL_CondWait(audio_ready, audio_mutex);
 			}
-		} while(!quitting && (!psg_buf || (ym_enabled && !ym_buf)));
-
-		local_quit = quitting;
-	SDL_UnlockMutex(audio_mutex);
-	if (!local_quit) {
-		if (ym_enabled) {
-			for (int i = 0; i < samples; i++)
+		} while(!quitting && num_populated < num_audio_sources);
+		if (!quitting) {
+			int16_t *end = stream + 2*samples;
+			for (uint8_t i = 0; i < num_audio_sources; i++)
 			{
-				*(stream++) = psg_buf[i] + *(ym_buf++);
-				*(stream++) = psg_buf[i] + *(ym_buf++);
-			}
-		} else {
-			for (int i = 0; i < samples; i++)
-			{
-				*(stream++) = psg_buf[i];
-				*(stream++) = psg_buf[i];
+				int16_t *src = audio_sources[i]->front;
+				if (audio_sources[i]->num_channels == 1) {
+					for (int16_t *cur = stream; cur < end;)
+					{
+						*(cur++) += *src;
+						*(cur++) += *(src++);
+					}
+				} else {
+					for (int16_t *cur = stream; cur < end;)
+					{
+						*(cur++) += *(src++);
+						*(cur++) += *(src++);
+					}
+				}
+				audio_sources[i]->front_populated = 0;
+				SDL_CondSignal(audio_sources[i]->cond);
 			}
 		}
-	}
-}
-
-void render_disable_ym()
-{
-	ym_enabled = 0;
-}
-
-void render_enable_ym()
-{
-	ym_enabled = 1;
+	SDL_UnlockMutex(audio_mutex);
 }
 
 static void render_close_audio()
@@ -113,6 +110,59 @@
 	SDL_CloseAudio();
 }
 
+audio_source *render_audio_source(uint8_t channels)
+{
+	audio_source *ret = NULL;
+	SDL_LockMutex(audio_mutex);
+		if (num_audio_sources < 8) {
+			ret = malloc(sizeof(audio_source));
+			ret->front = malloc(channels * buffer_samples * sizeof(int16_t));
+			ret->back = malloc(channels * buffer_samples * sizeof(int16_t));
+			ret->front_populated = 0;
+			ret->cond = SDL_CreateCond();
+			ret->num_channels = channels;
+			audio_sources[num_audio_sources++] = ret;
+		}
+	SDL_UnlockMutex(audio_mutex);
+	if (!ret) {
+		fatal_error("Too many audio sources!");
+	}
+	return ret;
+}
+
+void render_pause_source(audio_source *src)
+{
+	SDL_LockMutex(audio_mutex);
+		for (uint8_t i = 0; i < num_audio_sources; i++)
+		{
+			if (audio_sources[i] == src) {
+				audio_sources[i] = audio_sources[--num_audio_sources];
+				SDL_CondSignal(audio_ready);
+				break;
+			}
+		}
+	SDL_UnlockMutex(audio_mutex);
+}
+
+void render_resume_source(audio_source *src)
+{
+	SDL_LockMutex(audio_mutex);
+		if (num_audio_sources < 8) {
+			audio_sources[num_audio_sources++] = src;
+		}
+	SDL_UnlockMutex(audio_mutex);
+}
+
+void render_free_source(audio_source *src)
+{
+	render_pause_source(src);
+	
+	free(src->front);
+	free(src->back);
+	SDL_DestroyCond(src->cond);
+	free(src);
+}
+
 static SDL_Joystick * joysticks[MAX_JOYSTICKS];
 static int joystick_sdl_index[MAX_JOYSTICKS];
 
@@ -480,8 +530,6 @@
 	caption = title;
 
 	audio_mutex = SDL_CreateMutex();
-	psg_cond = SDL_CreateCond();
-	ym_cond = SDL_CreateCond();
 	audio_ready = SDL_CreateCond();
 
 	SDL_AudioSpec desired, actual;
@@ -1190,34 +1238,24 @@
 	in_toggle = 0;
 }
 
-void render_wait_psg(psg_context * context)
+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 (current_psg != NULL) {
-			SDL_CondWait(psg_cond, audio_mutex);
+		while (src->front_populated) {
+			SDL_CondWait(src->cond, audio_mutex);
 		}
-		current_psg = context->audio_buffer;
+		int16_t *tmp = src->front;
+		src->front = src->back;
+		src->back = tmp;
+		src->front_populated = 1;
 		SDL_CondSignal(audio_ready);
-
-		context->audio_buffer = context->back_buffer;
-		context->back_buffer = current_psg;
 	SDL_UnlockMutex(audio_mutex);
-	context->buffer_pos = 0;
-}
-
-void render_wait_ym(ym2612_context * context)
-{
-	SDL_LockMutex(audio_mutex);
-		while (current_ym != NULL) {
-			SDL_CondWait(ym_cond, audio_mutex);
-		}
-		current_ym = context->audio_buffer;
-		SDL_CondSignal(audio_ready);
-
-		context->audio_buffer = context->back_buffer;
-		context->back_buffer = current_ym;
-	SDL_UnlockMutex(audio_mutex);
-	context->buffer_pos = 0;
+	return src->back;
 }
 
 uint32_t render_audio_buffer()
--- a/sms.c	Mon Mar 26 23:36:39 2018 -0700
+++ b/sms.c	Wed Mar 28 23:36:08 2018 -0700
@@ -336,7 +336,6 @@
 
 static void run_sms(system_header *system)
 {
-	render_disable_ym();
 	sms_context *sms = (sms_context *)system;
 	uint32_t target_cycle = sms->z80->current_cycle + 3420*16;
 	//TODO: PAL support
@@ -387,14 +386,15 @@
 		}
 	}
 	vdp_release_framebuffer(sms->vdp);
+	render_pause_source(sms->psg->audio);
 	sms->should_return = 0;
-	render_enable_ym();
 }
 
 static void resume_sms(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
 	vdp_reacquire_framebuffer(sms->vdp);
+	render_resume_source(sms->psg->audio);
 	run_sms(system);
 }
 
--- a/ym2612.c	Mon Mar 26 23:36:39 2018 -0700
+++ b/ym2612.c	Wed Mar 28 23:36:08 2018 -0700
@@ -168,8 +168,8 @@
 	static uint8_t registered_finalize;
 	dfopen(debug_file, "ym_debug.txt", "w");
 	memset(context, 0, sizeof(*context));
-	context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
-	context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
+	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);
@@ -266,13 +266,10 @@
 
 void ym_free(ym2612_context *context)
 {
+	render_free_source(context->audio);
 	if (context == log_context) {
 		ym_finalize_log();
 	}
-	free(context->audio_buffer);
-	//TODO: Figure out how to make this 100% safe
-	//audio thread could still be using this
-	free(context->back_buffer);
 	free(context);
 }
 
@@ -649,7 +646,8 @@
 				context->buffer_pos += 2;
 				if (context->buffer_pos == context->sample_limit) {
 					if (!headless) {
-						render_wait_ym(context);
+						context->audio_buffer = render_audio_ready(context->audio);
+						context->buffer_pos = 0;
 					}
 				}
 			}
--- a/ym2612.h	Mon Mar 26 23:36:39 2018 -0700
+++ b/ym2612.h	Wed Mar 28 23:36:08 2018 -0700
@@ -9,6 +9,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include "serialize.h"
+#include "render.h"
 
 #define NUM_PART_REGS (0xB7-0x30)
 #define NUM_CHANNELS 6
@@ -63,7 +64,7 @@
 
 typedef struct {
     int16_t     *audio_buffer;
-    int16_t     *back_buffer;
+	audio_source *audio;
     uint64_t    buffer_fraction;
     uint64_t    buffer_inc;
     uint32_t    clock_inc;