diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2020-11-18 14:08:17 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2020-11-18 14:38:12 -0500 |
commit | 3027cead01d8aacce03dc360cedaa4cc44a062fc (patch) | |
tree | f7ff9d4fa1b4ab201580cb3468e36f705b0a9a8d /firmware | |
parent | 473aa615de2528e469a421d01be007e022d0a5b5 (diff) | |
download | rockbox-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
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/pcm_mixer.h | 4 | ||||
-rw-r--r-- | firmware/target/hosted/agptek/debug-agptek.c | 1 | ||||
-rw-r--r-- | firmware/target/hosted/pcm-alsa.c | 65 | ||||
-rw-r--r-- | firmware/target/hosted/pcm-alsa.h | 15 |
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 | |||
65 | static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */ | 71 | static 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; | |||
83 | static const void *pcm_data = 0; | 89 | static const void *pcm_data = 0; |
84 | static size_t pcm_size = 0; | 90 | static size_t pcm_size = 0; |
85 | 91 | ||
92 | static unsigned int xruns = 0; | ||
93 | |||
86 | static snd_async_handler_t *ahandler = NULL; | 94 | static snd_async_handler_t *ahandler = NULL; |
87 | static pthread_mutex_t pcm_mtx; | 95 | static pthread_mutex_t pcm_mtx; |
88 | static char signal_stack[SIGSTKSZ]; | 96 | static char signal_stack[SIGSTKSZ]; |
@@ -117,17 +125,21 @@ static int set_hwparams(snd_pcm_t *handle) | |||
117 | snd_pcm_hw_params_malloc(¶ms); | 125 | snd_pcm_hw_params_malloc(¶ms); |
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 | ||
754 | int pcm_alsa_get_rate(void) | 790 | unsigned int pcm_alsa_get_rate(void) |
755 | { | 791 | { |
756 | return real_sample_rate; | 792 | return real_sample_rate; |
757 | } | 793 | } |
758 | 794 | ||
795 | unsigned int pcm_alsa_get_xruns(void) | ||
796 | { | ||
797 | return xruns; | ||
798 | } | ||
799 | |||
759 | #ifdef HAVE_RECORDING | 800 | #ifdef HAVE_RECORDING |
760 | void pcm_rec_lock(void) | 801 | void 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); | |||
34 | void pcm_alsa_set_capture_device(const char *device); | 34 | void pcm_alsa_set_capture_device(const char *device); |
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | int pcm_alsa_get_rate(void); | 37 | unsigned int pcm_alsa_get_rate(void); |
38 | unsigned int pcm_alsa_get_xruns(void); | ||
38 | 39 | ||
39 | #endif /* __PCM_ALSA_RB_H__ */ | 40 | #endif /* __PCM_ALSA_RB_H__ */ |