audio: seed runloop controls during transport preparation

Prepare native transport and its initial runloop tempo/cutoff in one parked-worker transaction. Validate unsupported speed before allocation and retain the previous session on failure. Reuse the speed resolver and keep existing preparation semantics intact. Cover failure preservation and pre-source control readiness, and use the transaction in the existing real-wrapper runloop startup/restart suite.
This commit is contained in:
Codex authored and libretroadmin committed 2026-09-14 04:21:45 +02:00
1 parent c88d7cbca1
commit 3ba9724372
4 files changed
+91 -10

No files matched your search

+34 -4
View File
@@ -4574,11 +4574,10 @@ bool audio_driver_pipeline_transport_request_speed(uint32_t tempo_q16,
tempo_q16) : 0);
}
bool audio_driver_pipeline_transport_request_runloop(bool reset, bool lowpass)
static bool audio_driver_transport_runloop_tempo(uint32_t *tempo)
{
settings_t *settings = config_get_ptr();
uint32_t flags = runloop_get_flags();
uint32_t tempo;
uint64_t bits;
double duration = 1.0;
if (!(flags & RUNLOOP_FLAG_PAUSED))
@@ -4595,7 +4594,14 @@ bool audio_driver_pipeline_transport_request_runloop(bool reset, bool lowpass)
memcpy(&bits, &duration, sizeof(bits));
if (bits >= UINT64_C(0x7ff0000000000000)
|| !(duration >= 1.0 / 32.0 && duration <= 4.0)) return false;
tempo = (uint32_t)(65536.0 / duration + 0.5);
*tempo = (uint32_t)(65536.0 / duration + 0.5);
return true;
}
bool audio_driver_pipeline_transport_request_runloop(bool reset, bool lowpass)
{
uint32_t tempo;
if (!audio_driver_transport_runloop_tempo(&tempo)) return false;
return audio_driver_pipeline_transport_request_speed(tempo,
tempo != 65536, reset, lowpass);
}
@@ -6859,7 +6865,7 @@ static bool audio_driver_transport_bind(audio_driver_state_t *audio_st,
struct audio_transport_prepare
{
unsigned rate;
uint32_t search;
uint32_t search, control, cutoff;
bool release, result;
};
@@ -6877,8 +6883,16 @@ static void audio_driver_transport_control(void *userdata)
&& audio_st->context_audio_data
&& !retro_spsc_read_avail(&audio_st->pipe_ring)
&& !audio_st->pipe_pending_bytes)
{
request->result = audio_driver_transport_bind(audio_st,
request->rate, request->search, audio_st->pipe_float);
if (request->result && request->control)
{
audio_pipeline_layout_t *q = &audio_st->pipe_layouts;
q->published_control = q->current_control = request->control;
q->published_cutoff = q->current_cutoff = request->cutoff;
}
}
}
static void audio_driver_transport_transaction(struct audio_transport_prepare *request)
@@ -6896,6 +6910,21 @@ bool audio_driver_pipeline_transport_prepare(unsigned rate, uint32_t search_chan
{
struct audio_transport_prepare request;
request.rate = rate; request.search = search_channels;
request.control = request.cutoff = 0;
request.release = request.result = false;
audio_driver_transport_transaction(&request);
return request.result;
}
bool audio_driver_pipeline_transport_prepare_runloop(unsigned rate,
uint32_t search_channels, bool lowpass)
{
struct audio_transport_prepare request;
uint32_t tempo;
if (!audio_driver_transport_runloop_tempo(&tempo)) return false;
request.rate = rate; request.search = search_channels;
request.control = tempo == 65536 ? tempo : tempo | AUDIO_PIPELINE_STRETCH;
request.cutoff = lowpass ? audio_speed_lpf_cutoff(rate, tempo) : 0;
request.release = request.result = false;
audio_driver_transport_transaction(&request);
return request.result;
@@ -6905,6 +6934,7 @@ void audio_driver_pipeline_transport_release(void)
{
struct audio_transport_prepare request;
request.rate = 0; request.search = 0;
request.control = request.cutoff = 0;
request.release = true; request.result = false;
audio_driver_transport_transaction(&request);
}
+7
View File
@@ -965,6 +965,13 @@ struct audio_pipeline_stretch;
* Empty-ring format renegotiation rebuilds the session; allocation failure
* releases it while preserving the new native ring format. */
bool audio_driver_pipeline_transport_prepare(unsigned rate, uint32_t search_channels);
/* Prepare and seed current runloop tempo/cutoff in one parked transaction.
* Empty source/no pending device output required. Failure preserves the old
* transport and metadata; unsupported speed is rejected before allocation.
* Explicit startup API: does not register an automatic update caller. */
bool audio_driver_pipeline_transport_prepare_runloop(unsigned rate,
uint32_t search_channels, bool lowpass);
void audio_driver_pipeline_transport_release(void);
/* Main source producer only, before publishing the affected audio. Tempo is
* source frames/output frame in Q16, 0.25..32 when active (ignored when inactive).
@@ -1665,6 +1665,41 @@ static void transport_owner_cases(void)
CHECK(!st->pipe_transport && !st->pipe_transport_output, "failed prepare left state");
CHECK(audio_driver_pipeline_transport_prepare(48000, 1), "owner prepare");
stage = st->pipe_transport; output = st->pipe_transport_output;
runloop_state_get_ptr()->flags = RUNLOOP_FLAG_SLOWMOTION;
config_get_ptr()->floats.slowmotion_ratio = 8;
calls = transport_control_calls;
CHECK(!audio_driver_pipeline_transport_prepare_runloop(48000, 1, true)
&& transport_control_calls == calls, "unsupported startup parked worker");
runloop_state_get_ptr()->flags = RUNLOOP_FLAG_FASTMOTION;
config_get_ptr()->bools.audio_fastforward_speedup = true;
retro_atomic_store_release_int(&st->pipe_ff_mult_q16, 16384);
transport_fail_stage = true;
CHECK(!audio_driver_pipeline_transport_prepare_runloop(48000, 1, true),
"failed runloop startup accepted");
transport_fail_stage = false;
CHECK(st->pipe_transport == stage && st->pipe_transport_output == output
&& st->pipe_layouts.published_control == 65536
&& st->pipe_layouts.published_cutoff == 0,
"failed runloop startup changed session");
calls = transport_control_calls;
CHECK(audio_driver_pipeline_transport_prepare_runloop(48000, 1, true)
&& transport_control_calls == calls + 1, "runloop startup parking");
CHECK(st->pipe_layouts.current_control == (262144 | AUDIO_PIPELINE_STRETCH)
&& st->pipe_layouts.published_control == st->pipe_layouts.current_control
&& st->pipe_layouts.current_cutoff == 5400
&& st->pipe_layouts.published_cutoff == 5400
&& !retro_atomic_load_relaxed_size(&st->pipe_layouts.head),
"startup did not seed coherent speed controls");
CHECK(audio_pipeline_stretch_next(st->pipe_transport, 0, 1, &block)
&& !block.frames && audio_pipeline_stretch_needs_input(st->pipe_transport),
"startup controls not ready before source");
runloop_state_get_ptr()->flags = 0;
config_get_ptr()->bools.audio_fastforward_speedup = false;
config_get_ptr()->floats.slowmotion_ratio = 1;
CHECK(audio_driver_pipeline_transport_prepare_runloop(48000, 1, false)
&& st->pipe_layouts.current_control == 65536
&& st->pipe_layouts.current_cutoff == 0, "normal startup was not dry");
stage = st->pipe_transport; output = st->pipe_transport_output;
CHECK(stage && output && !((uintptr_t)output % 64), "owner storage/alignment");
wrapper.wait_writable = NULL;
CHECK(!audio_driver_pipeline_transport_prepare(48000, 1)
@@ -1682,6 +1717,8 @@ static void transport_owner_cases(void)
bytes = st->pipe_frame_bytes;
CHECK(retro_spsc_write(&st->pipe_ring, &input, bytes) == bytes, "queued owner source");
CHECK(!audio_driver_pipeline_transport_prepare(48000, 1), "replaced queued owner");
CHECK(!audio_driver_pipeline_transport_prepare_runloop(48000, 1, true)
&& st->pipe_transport == stage, "runloop startup replaced queued owner");
audio_driver_set_core_float(!floating);
CHECK(st->pipe_float == (floating != 0) && st->pipe_transport == stage,
"queued format changed session");
@@ -1690,6 +1727,8 @@ static void transport_owner_cases(void)
retro_atomic_load_relaxed_size(&st->pipe_ring.head), 2000), "owner pending control");
st->pipe_pending = (const uint8_t*)output; st->pipe_pending_bytes = bytes;
CHECK(!audio_driver_pipeline_transport_prepare(48000, 1), "replaced pending device output");
CHECK(!audio_driver_pipeline_transport_prepare_runloop(48000, 1, true)
&& st->pipe_transport == stage, "runloop startup replaced pending output");
calls = transport_control_calls;
audio_driver_set_core_float(!floating);
CHECK(transport_control_calls == calls + 1, "native rebind bypassed parking");
@@ -438,6 +438,15 @@ static float frame_audio_float[32768 * 8];
/* --- fixture --------------------------------------------------------- */
static bool prepare_transport(bool reset)
{
if (runloop_policy)
return audio_driver_pipeline_transport_prepare_runloop(CORE_RATE, 3, speed_lowpass);
return audio_driver_pipeline_transport_prepare(CORE_RATE, 3)
&& audio_driver_pipeline_transport_request(tempo_q16,
transport_mode == 3, reset, transport_mode == 2 ? 1000 : 0);
}
static bool pipeline_up(unsigned latency_ms)
{
audio_driver_state_t *st = &audio_driver_st;
@@ -549,9 +558,7 @@ static bool pipeline_up(unsigned latency_ms)
&st->context_audio_data, NULL, OUT_RATE, NULL, latency_ms,
false, false, &clocked_driver))
return false;
if (transport_mode && (!audio_driver_pipeline_transport_prepare(CORE_RATE, 3)
|| !audio_driver_pipeline_transport_request(tempo_q16,
transport_mode == 3, false, transport_mode == 2 ? 1000 : 0)))
if (transport_mode && !prepare_transport(false))
return false;
return true;
}
@@ -704,9 +711,7 @@ static void wrapper_restart(void)
if (transport_mode)
{
audio_driver_pipeline_transport_release();
if (!audio_driver_pipeline_transport_prepare(CORE_RATE, 3)
|| !audio_driver_pipeline_transport_request(tempo_q16,
transport_mode == 3, true, transport_mode == 2 ? 1000 : 0))
if (!prepare_transport(true))
fixture_failures++;
}
if (before != retro_atomic_load_acquire_size(&cnt_wakes)) fixture_failures++;