midi: a core's MIDI request lasts until the core is unloaded

A staged content load runs the new core's init and load on the
previous session's drivers, then rebuilds them. The core's
GET_MIDI_INTERFACE request now ends in runloop_event_deinit_core after
retro_deinit rather than in driver_uninit, so drivers_init reopens the
device on the rebuilt drivers for a core that asked during its load.

The content_load harness gains a lane for it: a core asking at load
has the output open after the load, the next core that does not ask
has it shut.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-26 13:45:44 +01:00
1 parent ad2e9e05d8
commit 27ed906296
5 files changed
+71 -11

No files matched your search

+4 -2
View File
@@ -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:
+6 -8
View File
@@ -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();
+3 -1
View File
@@ -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
@@ -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
+11
View File
@@ -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 <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <libretro.h>
@@ -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;
}