mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
console audio: every wait for the device is bounded
Each of these drivers waited for its output thread, callback or
hardware position with no bound, and one that stopped - the port
closed, the renderer gone, the system back from sleep with the channel
dropped - held the calling thread there. Each wait is now timed where
the platform offers a timed form of the same primitive, and capped at
a fixed number of them; a blocking write then returns what went and
wait_writable() hands the pass back as no space coming from this call.
psp scond_wait -> scond_wait_timeout, both loops; the write
loop also stops when the thread is no longer running
ps3 sysLwCondWait(cond, 0) - 0 is no timeout - -> a timeout in
microseconds, both loops; the write loop also stops when
the port is not started or the thread is quitting
wiiu the write's OSYieldThread() spin, which a running voice
whose callback had stopped never left, waits on the frame
event with OSWaitEventWithTimeout(); wait_writable()'s
OSWaitEvent() the same
switch compat_condvar_wait_timeout() added to the compat layer
(condvarWaitTimeout on libnx, trn_condvar_wait's own
timeout on libtransistor); switch_thread and the libnx
audren thread driver use it in both loops, the latter
also returning from a write while paused, when its render
thread takes nothing
ctr dsp the write's 100 ms poll of the channel position is capped;
the quit path returned true from a function returning
ssize_t, and returns -1
ctr csnd the write's 1 ms poll of the play position is capped
xenon360 the write's udelay(50) spin on the unplayed count is capped
Not changed, for want of a timed primitive: gx's write, a spin with no
sleep on the DMA state, and its wait_writable() on OSSleepThread();
the 3DS DSP driver's wait_writable() on LightEvent_Wait(); and the
non-threaded libnx audren driver's audrenWaitFrame(). Those need hands
on the platform.
None of this could be compiled here: no console SDK. Each change
reuses a primitive the tree already calls on that platform (rthreads
for psp and the Switch and Wii U timed waits), keeps declarations at
block starts, and every file balances; the platform workflows are the
compile check.
This commit is contained in:
1 parent
d6cd992321
commit
d32605d3af
9 files changed
+172
-15
No files matched your search
@@ -162,6 +162,10 @@ static void ctr_csnd_audio_free(void *data)
|
||||
free(ctr);
|
||||
}
|
||||
|
||||
/* How many 1 ms polls a blocking write waits for the play position to
|
||||
* advance before giving up on it. */
|
||||
#define CTR_CSND_AUDIO_WAIT_LAPS 2000
|
||||
|
||||
static ssize_t ctr_csnd_audio_write(void *data, const void *buf, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -180,11 +184,17 @@ static ssize_t ctr_csnd_audio_write(void *data, const void *buf, size_t len)
|
||||
ctr->pos = (ctr->playpos + (CTR_CSND_AUDIO_COUNT >> 1)) & CTR_CSND_AUDIO_COUNT_MASK;
|
||||
else
|
||||
{
|
||||
/* Poll the play position while CSND plays this out, capped: a
|
||||
* position that has stopped advancing never satisfies the test
|
||||
* below, and the write then returns having written nothing. */
|
||||
int laps = CTR_CSND_AUDIO_WAIT_LAPS;
|
||||
do
|
||||
{
|
||||
/* todo: compute the correct sleep period */
|
||||
retro_sleep(1);
|
||||
ctr_csnd_audio_update_playpos(ctr);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
} while (((ctr->playpos - ctr->pos) & CTR_CSND_AUDIO_COUNT_MASK) < (CTR_CSND_AUDIO_COUNT >> 1)
|
||||
|| (((ctr->pos - ctr->playpos) & CTR_CSND_AUDIO_COUNT_MASK) < (CTR_CSND_AUDIO_COUNT >> 4)));
|
||||
}
|
||||
|
||||
@@ -110,6 +110,10 @@ static void ctr_dsp_audio_free(void *data)
|
||||
ndspExit();
|
||||
}
|
||||
|
||||
/* How many 100 ms polls a blocking write waits for the channel to
|
||||
* advance before giving up on it. */
|
||||
#define CTR_DSP_AUDIO_WAIT_LAPS 20
|
||||
|
||||
static ssize_t ctr_dsp_audio_write(void *data, const void *buf, size_t len)
|
||||
{
|
||||
u32 pos;
|
||||
@@ -124,6 +128,12 @@ static ssize_t ctr_dsp_audio_write(void *data, const void *buf, size_t len)
|
||||
ctr->pos = (sample_pos + (CTR_DSP_AUDIO_COUNT >> 1)) & CTR_DSP_AUDIO_COUNT_MASK;
|
||||
else
|
||||
{
|
||||
/* Poll the channel position while the DSP plays this out. A
|
||||
* channel that has stopped advancing - the DSP reset, the
|
||||
* system back from sleep with the channel dropped - never
|
||||
* satisfies the test below, so the poll is capped and the
|
||||
* write then returns having written nothing. */
|
||||
int laps = CTR_DSP_AUDIO_WAIT_LAPS;
|
||||
do
|
||||
{
|
||||
svcSleepThread(100000);
|
||||
@@ -133,8 +143,10 @@ static ssize_t ctr_dsp_audio_write(void *data, const void *buf, size_t len)
|
||||
if (!aptMainLoop())
|
||||
{
|
||||
retroarch_main_quit();
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
|
||||
sample_pos = ndspChnGetSamplePos(ctr->channel);
|
||||
}while ( ((sample_pos - (ctr->pos + (len >>2))) & CTR_DSP_AUDIO_COUNT_MASK) > (CTR_DSP_AUDIO_COUNT >> 1)
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
#define AUDIO_BLOCKS 8
|
||||
#define AUDIO_CHANNELS 2
|
||||
|
||||
/* Bound on any wait for the audio thread to consume, in microseconds
|
||||
* (sysLwCondWait's unit; 0 there means no timeout at all), and how many
|
||||
* of them before the caller gets the pass back. */
|
||||
#define PS3_AUDIO_WAIT_US 100000
|
||||
#define PS3_AUDIO_WAIT_LAPS 8
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fifo_buffer_t *buffer;
|
||||
@@ -152,8 +158,22 @@ static ssize_t ps3_audio_write(void *data, const void *s, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (FIFO_WRITE_AVAIL(aud->buffer) < len)
|
||||
sysLwCondWait(&aud->cond, 0);
|
||||
{
|
||||
/* The audio thread signals each time it consumes a block. One
|
||||
* that has stopped - the port closed, the thread quitting -
|
||||
* signals nothing; the wait is timed and capped, and the write
|
||||
* then returns having written nothing rather than holding the
|
||||
* caller. */
|
||||
int laps = PS3_AUDIO_WAIT_LAPS;
|
||||
while (FIFO_WRITE_AVAIL(aud->buffer) < len)
|
||||
{
|
||||
if (!aud->started || aud->quit_thread)
|
||||
return 0;
|
||||
sysLwCondWait(&aud->cond, PS3_AUDIO_WAIT_US);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sysLwMutexLock(&aud->lock, PS3_SYS_NO_TIMEOUT);
|
||||
fifo_write(aud->buffer, s, len);
|
||||
@@ -245,6 +265,7 @@ static size_t ps3_audio_wait_writable(void *data, size_t len)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
size_t avail;
|
||||
int laps = PS3_AUDIO_WAIT_LAPS;
|
||||
|
||||
if (len > aud->buffer->size / 2)
|
||||
len = aud->buffer->size / 2;
|
||||
@@ -256,7 +277,12 @@ static size_t ps3_audio_wait_writable(void *data, size_t len)
|
||||
avail = ps3_audio_write_avail(aud);
|
||||
if (avail >= len)
|
||||
return avail;
|
||||
sysLwCondWait(&aud->cond, 0);
|
||||
/* Timed and capped, as in the write: no room after this many
|
||||
* waits is a thread that has stopped consuming, and the pass is
|
||||
* handed back as no space coming from this call. */
|
||||
sysLwCondWait(&aud->cond, PS3_AUDIO_WAIT_US);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,12 @@ typedef struct psp_audio
|
||||
#define AUDIO_BUFFER_SIZE (1u<<13u)
|
||||
#define AUDIO_BUFFER_SIZE_MASK (AUDIO_BUFFER_SIZE-1)
|
||||
|
||||
/* Bound on any wait for the audio thread to consume: one wait, and how
|
||||
* many of them before the caller gets the pass back. The thread
|
||||
* consumes a period every period while the device runs. */
|
||||
#define PSP_AUDIO_WAIT_US 100000
|
||||
#define PSP_AUDIO_WAIT_LAPS 8
|
||||
|
||||
/* Return port used */
|
||||
static int psp_configure_audio(unsigned rate)
|
||||
{
|
||||
@@ -221,9 +227,25 @@ static ssize_t psp_audio_write(void *data, const void *s, size_t len)
|
||||
}
|
||||
|
||||
slock_lock(psp->cond_lock);
|
||||
while (AUDIO_BUFFER_SIZE - ((uint16_t)
|
||||
(psp->write_pos - psp->read_pos) & AUDIO_BUFFER_SIZE_MASK) < len)
|
||||
scond_wait(psp->cond, psp->cond_lock);
|
||||
{
|
||||
/* The audio thread signals every period it consumes. One that has
|
||||
* stopped consuming - the device suspended, the thread gone -
|
||||
* signals nothing; the wait is bounded and the write then returns
|
||||
* having written nothing rather than holding the caller. */
|
||||
int laps = PSP_AUDIO_WAIT_LAPS;
|
||||
while (AUDIO_BUFFER_SIZE - ((uint16_t)
|
||||
(psp->write_pos - psp->read_pos) & AUDIO_BUFFER_SIZE_MASK) < len)
|
||||
{
|
||||
if ( !scond_wait_timeout(psp->cond, psp->cond_lock,
|
||||
PSP_AUDIO_WAIT_US)
|
||||
|| --laps < 0
|
||||
|| !psp->running)
|
||||
{
|
||||
slock_unlock(psp->cond_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
slock_unlock(psp->cond_lock);
|
||||
|
||||
slock_lock(psp->fifo_lock);
|
||||
@@ -321,6 +343,7 @@ static size_t psp_wait_writable(void *data, size_t len)
|
||||
{
|
||||
psp_audio_t* psp = (psp_audio_t*)data;
|
||||
size_t avail;
|
||||
int laps = PSP_AUDIO_WAIT_LAPS;
|
||||
|
||||
if (len > AUDIO_BUFFER_SIZE / 2)
|
||||
len = AUDIO_BUFFER_SIZE / 2;
|
||||
@@ -337,7 +360,16 @@ static size_t psp_wait_writable(void *data, size_t len)
|
||||
(psp->write_pos - psp->read_pos) & AUDIO_BUFFER_SIZE_MASK);
|
||||
if (avail >= len)
|
||||
break;
|
||||
scond_wait(psp->cond, psp->cond_lock);
|
||||
/* Bounded per wait and overall: a thread that has stopped
|
||||
* consuming hands the pass back as no space coming from this
|
||||
* call. */
|
||||
if ( !scond_wait_timeout(psp->cond, psp->cond_lock,
|
||||
PSP_AUDIO_WAIT_US)
|
||||
|| --laps < 0)
|
||||
{
|
||||
slock_unlock(psp->cond_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
slock_unlock(psp->cond_lock);
|
||||
return avail;
|
||||
|
||||
@@ -34,6 +34,9 @@ typedef CondVar compat_condvar;
|
||||
condvarInit(condvar)
|
||||
#define compat_condvar_wait(condvar, mutex) \
|
||||
condvarWait(condvar, mutex)
|
||||
/* Timed wait; ns is the bound in nanoseconds. */
|
||||
#define compat_condvar_wait_timeout(condvar, mutex, ns) \
|
||||
condvarWaitTimeout(condvar, mutex, ns)
|
||||
#define compat_condvar_wake_all(condvar) \
|
||||
condvarWakeAll(condvar)
|
||||
|
||||
@@ -80,6 +83,9 @@ typedef trn_condvar_t compat_condvar;
|
||||
trn_condvar_create(condvar)
|
||||
#define compat_condvar_wait(condvar, mutex) \
|
||||
trn_condvar_wait(condvar, mutex, -1)
|
||||
/* Timed wait; ns is the bound in nanoseconds. */
|
||||
#define compat_condvar_wait_timeout(condvar, mutex, ns) \
|
||||
trn_condvar_wait(condvar, mutex, (ns))
|
||||
#define compat_condvar_wake_all(condvar) \
|
||||
trn_condvar_signal(condvar, -1)
|
||||
|
||||
|
||||
@@ -279,6 +279,7 @@ static size_t libnx_audren_thread_audio_wait_writable(void *data, size_t len)
|
||||
{
|
||||
size_t available;
|
||||
libnx_audren_thread_t *aud = (libnx_audren_thread_t*)data;
|
||||
int laps = LIBNX_AUDREN_WAIT_LAPS;
|
||||
|
||||
if (!aud)
|
||||
return 0;
|
||||
@@ -294,9 +295,14 @@ static size_t libnx_audren_thread_audio_wait_writable(void *data, size_t len)
|
||||
mutexUnlock(&aud->fifo_lock);
|
||||
if (available >= len)
|
||||
return available;
|
||||
/* Timed and capped: a renderer that has stopped hands the pass
|
||||
* back as no space coming from this call. */
|
||||
mutexLock(&aud->fifo_condlock);
|
||||
condvarWait(&aud->fifo_condvar, &aud->fifo_condlock);
|
||||
condvarWaitTimeout(&aud->fifo_condvar, &aud->fifo_condlock,
|
||||
LIBNX_AUDREN_WAIT_NS);
|
||||
mutexUnlock(&aud->fifo_condlock);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,6 +316,12 @@ static size_t libnx_audren_thread_audio_buffer_size(void *data)
|
||||
return aud->buffer_size;
|
||||
}
|
||||
|
||||
/* Bound on a wait for the render thread to take from the fifo: one
|
||||
* wait, in nanoseconds (condvarWaitTimeout's unit), and how many before
|
||||
* the caller gets the pass back. */
|
||||
#define LIBNX_AUDREN_WAIT_NS 100000000ULL
|
||||
#define LIBNX_AUDREN_WAIT_LAPS 8
|
||||
|
||||
static ssize_t libnx_audren_thread_audio_write(void *data,
|
||||
const void *s, size_t len)
|
||||
{
|
||||
@@ -333,6 +345,8 @@ static ssize_t libnx_audren_thread_audio_write(void *data,
|
||||
}
|
||||
else
|
||||
{
|
||||
int laps = LIBNX_AUDREN_WAIT_LAPS;
|
||||
|
||||
_len = 0;
|
||||
while (_len < len && aud->running)
|
||||
{
|
||||
@@ -347,10 +361,19 @@ static ssize_t libnx_audren_thread_audio_write(void *data,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The render thread signals each time it takes from the
|
||||
* fifo. Paused it takes nothing, and a renderer that has
|
||||
* stopped takes nothing either; the wait is timed and
|
||||
* capped, and the write returns what went. */
|
||||
mutexUnlock(&aud->fifo_lock);
|
||||
if (aud->paused)
|
||||
break;
|
||||
mutexLock(&aud->fifo_condlock);
|
||||
condvarWait(&aud->fifo_condvar, &aud->fifo_condlock);
|
||||
condvarWaitTimeout(&aud->fifo_condvar, &aud->fifo_condlock,
|
||||
LIBNX_AUDREN_WAIT_NS);
|
||||
mutexUnlock(&aud->fifo_condlock);
|
||||
if (--laps < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +337,12 @@ static void switch_thread_audio_free(void *data)
|
||||
swa = NULL;
|
||||
}
|
||||
|
||||
/* Bound on a wait for the output thread to take from the fifo: one
|
||||
* wait, in nanoseconds, and how many before the caller gets the pass
|
||||
* back. */
|
||||
#define SWITCH_AUDIO_WAIT_NS 100000000ULL
|
||||
#define SWITCH_AUDIO_WAIT_LAPS 8
|
||||
|
||||
static ssize_t switch_thread_audio_write(void *data, const void *s, size_t len)
|
||||
{
|
||||
size_t avail, _len;
|
||||
@@ -356,6 +362,8 @@ static ssize_t switch_thread_audio_write(void *data, const void *s, size_t len)
|
||||
}
|
||||
else
|
||||
{
|
||||
int laps = SWITCH_AUDIO_WAIT_LAPS;
|
||||
|
||||
_len = 0;
|
||||
while (_len < len && swa->running)
|
||||
{
|
||||
@@ -363,11 +371,18 @@ static ssize_t switch_thread_audio_write(void *data, const void *s, size_t len)
|
||||
avail = FIFO_WRITE_AVAIL(swa->fifo);
|
||||
if (avail == 0)
|
||||
{
|
||||
/* The output thread signals each time it takes from the
|
||||
* fifo. One that has stopped taking - the output closed,
|
||||
* the thread stalled - signals nothing; the wait is timed
|
||||
* and capped, and the write returns what went. */
|
||||
compat_mutex_unlock(&swa->fifoLock);
|
||||
compat_mutex_lock(&swa->condLock);
|
||||
if (swa->running)
|
||||
compat_condvar_wait(&swa->cond, &swa->condLock);
|
||||
compat_condvar_wait_timeout(&swa->cond, &swa->condLock,
|
||||
SWITCH_AUDIO_WAIT_NS);
|
||||
compat_mutex_unlock(&swa->condLock);
|
||||
if (--laps < 0)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -414,6 +429,7 @@ static size_t switch_thread_audio_wait_writable(void *data, size_t len)
|
||||
{
|
||||
size_t avail;
|
||||
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
|
||||
int laps = SWITCH_AUDIO_WAIT_LAPS;
|
||||
|
||||
if (!swa)
|
||||
return 0;
|
||||
@@ -429,10 +445,15 @@ static size_t switch_thread_audio_wait_writable(void *data, size_t len)
|
||||
compat_mutex_unlock(&swa->fifoLock);
|
||||
if (avail >= len)
|
||||
return avail;
|
||||
/* Timed and capped: an output thread that has stopped hands the
|
||||
* pass back as no space coming from this call. */
|
||||
compat_mutex_lock(&swa->condLock);
|
||||
if (swa->running)
|
||||
compat_condvar_wait(&swa->cond, &swa->condLock);
|
||||
compat_condvar_wait_timeout(&swa->cond, &swa->condLock,
|
||||
SWITCH_AUDIO_WAIT_NS);
|
||||
compat_mutex_unlock(&swa->condLock);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ typedef struct
|
||||
#define AX_AUDIO_SAMPLE_MIN (AX_AUDIO_SAMPLE_COUNT * 3) /* 9ms */
|
||||
#define AX_AUDIO_SAMPLE_LOAD (AX_AUDIO_SAMPLE_COUNT * 10)/* 30ms */
|
||||
#define AX_AUDIO_MAX_FREE (AX_AUDIO_COUNT - (AX_AUDIO_SAMPLE_COUNT * 2))
|
||||
/* Bound on a wait for the frame callback to free room: one wait, in
|
||||
* microseconds, and how many before the caller gets the pass back. */
|
||||
#define AX_AUDIO_WAIT_US 100000
|
||||
#define AX_AUDIO_WAIT_LAPS 8
|
||||
#define AX_AUDIO_RATE 48000
|
||||
|
||||
#if 0
|
||||
@@ -224,11 +228,19 @@ static ssize_t ax_audio_write(void* data, const void* buf, size_t len)
|
||||
}
|
||||
else if (count_avail < count)
|
||||
{
|
||||
/* Sync, wait for free memory */
|
||||
/* Sync, wait for free memory. The AX frame callback frees a
|
||||
* frame's worth and signals frame_event each frame; wait on that
|
||||
* rather than yield-spin, and bound it - a voice that reports
|
||||
* running while its callback has stopped freed nothing, and the
|
||||
* spin never ended. The write then takes what room there is. */
|
||||
int laps = AX_AUDIO_WAIT_LAPS;
|
||||
while (AXIsMultiVoiceRunning(ax->mvoice) && (count_avail < count))
|
||||
{
|
||||
OSYieldThread(); /* Gives threads with same priority time to run */
|
||||
OSWaitEventWithTimeout(&ax->frame_event,
|
||||
(OSTime)OSMicroseconds(AX_AUDIO_WAIT_US));
|
||||
count_avail = (ax->written > AX_AUDIO_MAX_FREE ? 0 : (AX_AUDIO_MAX_FREE - ax->written));
|
||||
if (--laps < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +337,7 @@ static size_t ax_audio_wait_writable(void* data, size_t len)
|
||||
{
|
||||
ax_audio_t* ax = (ax_audio_t*)data;
|
||||
size_t avail;
|
||||
int laps = AX_AUDIO_WAIT_LAPS;
|
||||
|
||||
if (len > AX_AUDIO_MAX_FREE / 2)
|
||||
len = AX_AUDIO_MAX_FREE / 2;
|
||||
@@ -337,7 +350,12 @@ static size_t ax_audio_wait_writable(void* data, size_t len)
|
||||
? 0 : (AX_AUDIO_MAX_FREE - ax->written);
|
||||
if (avail >= len)
|
||||
return avail;
|
||||
OSWaitEvent(&ax->frame_event);
|
||||
/* Timed and capped: a callback that has stopped hands the pass
|
||||
* back as no space coming from this call. */
|
||||
OSWaitEventWithTimeout(&ax->frame_event,
|
||||
(OSTime)OSMicroseconds(AX_AUDIO_WAIT_US));
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ static INLINE uint32_t xenon360_bswap_32(uint32_t val)
|
||||
((val >> 8) & 0xff00) | ((val << 8) & 0xff0000);
|
||||
}
|
||||
|
||||
/* How many 50 us delays a blocking write waits for the queue to drain
|
||||
* before giving up on it (about a second). */
|
||||
#define XENON360_AUDIO_WAIT_LAPS 20000
|
||||
|
||||
static ssize_t xenon360_audio_write(void *data, const void *s, size_t len)
|
||||
{
|
||||
size_t _len = 0, i;
|
||||
@@ -83,11 +87,16 @@ static ssize_t xenon360_audio_write(void *data, const void *s, size_t len)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Capped: a sound queue that stops draining never drops below the
|
||||
* mark, and the write then returns having written nothing. */
|
||||
int laps = XENON360_AUDIO_WAIT_LAPS;
|
||||
while (xenon_sound_get_unplayed() >= MAX_BUFFER)
|
||||
{
|
||||
/* libxenon doesn't have proper
|
||||
* synchronization primitives for this... */
|
||||
udelay(50);
|
||||
if (--laps < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xenon_sound_submit(xa->buffer, len);
|
||||
|
||||
Reference in new issue
Block a user