diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/target/hosted/pcm-alsa.c | 116 |
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 | |||
440 | void cleanup(void) | 449 | void cleanup(void) |
441 | { | 450 | { |
442 | free(frames); | 451 | free(frames); |
@@ -448,6 +457,9 @@ void cleanup(void) | |||
448 | void pcm_play_dma_init(void) | 457 | void 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 | |||
486 | void pcm_play_lock(void) | 497 | void 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) |
507 | static void pcm_dma_apply_settings_nolock(void) | 518 | static 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 | |||
537 | void pcm_play_dma_pause(bool pause) | 548 | void 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 | |||
543 | void pcm_play_dma_stop(void) | 554 | void 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 | ||
548 | void pcm_play_dma_start(const void *addr, size_t size) | 563 | void 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 | } |