changeset 2640:c30e5548154f

Get sync to audio working in emscripten
author Michael Pavone <pavone@retrodev.com>
date Wed, 26 Feb 2025 22:55:42 -0800
parents 0046305e3fa8
children 8d016949c29b
files blastem.c render.h render_sdl.c
diffstat 3 files changed, 115 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Wed Feb 26 20:46:00 2025 -0800
+++ b/blastem.c	Wed Feb 26 22:55:42 2025 -0800
@@ -32,6 +32,7 @@
 #endif
 #ifdef __EMSCRIPTEN__
 #include <emscripten.h>
+#include "render_audio.h"
 #endif
 
 #include "version.inc"
@@ -142,8 +143,90 @@
 	}
 }
 
+static uint8_t menu;
+static uint8_t use_nuklear;
+#ifdef __EMSCRIPTEN__
+void handle_frame_presented(void)
+{
+	if (current_system) {
+		current_system->request_exit(current_system);
+	}
+}
+
+void browser_main_loop(void)
+{
+	static uint8_t system_started;
+#ifndef DISABLE_NUKLEAR
+	static uint8_t was_menu;
+	if (use_nuklear) {
+		if (menu && !was_menu) {
+			ui_enter();
+		} else if (!menu && was_menu) {
+			ui_exit();
+		}
+		if (menu) {
+			render_update_display();
+		}
+	}
+#endif
+	if (!current_system && game_system) {
+		current_system = game_system;
+		menu = 0;
+#ifndef DISABLE_NUKLEAR
+		was_menu = 0;
+		ui_exit();
+#endif
+	}
+	if (current_system) {
+		if (system_started && render_is_audio_sync()) {
+			if (all_sources_ready()) {
+				return;
+			}
+		}
+		if (current_system->next_rom) {
+			char *next_rom = current_system->next_rom;
+			current_system->next_rom = NULL;
+			init_system_with_media(next_rom, 0);
+			system_started = 0;
+			menu = 0;
+			current_system = game_system;
+		} else if (!menu) {
+			if (system_started) {
+				current_system->resume_context(current_system);
+			} else {
+				system_started = 1;
+				current_system->start_context(current_system, NULL);
+			}
+			if (current_system->force_release) {
+				menu = 1;
+			}
+		}
+	}
+	
+}
+
+void setup_main_loop(void)
+{
+	//can't use render_is_audio_sync since we haven't called render_init/render_config_updated yet
+	char *sync = tern_find_path_default(config, "system\0sync_source\0", (tern_val){.ptrval = "audio"}, TVAL_PTR).ptrval;
+	emscripten_cancel_main_loop();
+	if (!strcmp("video", sync)) {
+		render_set_audio_full_fun(NULL);
+		render_set_frame_presented_fun(handle_frame_presented);
+		emscripten_set_main_loop(browser_main_loop, 0, 0);
+	} else {
+		render_set_frame_presented_fun(NULL);
+		render_set_audio_full_fun(handle_frame_presented);
+		emscripten_set_main_loop(browser_main_loop, 1, 0); //dummy fps value, will be overridden by a call to emscripten_set_main_loop_timing
+	}
+}
+#endif
+
 void apply_updated_config(void)
 {
+#ifdef __EMSCRIPTEN__
+	setup_main_loop();
+#endif
 	render_config_updated();
 	set_bindings();
 	update_pad_bindings();
@@ -274,64 +357,6 @@
 	update_title(game_system->info.name);
 }
 
