summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2020-10-02 20:54:57 -0400
committerSolomon Peachy <pizza@shaftnet.org>2020-10-03 01:04:53 +0000
commitc7eceea183d5fe80d7346a4331dff1b60c8a500c (patch)
tree0812c65662eaa735ce14bc55534f6edd25fa1417
parentb0e1b245b471f2c71d7b74ce882fbaa318e3ab91 (diff)
downloadrockbox-c7eceea183d5fe80d7346a4331dff1b60c8a500c.tar.gz
rockbox-c7eceea183d5fe80d7346a4331dff1b60c8a500c.zip
alsa: Handle underruns when starting a new stream
Unsure why this is happening, but now we detect and recover errors at startup. Also clean up the mismash of printf(), DEBUGF(), etc in favor of panicf() for the really serious stuff and logf() for everything else. Change-Id: I9aaa620d55d556645c9a6d108541b987983b32a8
-rw-r--r--firmware/target/hosted/pcm-alsa.c116
1 files changed, 69 insertions, 47 deletions
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c
index c7df99e23d..d42775e8ef 100644
--- a/firmware/target/hosted/pcm-alsa.c
+++ b/firmware/target/hosted/pcm-alsa.c
@@ -5,9 +5,9 @@
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$
9 * 8 *
10 * Copyright (C) 2010 Thomas Martitz 9 * Copyright (C) 2010 Thomas Martitz
10 * Copyright (c) 2020 Solomon Peachy
11 * 11 *
12 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -41,12 +41,14 @@
41 * device works which doesnt break with other apps running. 41 * device works which doesnt break with other apps running.
42 */ 42 */
43 43
44
45#include "autoconf.h" 44#include "autoconf.h"
46 45
47#include <stdlib.h> 46#include <stdlib.h>
48#include <stdbool.h> 47#include <stdbool.h>
49#include <alsa/asoundlib.h> 48#include <alsa/asoundlib.h>
49
50//#define LOGF_ENABLE
51
50#include "system.h" 52#include "system.h"
51#include "debug.h" 53#include "debug.h"
52#include "kernel.h" 54#include "kernel.h"
@@ -59,6 +61,8 @@
59#include "audiohw.h" 61#include "audiohw.h"
60#include "pcm-alsa.h" 62#include "pcm-alsa.h"
61 63
64#include "logf.h"
65
62#include <pthread.h> 66#include <pthread.h>
63#include <signal.h> 67#include <signal.h>
64 68
@@ -119,28 +123,28 @@ static int set_hwparams(snd_pcm_t *handle)
119 err = snd_pcm_hw_params_any(handle, params); 123 err = snd_pcm_hw_params_any(handle, params);
120 if (err < 0) 124 if (err < 0)
121 { 125 {
122 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); 126 panicf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
123 goto error; 127 goto error;
124 } 128 }
125 /* set the interleaved read/write format */ 129 /* set the interleaved read/write format */
126 err = snd_pcm_hw_params_set_access(handle, params, access_); 130 err = snd_pcm_hw_params_set_access(handle, params, access_);
127 if (err < 0) 131 if (err < 0)
128 { 132 {
129 printf("Access type not available for playback: %s\n", snd_strerror(err)); 133 panicf("Access type not available for playback: %s\n", snd_strerror(err));
130 goto error; 134 goto error;
131 } 135 }
132 /* set the sample format */ 136 /* set the sample format */
133 err = snd_pcm_hw_params_set_format(handle, params, format); 137 err = snd_pcm_hw_params_set_format(handle, params, format);
134 if (err < 0) 138 if (err < 0)
135 { 139 {
136 printf("Sample format not available for playback: %s\n", snd_strerror(err)); 140 logf("Sample format not available for playback: %s\n", snd_strerror(err));
137 goto error; 141 goto error;
138 } 142 }
139 /* set the count of channels */ 143 /* set the count of channels */
140 err = snd_pcm_hw_params_set_channels(handle, params, channels); 144 err = snd_pcm_hw_params_set_channels(handle, params, channels);
141 if (err < 0) 145 if (err < 0)
142 { 146 {
143 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); 147 logf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
144 goto error; 148 goto error;
145 } 149 }
146 /* set the stream rate */ 150 /* set the stream rate */
@@ -148,13 +152,13 @@ static int set_hwparams(snd_pcm_t *handle)
148 err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0); 152 err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0);
149 if (err < 0) 153 if (err < 0)
150 { 154 {
151 printf("Rate %iHz not available for playback: %s\n", sample_rate, snd_strerror(err)); 155 logf("Rate %iHz not available for playback: %s\n", sample_rate, snd_strerror(err));
152 goto error; 156 goto error;
153 } 157 }
154 real_sample_rate = srate; 158 real_sample_rate = srate;
155 if (real_sample_rate != sample_rate) 159 if (real_sample_rate != sample_rate)
156 { 160 {
157 printf("Rate doesn't match (requested %iHz, get %iHz)\n", sample_rate, real_sample_rate); 161 logf("Rate doesn't match (requested %iHz, get %iHz)\n", sample_rate, real_sample_rate);
158 err = -EINVAL; 162 err = -EINVAL;
159 goto error; 163 goto error;
160 } 164 }
@@ -163,7 +167,7 @@ static int set_hwparams(snd_pcm_t *handle)
163 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); 167 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
164 if (err < 0) 168 if (err < 0)
165 { 169 {
166 printf("Unable to set buffer size %ld for playback: %s\n", buffer_size, snd_strerror(err)); 170 logf("Unable to set buffer size %ld for playback: %s\n", buffer_size, snd_strerror(err));
167 goto error; 171 goto error;
168 } 172 }
169 173
@@ -171,18 +175,18 @@ static int set_hwparams(snd_pcm_t *handle)
171 err = snd_pcm_hw_params_set_period_size_near (handle, params, &period_size, NULL); 175 err = snd_pcm_hw_params_set_period_size_near (handle, params, &period_size, NULL);
172 if (err < 0) 176 if (err < 0)
173 { 177 {
174 printf("Unable to set period size %ld for playback: %s\n", period_size, snd_strerror(err)); 178 logf("Unable to set period size %ld for playback: %s\n", period_size, snd_strerror(err));
175 goto error; 179 goto error;
176 } 180 }
177 181
178 free(frames); 182 if (frames) free(frames);
179 frames = calloc(1, period_size * channels * sizeof(sample_t)); 183 frames = calloc(1, period_size * channels * sizeof(sample_t));
180 184
181 /* write the parameters to device */ 185 /* write the parameters to device */
182 err = snd_pcm_hw_params(handle, params); 186 err = snd_pcm_hw_params(handle, params);
183 if (err < 0) 187 if (err < 0)
184 { 188 {
185 printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); 189 logf("Unable to set hw params for playback: %s\n", snd_strerror(err));
186 goto error; 190 goto error;
187 } 191 }
188 192
@@ -204,28 +208,28 @@ static int set_swparams(snd_pcm_t *handle)
204 err = snd_pcm_sw_params_current(handle, swparams); 208 err = snd_pcm_sw_params_current(handle, swparams);
205 if (err < 0) 209 if (err < 0)
206 { 210 {
207 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); 211 logf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
208 goto error; 212 goto error;
209 } 213 }
210 /* start the transfer when the buffer is half full */ 214 /* start the transfer when the buffer is half full */
211 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size / 2); 215 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size / 2);
212 if (err < 0) 216 if (err < 0)
213 { 217 {
214 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); 218 logf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
215 goto error; 219 goto error;
216 } 220 }
217 /* allow the transfer when at least period_size samples can be processed */ 221 /* allow the transfer when at least period_size samples can be processed */
218 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); 222 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
219 if (err < 0) 223 if (err < 0)
220 { 224 {
221 printf("Unable to set avail min for playback: %s\n", snd_strerror(err)); 225 logf("Unable to set avail min for playback: %s\n", snd_strerror(err));
222 goto error; 226 goto error;
223 } 227 }
224 /* write the parameters to the playback device */ 228 /* write the parameters to the playback device */
225 err = snd_pcm_sw_params(handle, swparams); 229 err = snd_pcm_sw_params(handle, swparams);
226 if (err < 0) 230 if (err < 0)
227 { 231 {
228 printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); 232 logf("Unable to set sw params for playback: %s\n", snd_strerror(err));
229 goto error; 233 goto error;
230 } 234 }
231 235
@@ -268,14 +272,14 @@ void pcm_alsa_set_digital_volume(int vol_db_l, int vol_db_r)
268 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 2); 272 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 2);
269 else 273 else
270 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 1); 274 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 1);
271 printf("l: %d dB -> factor = %d\n", vol_db_l - 48, dig_vol_mult_l); 275 logf("l: %d dB -> factor = %d\n", vol_db_l - 48, dig_vol_mult_l);
272 if(r_r == 0) 276 if(r_r == 0)
273 dig_vol_mult_r = 1 << vol_shift_r; 277 dig_vol_mult_r = 1 << vol_shift_r;
274 else if(r_r == 1) 278 else if(r_r == 1)
275 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 2); 279 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 2);
276 else 280 else
277 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 1); 281 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 1);
278 printf("r: %d dB -> factor = %d\n", vol_db_r - 48, dig_vol_mult_r); 282 logf("r: %d dB -> factor = %d\n", vol_db_r - 48, dig_vol_mult_r);
279} 283}
280 284
281/* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */ 285/* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */
@@ -327,6 +331,7 @@ static bool fill_frames(void)
327 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 331 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
328 } 332 }
329 } 333 }
334
330 return true; 335 return true;
331} 336}
332 337
@@ -351,13 +356,13 @@ static void pcm_tick(void)
351 int err = snd_pcm_writei(handle, frames, period_size); 356 int err = snd_pcm_writei(handle, frames, period_size);
352 if (err < 0 && err != period_size && err != -EAGAIN) 357 if (err < 0 && err != period_size && err != -EAGAIN)
353 { 358 {
354 printf("Write error: written %i expected %li\n", err, period_size); 359 logf("Write error: written %i expected %li\n", err, period_size);
355 break; 360 break;
356 } 361 }
357 } 362 }
358 else 363 else
359 { 364 {
360 DEBUGF("%s: No Data.\n", __func__); 365 logf("%s: No Data.\n", __func__);
361 break; 366 break;
362 } 367 }
363 } 368 }
@@ -384,14 +389,14 @@ static int async_rw(snd_pcm_t *handle)
384 err = sigaltstack(&ss, NULL); 389 err = sigaltstack(&ss, NULL);
385 if (err < 0) 390 if (err < 0)
386 { 391 {
387 DEBUGF("Unable to install alternative signal stack: %s", strerror(err)); 392 logf("Unable to install alternative signal stack: %s", strerror(err));
388 return err; 393 return err;
389 } 394 }
390 395
391 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, NULL); 396 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, NULL);
392 if (err < 0) 397 if (err < 0)
393 { 398 {
394 DEBUGF("Unable to register async handler: %s\n", snd_strerror(err)); 399 logf("Unable to register async handler: %s\n", snd_strerror(err));
395 return err; 400 return err;
396 } 401 }
397 402
@@ -401,7 +406,7 @@ static int async_rw(snd_pcm_t *handle)
401 err = sigaction(SIGIO, &sa, NULL); 406 err = sigaction(SIGIO, &sa, NULL);
402 if (err < 0) 407 if (err < 0)
403 { 408 {
404 DEBUGF("Unable to install alternative signal stack: %s", strerror(err)); 409 logf("Unable to install alternative signal stack: %s", strerror(err));
405 return err; 410 return err;
406 } 411 }
407#endif 412#endif
@@ -416,27 +421,31 @@ static int async_rw(snd_pcm_t *handle)
416 421
417 if (err < 0) 422 if (err < 0)
418 { 423 {
419 DEBUGF("Initial write error: %s\n", snd_strerror(err)); 424 logf("Initial write error: %s\n", snd_strerror(err));
420 return err; 425 return err;
421 } 426 }
422 if (err != (ssize_t)sample_size) 427 if (err != (ssize_t)sample_size)
423 { 428 {
424 DEBUGF("Initial write error: written %i expected %li\n", err, sample_size); 429 logf("Initial write error: written %i expected %li\n", err, sample_size);
425 return err; 430 return err;
426 } 431 }
427 if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) 432
433 snd_pcm_state_t state = snd_pcm_state(handle);
434 logf("PCM RW State %d", state);
435 if (state == SND_PCM_STATE_PREPARED)
428 { 436 {
429 err = snd_pcm_start(handle); 437 err = snd_pcm_start(handle);
430 if (err < 0) 438 if (err < 0)
431 { 439 {
432 DEBUGF("Start error: %s\n", snd_strerror(err)); 440 logf("Start error: %s\n", snd_strerror(err));
433 return err; 441 return err;
434 } 442 }
443 } else {
444 return state;
435 } 445 }
436 return 0; 446 return 0;
437} 447}
438 448
439
440void cleanup(void) 449void cleanup(void)
441{ 450{
442 free(frames); 451 free(frames);
@@ -448,6 +457,9 @@ void cleanup(void)
448void pcm_play_dma_init(void) 457void pcm_play_dma_init(void)
449{ 458{
450 int err; 459 int err;
460
461 logf("PCM DMA Init");
462
451 audiohw_preinit(); 463 audiohw_preinit();
452 464
453 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) 465 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
@@ -482,7 +494,6 @@ void pcm_play_dma_init(void)
482 return; 494 return;
483} 495}
484 496
485
486void pcm_play_lock(void) 497void pcm_play_lock(void)
487{ 498{
488#ifdef USE_ASYNC_CALLBACK 499#ifdef USE_ASYNC_CALLBACK
@@ -506,12 +517,13 @@ void pcm_play_unlock(void)
506#if defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_ROCKER_CODEC) 517#if defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_ROCKER_CODEC)
507static void pcm_dma_apply_settings_nolock(void) 518static void pcm_dma_apply_settings_nolock(void)
508{ 519{
520 logf("PCM DMA Settings %d %d", sample_rate, pcm_sampr);
509 if (sample_rate != pcm_sampr) 521 if (sample_rate != pcm_sampr)
510 { 522 {
511 audiohw_mute(true); 523 audiohw_mute(true);
512 snd_pcm_drop(handle); 524 snd_pcm_drop(handle);
513 set_hwparams(handle); 525 set_hwparams(handle);
514 audiohw_mute(false); 526 audiohw_mute(false);
515 } 527 }
516} 528}
517#else 529#else
@@ -533,20 +545,24 @@ void pcm_dma_apply_settings(void)
533 pcm_play_unlock(); 545 pcm_play_unlock();
534} 546}
535 547
536
537void pcm_play_dma_pause(bool pause) 548void pcm_play_dma_pause(bool pause)
538{ 549{
550 logf("PCM DMA pause %d", pause);
539 snd_pcm_pause(handle, pause); 551 snd_pcm_pause(handle, pause);
540} 552}
541 553
542
543void pcm_play_dma_stop(void) 554void pcm_play_dma_stop(void)
544{ 555{
556 snd_pcm_nonblock(handle, 0);
545 snd_pcm_drain(handle); 557 snd_pcm_drain(handle);
558 snd_pcm_nonblock(handle, 1);
559 sample_rate = 0;
560 logf("PCM DMA stopped");
546} 561}
547 562
548void pcm_play_dma_start(const void *addr, size_t size) 563void pcm_play_dma_start(const void *addr, size_t size)
549{ 564{
565 logf("PCM DMA start (%p %d)", addr, size);
550 pcm_dma_apply_settings_nolock(); 566 pcm_dma_apply_settings_nolock();
551 567
552 pcm_data = addr; 568 pcm_data = addr;
@@ -555,32 +571,38 @@ void pcm_play_dma_start(const void *addr, size_t size)
555 while (1) 571 while (1)
556 { 572 {
557 snd_pcm_state_t state = snd_pcm_state(handle); 573 snd_pcm_state_t state = snd_pcm_state(handle);
574 logf("PCM State %d", state);
575
558 switch (state) 576 switch (state)
559 { 577 {
560 case SND_PCM_STATE_RUNNING: 578 case SND_PCM_STATE_RUNNING:
561 return; 579 return;
562 case SND_PCM_STATE_XRUN: 580 case SND_PCM_STATE_XRUN:
563 { 581 {
564 DEBUGF("Trying to recover from error\n"); 582 logf("Trying to recover from error\n");
565 int err = snd_pcm_recover(handle, -EPIPE, 0); 583 int err = snd_pcm_recover(handle, -EPIPE, 0);
566 if (err < 0) 584 if (err < 0)
567 DEBUGF("Recovery failed: %s\n", snd_strerror(err)); 585 logf("Recovery failed: %s\n", snd_strerror(err));
568 continue; 586 continue;
569 } 587 }
570 case SND_PCM_STATE_SETUP: 588 case SND_PCM_STATE_SETUP:
571 { 589 {
572 int err = snd_pcm_prepare(handle); 590 int err = snd_pcm_prepare(handle);
573 if (err < 0) 591 if (err < 0)
574 printf("Prepare error: %s\n", snd_strerror(err)); 592 logf("Prepare error: %s\n", snd_strerror(err));
575 /* fall through */ 593 /* fall through */
576 } 594 }
577 case SND_PCM_STATE_PREPARED: 595 case SND_PCM_STATE_PREPARED:
578 { /* prepared state, we need to fill the buffer with silence before 596 { /* prepared state, we need to fill the buffer with silence before
579 * starting */ 597 * starting */
580 int err = async_rw(handle); 598 int err = async_rw(handle);
581 if (err < 0) 599 if (err < 0) {
582 printf("Start error: %s\n", snd_strerror(err)); 600 logf("Start error: %s\n", snd_strerror(err));
583 return; 601 return;
602 }
603 if (err == 0)
604 return;
605 break;
584 } 606 }
585 case SND_PCM_STATE_PAUSED: 607 case SND_PCM_STATE_PAUSED:
586 { /* paused, simply resume */ 608 { /* paused, simply resume */
@@ -591,7 +613,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
591 /* run until drained */ 613 /* run until drained */
592 continue; 614 continue;
593 default: 615 default:
594 DEBUGF("Unhandled state: %s\n", snd_pcm_state_name(state)); 616 logf("Unhandled state: %s\n", snd_pcm_state_name(state));
595 return; 617 return;
596 } 618 }
597 } 619 }