summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2020-11-18 14:08:17 -0500
committerSolomon Peachy <pizza@shaftnet.org>2020-11-18 14:38:12 -0500
commit3027cead01d8aacce03dc360cedaa4cc44a062fc (patch)
treef7ff9d4fa1b4ab201580cb3468e36f705b0a9a8d
parent473aa615de2528e469a421d01be007e022d0a5b5 (diff)
downloadrockbox-3027cead01d8aacce03dc360cedaa4cc44a062fc.tar.gz
rockbox-3027cead01d8aacce03dc360cedaa4cc44a062fc.zip
hosted: Improve buffer underrun handling in the ALSA driver
* Bump internal mix buffer size by 4x, to 1K frames (matching ALSA period) * Handle an underrun that occurs when filling the audio buffer * Log underruns and make them available in the debug info Change-Id: I28d56dd35d88851fa167ad92368a5882937a758f
-rw-r--r--firmware/export/pcm_mixer.h4
-rw-r--r--firmware/target/hosted/agptek/debug-agptek.c1
-rw-r--r--firmware/target/hosted/pcm-alsa.c65
-rw-r--r--firmware/target/hosted/pcm-alsa.h15
4 files changed, 66 insertions, 19 deletions
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index 3d255a7345..39a814de6f 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -40,6 +40,10 @@
40/* iBasso Devices: Match Rockbox PCM buffer size to ALSA PCM buffer size 40/* iBasso Devices: Match Rockbox PCM buffer size to ALSA PCM buffer size
41 to minimize memory transfers. */ 41 to minimize memory transfers. */
42#define MIX_FRAME_SAMPLES 2048 42#define MIX_FRAME_SAMPLES 2048
43#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
44/* Hosted targets need larger buffers for decent performance due to
45 OS locking/scheduling overhead */
46#define MIX_FRAME_SAMPLES 1024
43#else 47#else
44/* Assume HW DMA engine is available or sufficient latency exists in the 48/* Assume HW DMA engine is available or sufficient latency exists in the
45 PCM pathway */ 49 PCM pathway */
diff --git a/firmware/target/hosted/agptek/debug-agptek.c b/firmware/target/hosted/agptek/debug-agptek.c
index 8ef0048945..a9b829f7ec 100644
--- a/firmware/target/hosted/agptek/debug-agptek.c
+++ b/firmware/target/hosted/agptek/debug-agptek.c
@@ -62,6 +62,7 @@ bool dbg_hw_info(void)
62 } 62 }
63 63
64 lcd_putsf(0, line++, "pcm srate: %d", pcm_alsa_get_rate()); 64 lcd_putsf(0, line++, "pcm srate: %d", pcm_alsa_get_rate());
65 lcd_putsf(0, line++, "pcm xruns: %d", pcm_alsa_get_xruns());
65#ifdef HAVE_HEADPHONE_DETECTION 66#ifdef HAVE_HEADPHONE_DETECTION
66 lcd_putsf(0, line++, "hp: %d", headphones_inserted()); 67 lcd_putsf(0, line++, "hp: %d", headphones_inserted());
67#endif 68#endif
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c
index 14e1c4cd3e..1854f67ba0 100644
--- a/firmware/target/hosted/pcm-alsa.c
+++ b/firmware/target/hosted/pcm-alsa.c
@@ -62,6 +62,12 @@
62#define DEFAULT_PLAYBACK_DEVICE "plughw:0,0" 62#define DEFAULT_PLAYBACK_DEVICE "plughw:0,0"
63#define DEFAULT_CAPTURE_DEVICE "default" 63#define DEFAULT_CAPTURE_DEVICE "default"
64 64
65#if MIX_FRAME_SAMPLES < 512
66#error "MIX_FRAME_SAMPLES needs to be at least 512!"
67#elif MIX_FRAME_SAMPLES < 1024
68#warning "MIX_FRAME_SAMPLES <1024 may cause dropouts!"
69#endif
70
65static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */ 71static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */
66#if defined(SONY_NWZ_LINUX) || defined(HAVE_FIIO_LINUX_CODEC) 72#if defined(SONY_NWZ_LINUX) || defined(HAVE_FIIO_LINUX_CODEC)
67/* Sony NWZ must use 32-bit per sample */ 73/* Sony NWZ must use 32-bit per sample */
@@ -83,6 +89,8 @@ static sample_t *frames = NULL;
83static const void *pcm_data = 0; 89static const void *pcm_data = 0;
84static size_t pcm_size = 0; 90static size_t pcm_size = 0;
85 91
92static unsigned int xruns = 0;
93
86static snd_async_handler_t *ahandler = NULL; 94static snd_async_handler_t *ahandler = NULL;
87static pthread_mutex_t pcm_mtx; 95static pthread_mutex_t pcm_mtx;
88static char signal_stack[SIGSTKSZ]; 96static char signal_stack[SIGSTKSZ];
@@ -117,17 +125,21 @@ static int set_hwparams(snd_pcm_t *handle)
117 snd_pcm_hw_params_malloc(&params); 125 snd_pcm_hw_params_malloc(&params);
118 126
119 /* Size playback buffers based on sample rate. 127 /* Size playback buffers based on sample rate.
120 Note these are in FRAMES, and are sized to be about 10ms 128
121 for the buffer and 2.5ms for the period */ 129 Buffer size must be at least 4x period size!
130
131 Note these are in FRAMES, and are sized to be about 8.5ms
132 for the buffer and 2.1ms for the period
133 */
122 if (pcm_sampr > SAMPR_96) { 134 if (pcm_sampr > SAMPR_96) {
123 buffer_size = MIX_FRAME_SAMPLES * 16 * 4; /* 32k */ 135 buffer_size = MIX_FRAME_SAMPLES * 4 * 4;
124 period_size = MIX_FRAME_SAMPLES * 4 * 4; /* 4k */ 136 period_size = MIX_FRAME_SAMPLES * 4;
125 } else if (pcm_sampr > SAMPR_48) { 137 } else if (pcm_sampr > SAMPR_48) {
126 buffer_size = MIX_FRAME_SAMPLES * 16 * 2; /* 16k */ 138 buffer_size = MIX_FRAME_SAMPLES * 2 * 4;
127 period_size = MIX_FRAME_SAMPLES * 4 * 2; /* 2k */ 139 period_size = MIX_FRAME_SAMPLES * 2;
128 } else { 140 } else {
129 buffer_size = MIX_FRAME_SAMPLES * 16; /* 4k */ 141 buffer_size = MIX_FRAME_SAMPLES * 4;
130 period_size = MIX_FRAME_SAMPLES * 4; /* 1k */ 142 period_size = MIX_FRAME_SAMPLES;
131 } 143 }
132 144
133 /* choose all parameters */ 145 /* choose all parameters */
@@ -407,7 +419,8 @@ static void async_callback(snd_async_handler_t *ahandler)
407 419
408 if (state == SND_PCM_STATE_XRUN) 420 if (state == SND_PCM_STATE_XRUN)
409 { 421 {
410 logf("underrun!"); 422 xruns++;
423 logf("initial underrun!");
411 err = snd_pcm_recover(handle, -EPIPE, 0); 424 err = snd_pcm_recover(handle, -EPIPE, 0);
412 if (err < 0) { 425 if (err < 0) {
413 logf("XRUN Recovery error: %s", snd_strerror(err)); 426 logf("XRUN Recovery error: %s", snd_strerror(err));
@@ -432,8 +445,20 @@ static void async_callback(snd_async_handler_t *ahandler)
432 { 445 {
433 if (copy_frames(false)) 446 if (copy_frames(false))
434 { 447 {
448 retry:
435 err = snd_pcm_writei(handle, frames, period_size); 449 err = snd_pcm_writei(handle, frames, period_size);
436 if (err < 0 && err != period_size && err != -EAGAIN) 450 if (err == -EPIPE)
451 {
452 logf("mid underrun!");
453 xruns++;
454 err = snd_pcm_recover(handle, -EPIPE, 0);
455 if (err < 0) {
456 logf("XRUN Recovery error: %s", snd_strerror(err));
457 goto abort;
458 }
459 goto retry;
460 }
461 else if (err != period_size)
437 { 462 {
438 logf("Write error: written %i expected %li", err, period_size); 463 logf("Write error: written %i expected %li", err, period_size);
439 break; 464 break;
@@ -452,7 +477,18 @@ static void async_callback(snd_async_handler_t *ahandler)
452 while (snd_pcm_avail_update(handle) >= period_size) 477 while (snd_pcm_avail_update(handle) >= period_size)
453 { 478 {
454 int err = snd_pcm_readi(handle, frames, period_size); 479 int err = snd_pcm_readi(handle, frames, period_size);
455 if (err < 0 && err != period_size && err != -EAGAIN) 480 if (err == -EPIPE)
481 {
482 logf("rec mid underrun!");
483 xruns++;
484 err = snd_pcm_recover(handle, -EPIPE, 0);
485 if (err < 0) {
486 logf("XRUN Recovery error: %s", snd_strerror(err));
487 goto abort;
488 }
489 continue; /* buffer contents trashed, no sense in trying to copy */
490 }
491 else if (err != period_size)
456 { 492 {
457 logf("Read error: read %i expected %li", err, period_size); 493 logf("Read error: read %i expected %li", err, period_size);
458 break; 494 break;
@@ -751,11 +787,16 @@ void pcm_play_dma_postinit(void)
751#endif 787#endif
752} 788}
753 789
754int pcm_alsa_get_rate(void) 790unsigned int pcm_alsa_get_rate(void)
755{ 791{
756 return real_sample_rate; 792 return real_sample_rate;
757} 793}
758 794
795unsigned int pcm_alsa_get_xruns(void)
796{
797 return xruns;
798}
799
759#ifdef HAVE_RECORDING 800#ifdef HAVE_RECORDING
760void pcm_rec_lock(void) 801void pcm_rec_lock(void)
761{ 802{
diff --git a/firmware/target/hosted/pcm-alsa.h b/firmware/target/hosted/pcm-alsa.h
index 1392593c0c..4c0b0d0b9d 100644
--- a/firmware/target/hosted/pcm-alsa.h
+++ b/firmware/target/hosted/pcm-alsa.h
@@ -1,10 +1,10 @@
1/*************************************************************************** 1/***************************************************************************
2 * __________ __ ___. 2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * 8 *
9 * Copyright (C) 2016 Amaury Pouly 9 * Copyright (C) 2016 Amaury Pouly
10 * 10 *
@@ -34,6 +34,7 @@ void pcm_alsa_set_playback_device(const char *device);
34void pcm_alsa_set_capture_device(const char *device); 34void pcm_alsa_set_capture_device(const char *device);
35#endif 35#endif
36 36
37int pcm_alsa_get_rate(void); 37unsigned int pcm_alsa_get_rate(void);
38unsigned int pcm_alsa_get_xruns(void);
38 39
39#endif /* __PCM_ALSA_RB_H__ */ 40#endif /* __PCM_ALSA_RB_H__ */