mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2025-01-03 14:35:38 +00:00
ALSA: recover from underflow gracefully
This commit is contained in:
parent
2900616e9b
commit
5503072fc8
|
@ -241,10 +241,10 @@ view `coverage/index.html` in a browser.
|
|||
|
||||
## Roadmap
|
||||
|
||||
0. Integrate into libgroove and test with Groove Basin
|
||||
0. implement CoreAudio (OSX) backend, get examples working
|
||||
0. implement WASAPI (Windows) backend, get examples working
|
||||
0. implement ASIO (Windows) backend, get examples working
|
||||
0. Integrate into libgroove and test with Groove Basin
|
||||
0. Avoid calling `soundio_panic` in PulseAudio.
|
||||
0. PulseAudio: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`.
|
||||
0. clear buffer maybe could take an argument to say how many frames to not clear
|
||||
|
@ -259,6 +259,8 @@ view `coverage/index.html` in a browser.
|
|||
`pa_stream_flush`, then uncork the stream.
|
||||
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
||||
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
||||
0. Create a test for underflow handling. It just makes a sine wave for 5
|
||||
seconds, then on next audio callback, sleep for 2 seconds.
|
||||
0. Create a test for pausing and resuming input and output streams.
|
||||
0. Create a test for the latency / synchronization API.
|
||||
- Input is an audio file and some events indexed at particular frame - when
|
||||
|
|
|
@ -30,7 +30,6 @@ static int usage(char *exe) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const float PI = 3.1415926535f;
|
||||
static float seconds_offset = 0.0f;
|
||||
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
||||
|
@ -61,8 +60,11 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
}
|
||||
seconds_offset += seconds_per_frame * frame_count;
|
||||
|
||||
if ((err = soundio_outstream_end_write(outstream, frame_count)))
|
||||
if ((err = soundio_outstream_end_write(outstream, frame_count))) {
|
||||
if (err == SoundIoErrorUnderflow)
|
||||
return;
|
||||
panic("%s", soundio_strerror(err));
|
||||
}
|
||||
|
||||
requested_frame_count -= frame_count;
|
||||
if (requested_frame_count <= 0)
|
||||
|
|
27
src/alsa.cpp
27
src/alsa.cpp
|
@ -867,7 +867,7 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
|||
deallocate(osa->sample_buffer, osa->sample_buffer_size);
|
||||
}
|
||||
|
||||
static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
||||
static int os_xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
|
||||
if (err == -EPIPE) {
|
||||
|
@ -950,11 +950,13 @@ void outstream_thread_run(void *arg) {
|
|||
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_SETUP:
|
||||
{
|
||||
if ((err = snd_pcm_prepare(osa->handle)) < 0) {
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
{
|
||||
if ((err = snd_pcm_start(osa->handle)) < 0) {
|
||||
|
@ -976,7 +978,7 @@ void outstream_thread_run(void *arg) {
|
|||
|
||||
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
|
||||
if (avail < 0) {
|
||||
if ((err = xrun_recovery(os, avail)) < 0) {
|
||||
if ((err = os_xrun_recovery(os, avail)) < 0) {
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
|
@ -987,13 +989,13 @@ void outstream_thread_run(void *arg) {
|
|||
continue;
|
||||
}
|
||||
case SND_PCM_STATE_XRUN:
|
||||
if ((err = xrun_recovery(os, -EPIPE)) < 0) {
|
||||
if ((err = os_xrun_recovery(os, -EPIPE)) < 0) {
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
if ((err = xrun_recovery(os, -ESTRPIPE)) < 0) {
|
||||
if ((err = os_xrun_recovery(os, -ESTRPIPE)) < 0) {
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
return;
|
||||
}
|
||||
|
@ -1240,20 +1242,11 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
}
|
||||
|
||||
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
|
||||
|
||||
assert(!osa->thread);
|
||||
|
||||
// Give the API user a chance to fill the buffer before playback commences.
|
||||
snd_pcm_sframes_t avail = snd_pcm_avail_update(osa->handle);
|
||||
int err;
|
||||
if (avail < 0) {
|
||||
if ((err = xrun_recovery(os, avail)) < 0)
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
outstream->write_callback(outstream, avail);
|
||||
|
||||
osa->thread_exit_flag.test_and_set();
|
||||
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread)))
|
||||
return err;
|
||||
|
@ -1288,7 +1281,9 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
|||
int err;
|
||||
|
||||
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
|
||||
if ((err = xrun_recovery(os, err)) < 0)
|
||||
if (err == -EPIPE || err == -ESTRPIPE)
|
||||
return SoundIoErrorUnderflow;
|
||||
else
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
|
||||
|
@ -1326,7 +1321,9 @@ static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate
|
|||
|
||||
if (commitres < 0 || commitres != frame_count) {
|
||||
int err = (commitres >= 0) ? -EPIPE : commitres;
|
||||
if ((err = xrun_recovery(os, err)) < 0)
|
||||
if (err == -EPIPE || err == -ESTRPIPE)
|
||||
return SoundIoErrorUnderflow;
|
||||
else
|
||||
return SoundIoErrorStreaming;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ const char *soundio_strerror(int error) {
|
|||
case SoundIoErrorIncompatibleBackend: return "incompatible backend";
|
||||
case SoundIoErrorBackendDisconnected: return "backend disconnected";
|
||||
case SoundIoErrorInterrupted: return "interrupted; try again";
|
||||
case SoundIoErrorUnderflow: return "buffer underflow";
|
||||
}
|
||||
soundio_panic("invalid error enum value: %d", error);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ enum SoundIoError {
|
|||
SoundIoErrorIncompatibleBackend,
|
||||
SoundIoErrorBackendDisconnected,
|
||||
SoundIoErrorInterrupted,
|
||||
SoundIoErrorUnderflow,
|
||||
};
|
||||
|
||||
enum SoundIoChannelId {
|
||||
|
@ -653,6 +654,7 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
|||
|
||||
// Commits the write that you began with `soundio_outstream_begin_write`.
|
||||
// You must call this function only from the `write_callback` thread context.
|
||||
// This function might return `SoundIoErrorUnderflow` but don't count on it.
|
||||
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count);
|
||||
|
||||
// Clears the output stream buffer.
|
||||
|
|
Loading…
Reference in a new issue