-static uint8_t menu;
-static uint8_t use_nuklear;
-#ifdef __EMSCRIPTEN__
-void handle_frame_presented(void)
-{
-	if (current_system) {
-		current_system->request_exit(current_system);
-	}
-}
-
-void browser_main_loop(void)
-{
-	static uint8_t system_started;
-#ifndef DISABLE_NUKLEAR
-	static uint8_t was_menu;
-	if (use_nuklear) {
-		if (menu && !was_menu) {
-			ui_enter();
-		} else if (!menu && was_menu) {
-			ui_exit();
-		}
-		if (menu) {
-			render_update_display();
-		}
-	}
-#endif
-	if (!current_system && game_system) {
-		current_system = game_system;
-		menu = 0;
-#ifndef DISABLE_NUKLEAR
-		was_menu = 0;
-		ui_exit();
-#endif
-	}
-	if (current_system) {
-		if (current_system->next_rom) {
-			char *next_rom = current_system->next_rom;
-			current_system->next_rom = NULL;
-			init_system_with_media(next_rom, 0);
-			system_started = 0;
-			menu = 0;
-			current_system = game_system;
-		} else if (!menu) {
-			if (system_started) {
-				current_system->resume_context(current_system);
-			} else {
-				system_started = 1;
-				current_system->start_context(current_system, NULL);
-			}
-			if (current_system->force_release) {
-				menu = 1;
-			}
-		}
-	}
-	
-}
-#endif
-
 char *parse_addr_port(char *arg)
 {
 	while (*arg && *arg != ':') {
@@ -550,10 +575,8 @@
 		fullscreen = !fullscreen;
 	}
 #ifdef __EMSCRIPTEN__
-	config = tern_insert_path(config, "system\0sync_source\0", (tern_val){.ptrval = strdup("video")}, TVAL_PTR);
 	config = tern_insert_path(config, "ui\0initial_path\0", (tern_val){.ptrval = strdup("/roms")}, TVAL_PTR);
-	render_set_frame_presented_fun(handle_frame_presented);
-	emscripten_set_main_loop(browser_main_loop, 0, 0);
+	setup_main_loop();
 #endif
 	if (!headless) {
 		if (reader_addr) {
--- a/render.h	Wed Feb 26 20:46:00 2025 -0800
+++ b/render.h	Wed Feb 26 22:55:42 2025 -0800
@@ -143,6 +143,7 @@
 void render_set_ui_render_fun(ui_render_fun);
 void render_set_ui_fb_resize_handler(ui_render_fun resize);
 void render_set_frame_presented_fun(ui_render_fun);
+void render_set_audio_full_fun(ui_render_fun);
 void render_video_loop(void);
 uint8_t render_should_release_on_exit(void);
 void render_set_external_sync(uint8_t ext_sync_on);
--- a/render_sdl.c	Wed Feb 26 20:46:00 2025 -0800
+++ b/render_sdl.c	Wed Feb 26 22:55:42 2025 -0800
@@ -26,6 +26,9 @@
 #include <GL/glew.h>
 #endif
 #endif
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
 
 #define MAX_EVENT_POLL_PER_FRAME 2
 
@@ -123,12 +126,20 @@
 {
 	SDL_LockMutex(audio_mutex);
 		uint8_t all_ready;
+#ifdef __EMSCRIPTEN__
+		if (!all_sources_ready()) {
+			memset(byte_stream, 0, len);
+			SDL_UnlockMutex(audio_mutex);
+			return;
+		}
+#else
 		do {
 			all_ready = all_sources_ready();
 			if (!quitting && !all_ready) {
 				SDL_CondWait(audio_ready, audio_mutex);
 			}
 		} while(!quitting && !all_ready);
+#endif
 		if (!quitting) {
 			mix_and_convert(byte_stream, len, NULL);
 		}
@@ -255,6 +266,12 @@
 
 uint8_t audio_deadlock_hack(void);
 
+static ui_render_fun audio_full_cb;
+void render_set_audio_full_fun(ui_render_fun cb)
+{
+	audio_full_cb = cb;
+}
+
 void render_do_audio_ready(audio_source *src)
 {
 	if (sync_src == SYNC_AUDIO_THREAD) {
@@ -268,7 +285,9 @@
 			system_request_exit(current_system, 0);
 		}
 	} else if (sync_src == SYNC_AUDIO) {
+		uint8_t all_ready = 0;
 		SDL_LockMutex(audio_mutex);
+#ifndef __EMSCRIPTEN__
 			if (src->front_populated) {
 				if (audio_deadlock_hack()) {
 					SDL_CondSignal(audio_ready);
@@ -277,13 +296,18 @@
 			while (src->front_populated) {
 				SDL_CondWait(src->opaque, audio_mutex);
 			}
+#endif
 			int16_t *tmp = src->front;
 			src->front = src->back;
 			src->back = tmp;
 			src->front_populated = 1;
 			src->buffer_pos = 0;
+			all_ready = all_sources_ready();
 			SDL_CondSignal(audio_ready);
 		SDL_UnlockMutex(audio_mutex);
+		if (all_ready && audio_full_cb) {
+			audio_full_cb();
+		}
 	} else {
 		uint32_t num_buffered;
 		SDL_LockAudio();
@@ -1070,6 +1094,12 @@
 		debug_message("unsupported format %X\n", actual.format);
 		warning("Unsupported audio sample format: %X\n", actual.format);
 	}
+#ifdef __EMSCRIPTEN__
+	if (sync_src == SYNC_AUDIO) {
+		printf("emscripten_set_main_loop_timing %d\n", actual.samples * 500 / actual.freq);
+		emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, actual.samples * 500 / actual.freq);
+	}
+#endif
 	render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8);
 }