diff --git a/midi_driver.h b/midi_driver.h index 00b3396e31..eda442f007 100644 --- a/midi_driver.h +++ b/midi_driver.h @@ -230,8 +230,10 @@ bool midi_driver_set_volume(unsigned volume); bool midi_driver_set_input(const char *input); bool midi_driver_set_output(void *data, const char *output); -/* Opens the devices on behalf of a core, now or at the next driver init */ -void midi_driver_request(void); +/* true: opens the devices on behalf of a core, now or at the next + * driver init, and at every driver init until the core is unloaded. + * false: the core is unloaded; the next driver init leaves them shut. */ +void midi_driver_request(bool requested); /** * midi_driver_get_event_size: diff --git a/retroarch.c b/retroarch.c index d5f9040352..2e1859b17e 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1025,10 +1025,10 @@ static bool midi_driver_init(void *data) return true; } -void midi_driver_request(void) +void midi_driver_request(bool requested) { - rarch_midi_drv_requested = true; - if (rarch_midi_drv_inited) + rarch_midi_drv_requested = requested; + if (requested && rarch_midi_drv_inited) midi_driver_open(config_get_ptr()); } @@ -2254,13 +2254,11 @@ void driver_uninit(int flags, enum driver_lifetime_flags lifetime_flags) microphone_driver_deinit(lifetime_flags & DRIVER_LIFETIME_RESET); #endif + /* The core's request outlives this: a staged content load frees + * the drivers after the new core has asked, and drivers_init + * reopens for it. The request ends with the core's unload. */ if (flags & DRIVER_MIDI_MASK) - { midi_driver_free(); - /* Reopen after a reinit, not after the core is unloaded */ - if (!(lifetime_flags & DRIVER_LIFETIME_RESET)) - rarch_midi_drv_requested = false; - } #ifdef HAVE_LAKKA cpu_scaling_driver_free(); diff --git a/runloop.c b/runloop.c index 37cda99a37..786d501ca0 100644 --- a/runloop.c +++ b/runloop.c @@ -4041,7 +4041,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) if (midi_interface) { - midi_driver_request(); + midi_driver_request(true); midi_interface->input_enabled = midi_driver_input_enabled; midi_interface->output_enabled = midi_driver_output_enabled; midi_interface->read = midi_driver_read; @@ -5732,6 +5732,8 @@ void runloop_event_deinit_core(void) * follows to write that. */ input_driver_flush_rumble(); led_driver_flush(); + /* After retro_deinit, which may still send its notes-off */ + midi_driver_request(false); /* retro_deinit() may call * RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE diff --git a/samples/tasks/content_load/content_load_test.c b/samples/tasks/content_load/content_load_test.c index e9f85fbd03..152b01a9a0 100644 --- a/samples/tasks/content_load/content_load_test.c +++ b/samples/tasks/content_load/content_load_test.c @@ -20,6 +20,9 @@ * refused, and the first still completes; * - reinit deferred: a driver reinit asked for while the load is in * flight leaves the drivers as they are - the load rebuilds them; + * - midi requested: a core that asks for the MIDI interface while it + * loads has the output open once the load has rebuilt the drivers; + * the next core, which does not ask, has it shut; * - fallback: a core that cannot be loaded ends on the dummy core * with the drivers rebuilt and the menu up; * - not a core: a library that opens but lacks retro_init does the @@ -66,6 +69,7 @@ #include "../../../verbosity.h" #include "../../../gfx/video_driver.h" #include "../../../audio/audio_driver.h" +#include "../../../midi_driver.h" #include "../../../gfx/video_defines.h" #include "../../../menu/menu_driver.h" #include "../../../tasks/task_content.h" @@ -496,6 +500,45 @@ static void lane_audio_kept(void) " a content load; with keeping off it is closed and opened\n"); } +/* The MIDI device of a core that asks for the interface while it loads: + * the ask comes before the load rebuilds the drivers, and the device + * is open on the rebuilt ones. A core that does not ask leaves it shut. */ +static void lane_midi_requested(void) +{ + struct load_frame log[LOAD_FRAMES]; + unsigned had = failures; + unsigned n; + + putenv((char*)"HARNESS_CORE_MIDI=1"); + open_menu(); + CHECK(task_push_load_contentless_core_from_menu(core_path), + "the load was not started"); + n = run_load(log, LOAD_FRAMES); + CHECK(n < LOAD_FRAMES && core_is_up(), + "the load did not bring the core up within %u frames", LOAD_FRAMES); + CHECK(midi_driver_output_enabled(), + "the core asked for MIDI while loading, and no output is open " + "after the load"); + pump(2); + CHECK(midi_driver_output_enabled(), + "the MIDI output closed once the core ran"); + + putenv((char*)"HARNESS_CORE_MIDI="); + open_menu(); + CHECK(task_push_load_contentless_core_from_menu(core_path), + "the second load was not started"); + n = run_load(log, LOAD_FRAMES); + CHECK(n < LOAD_FRAMES && core_is_up(), + "the second load did not bring the core up within %u frames", + LOAD_FRAMES); + CHECK(!midi_driver_output_enabled(), + "the MIDI output is open for a core that never asked for it"); + + if (failures == had) + fprintf(stderr, "[pass] midi requested lane: the output is open for" + " a core that asks while loading, shut for one that does not\n"); +} + #if defined(HAVE_DYNAMIC) static void harness_sibling(char *s, size_t len, const char *name) @@ -2735,6 +2778,9 @@ int main(int argc, char *argv[]) fprintf(cfg, "savestate_auto_save = \"false\"\n"); fprintf(cfg, "menu_show_load_content_animation = \"false\"\n"); fprintf(cfg, "threaded_data_runloop_enable = \"false\"\n"); + /* The null MIDI driver's one output: the MIDI lane's device */ + fprintf(cfg, "midi_driver = \"null\"\n"); + fprintf(cfg, "midi_output = \"Null\"\n"); /* The records of the cores asked live next to the info cache */ fprintf(cfg, "libretro_info_path = \"%s\"\n", dir); /* Where a contentless core's states go, named after the core: @@ -2809,6 +2855,7 @@ int main(int argc, char *argv[]) lane_staged(); lane_core_opened_once(); lane_audio_kept(); + lane_midi_requested(); #if defined(HAVE_DYNAMIC) lane_probe_cached(); #endif diff --git a/samples/tasks/content_load/harness_core.c b/samples/tasks/content_load/harness_core.c index ec4b511213..57e9947102 100644 --- a/samples/tasks/content_load/harness_core.c +++ b/samples/tasks/content_load/harness_core.c @@ -2,6 +2,7 @@ * content, pushes a frame per retro_run, and counts its own * retro_init calls behind an export, so the harness can tell exactly * which frame the frontend brought it up on. */ +#include #include #include #include @@ -188,6 +189,16 @@ bool retro_load_game(const struct retro_game_info *game) hw.context_destroy = hw_context_destroy; environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw); #endif + /* HARNESS_CORE_MIDI set: the MIDI interface is asked for here, as + * cores that play music through it do */ + { + const char *midi = getenv("HARNESS_CORE_MIDI"); + if (midi && *midi) + { + struct retro_midi_interface midi_cb; + environ_cb(RETRO_ENVIRONMENT_GET_MIDI_INTERFACE, &midi_cb); + } + } (void)game; return true; }