summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2020-10-22 17:15:45 -0400
committerSolomon Peachy <pizza@shaftnet.org>2020-10-23 17:48:51 -0400
commit46e357f1bbd459e0c4e3c1d620c8f790cb910c6a (patch)
tree32a074942abf18c40783cad0e6ff9ff425ad3fc1
parent2cf75bf008e851831aa8f70ae47be5f980686244 (diff)
downloadrockbox-46e357f1bbd459e0c4e3c1d620c8f790cb910c6a.tar.gz
rockbox-46e357f1bbd459e0c4e3c1d620c8f790cb910c6a.zip
ALSA: Further rework:
* Get rid of non-async (ie tick task) mode due to inherent brokenness * Get rid of nonblock mode; we never write if buffers aren't sufficient * Move driver init into pcm_open() instead of pcm_init() * Much better underrun handling * Better error handling in some situations * Add in recording functionality * Use smaller audio buffers to avoid glitching * Don't start audio buffer with silence * Allow device name to be overridden by target Recording portions based on work done by Lorenzo Miori in g#633 Change-Id: I0197bdc1749c28109eb79def1e6a3e1d62d8cef3
-rw-r--r--firmware/target/hosted/pcm-alsa.c556
-rw-r--r--firmware/target/hosted/pcm-alsa.h6
2 files changed, 370 insertions, 192 deletions
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c
index baa841ebe9..14f5160e24 100644
--- a/firmware/target/hosted/pcm-alsa.c
+++ b/firmware/target/hosted/pcm-alsa.c
@@ -19,25 +19,16 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22
23/* 22/*
24 * Based, but heavily modified, on the example given at 23 * Based, but heavily modified, on the example given at
25 * http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html 24 * http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html
26 * 25 *
27 * This driver uses the so-called unsafe async callback method and hardcoded device 26 * This driver uses the so-called unsafe async callback method.
28 * names. It fails when the audio device is busy by other apps.
29 * 27 *
30 * To make the async callback safer, an alternative stack is installed, since 28 * To make the async callback safer, an alternative stack is installed, since
31 * it's run from a signal hanlder (which otherwise uses the user stack). If 29 * it's run from a signal hanlder (which otherwise uses the user stack).
32 * tick tasks are run from a signal handler too, please install
33 * an alternative stack for it too.
34 *
35 * TODO: Rewrite this to do it properly with multithreading
36 * 30 *
37 * Alternatively, a version using polling in a tick task is provided. While 31 * TODO: Rewrite this to properly use multithreading and/or direct mmap()
38 * supposedly safer, it appears to use more CPU (however I didn't measure it
39 * accurately, only looked at htop). At least, in this mode the "default"
40 * device works which doesnt break with other apps running.
41 */ 32 */
42 33
43#include "autoconf.h" 34#include "autoconf.h"
@@ -69,8 +60,7 @@
69 * default doesnt seem to work with async callback but doesn't break 60 * default doesnt seem to work with async callback but doesn't break
70 * with multple applications running */ 61 * with multple applications running */
71#define DEFAULT_PLAYBACK_DEVICE "plughw:0,0" 62#define DEFAULT_PLAYBACK_DEVICE "plughw:0,0"
72 63#define DEFAULT_CAPTURE_DEVICE "default"
73#define USE_ASYNC_CALLBACK
74 64
75static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */ 65static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */
76#if defined(SONY_NWZ_LINUX) || defined(HAVE_FIIO_LINUX_CODEC) 66#if defined(SONY_NWZ_LINUX) || defined(HAVE_FIIO_LINUX_CODEC)
@@ -93,12 +83,30 @@ static sample_t *frames = NULL;
93static const void *pcm_data = 0; 83static const void *pcm_data = 0;
94static size_t pcm_size = 0; 84static size_t pcm_size = 0;
95 85
96#ifdef USE_ASYNC_CALLBACK 86static snd_async_handler_t *ahandler = NULL;
97static snd_async_handler_t *ahandler;
98static pthread_mutex_t pcm_mtx; 87static pthread_mutex_t pcm_mtx;
99static char signal_stack[SIGSTKSZ]; 88static char signal_stack[SIGSTKSZ];
100#else 89
101static int recursion; 90static const char *playback_dev = DEFAULT_PLAYBACK_DEVICE;
91
92#ifdef HAVE_RECORDING
93static void *pcm_data_rec = DEFAULT_CAPTURE_DEVICE;
94static const char *capture_dev = NULL;
95static snd_pcm_stream_t current_alsa_mode; /* SND_PCM_STREAM_PLAYBACK / _CAPTURE */
96#endif
97
98static const char *current_alsa_device;
99
100void pcm_alsa_set_playback_device(const char *device)
101{
102 playback_dev = device;
103}
104
105#ifdef HAVE_RECORDING
106void pcm_alsa_set_capture_device(const char *device)
107{
108 capture_dev = device;
109}
102#endif 110#endif
103 111
104static int set_hwparams(snd_pcm_t *handle) 112static int set_hwparams(snd_pcm_t *handle)
@@ -108,44 +116,46 @@ static int set_hwparams(snd_pcm_t *handle)
108 snd_pcm_hw_params_t *params; 116 snd_pcm_hw_params_t *params;
109 snd_pcm_hw_params_malloc(&params); 117 snd_pcm_hw_params_malloc(&params);
110 118
111 /* Size playback buffers based on sample rate */ 119 /* Size playback buffers based on sample rate.
120 Note these are in FRAMES, and are sized to be about 10ms
121 for the buffer and 2.5ms for the period */
112 if (pcm_sampr > SAMPR_96) { 122 if (pcm_sampr > SAMPR_96) {
113 buffer_size = MIX_FRAME_SAMPLES * 32 * 4; /* ~64k */ 123 buffer_size = MIX_FRAME_SAMPLES * 16 * 4; /* 32k */
114 period_size = MIX_FRAME_SAMPLES * 4 * 4; /* ~16k */ 124 period_size = MIX_FRAME_SAMPLES * 4 * 4; /* 4k */
115 } else if (pcm_sampr > SAMPR_48) { 125 } else if (pcm_sampr > SAMPR_48) {
116 buffer_size = MIX_FRAME_SAMPLES * 32 * 2; /* ~32k */ 126 buffer_size = MIX_FRAME_SAMPLES * 16 * 2; /* 16k */
117 period_size = MIX_FRAME_SAMPLES * 4 * 2; /* ~8k */ 127 period_size = MIX_FRAME_SAMPLES * 4 * 2; /* 2k */
118 } else { 128 } else {
119 buffer_size = MIX_FRAME_SAMPLES * 32; /* ~16k */ 129 buffer_size = MIX_FRAME_SAMPLES * 16; /* 4k */
120 period_size = MIX_FRAME_SAMPLES * 4; /* ~4k */ 130 period_size = MIX_FRAME_SAMPLES * 4; /* 1k */
121 } 131 }
122 132
123 /* choose all parameters */ 133 /* choose all parameters */
124 err = snd_pcm_hw_params_any(handle, params); 134 err = snd_pcm_hw_params_any(handle, params);
125 if (err < 0) 135 if (err < 0)
126 { 136 {
127 panicf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); 137 panicf("Broken configuration for playback: no configurations available: %s", snd_strerror(err));
128 goto error; 138 goto error;
129 } 139 }
130 /* set the interleaved read/write format */ 140 /* set the interleaved read/write format */
131 err = snd_pcm_hw_params_set_access(handle, params, access_); 141 err = snd_pcm_hw_params_set_access(handle, params, access_);
132 if (err < 0) 142 if (err < 0)
133 { 143 {
134 panicf("Access type not available for playback: %s\n", snd_strerror(err)); 144 panicf("Access type not available for playback: %s", snd_strerror(err));
135 goto error; 145 goto error;
136 } 146 }
137 /* set the sample format */ 147 /* set the sample format */
138 err = snd_pcm_hw_params_set_format(handle, params, format); 148 err = snd_pcm_hw_params_set_format(handle, params, format);
139 if (err < 0) 149 if (err < 0)
140 { 150 {
141 logf("Sample format not available for playback: %s\n", snd_strerror(err)); 151 logf("Sample format not available for playback: %s", snd_strerror(err));
142 goto error; 152 goto error;
143 } 153 }
144 /* set the count of channels */ 154 /* set the count of channels */
145 err = snd_pcm_hw_params_set_channels(handle, params, channels); 155 err = snd_pcm_hw_params_set_channels(handle, params, channels);
146 if (err < 0) 156 if (err < 0)
147 { 157 {
148 logf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); 158 logf("Channels count (%i) not available for playbacks: %s", channels, snd_strerror(err));
149 goto error; 159 goto error;
150 } 160 }
151 /* set the stream rate */ 161 /* set the stream rate */
@@ -153,13 +163,13 @@ static int set_hwparams(snd_pcm_t *handle)
153 err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0); 163 err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0);
154 if (err < 0) 164 if (err < 0)
155 { 165 {
156 logf("Rate %iHz not available for playback: %s\n", pcm_sampr, snd_strerror(err)); 166 logf("Rate %luHz not available for playback: %s", pcm_sampr, snd_strerror(err));
157 goto error; 167 goto error;
158 } 168 }
159 real_sample_rate = srate; 169 real_sample_rate = srate;
160 if (real_sample_rate != pcm_sampr) 170 if (real_sample_rate != pcm_sampr)
161 { 171 {
162 logf("Rate doesn't match (requested %iHz, get %iHz)\n", pcm_sampr, real_sample_rate); 172 logf("Rate doesn't match (requested %luHz, get %dHz)", pcm_sampr, real_sample_rate);
163 err = -EINVAL; 173 err = -EINVAL;
164 goto error; 174 goto error;
165 } 175 }
@@ -168,7 +178,7 @@ static int set_hwparams(snd_pcm_t *handle)
168 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); 178 err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
169 if (err < 0) 179 if (err < 0)
170 { 180 {
171 logf("Unable to set buffer size %ld for playback: %s\n", buffer_size, snd_strerror(err)); 181 logf("Unable to set buffer size %ld for playback: %s", buffer_size, snd_strerror(err));
172 goto error; 182 goto error;
173 } 183 }
174 184
@@ -176,7 +186,7 @@ static int set_hwparams(snd_pcm_t *handle)
176 err = snd_pcm_hw_params_set_period_size_near (handle, params, &period_size, NULL); 186 err = snd_pcm_hw_params_set_period_size_near (handle, params, &period_size, NULL);
177 if (err < 0) 187 if (err < 0)
178 { 188 {
179 logf("Unable to set period size %ld for playback: %s\n", period_size, snd_strerror(err)); 189 logf("Unable to set period size %ld for playback: %s", period_size, snd_strerror(err));
180 goto error; 190 goto error;
181 } 191 }
182 192
@@ -187,7 +197,7 @@ static int set_hwparams(snd_pcm_t *handle)
187 err = snd_pcm_hw_params(handle, params); 197 err = snd_pcm_hw_params(handle, params);
188 if (err < 0) 198 if (err < 0)
189 { 199 {
190 logf("Unable to set hw params for playback: %s\n", snd_strerror(err)); 200 logf("Unable to set hw params for playback: %s", snd_strerror(err));
191 goto error; 201 goto error;
192 } 202 }
193 203
@@ -209,28 +219,28 @@ static int set_swparams(snd_pcm_t *handle)
209 err = snd_pcm_sw_params_current(handle, swparams); 219 err = snd_pcm_sw_params_current(handle, swparams);
210 if (err < 0) 220 if (err < 0)
211 { 221 {
212 logf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); 222 logf("Unable to determine current swparams for playback: %s", snd_strerror(err));
213 goto error; 223 goto error;
214 } 224 }
215 /* start the transfer when the buffer is half full */ 225 /* start the transfer when the buffer is half full */
216 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size / 2); 226 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size / 2);
217 if (err < 0) 227 if (err < 0)
218 { 228 {
219 logf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); 229 logf("Unable to set start threshold mode for playback: %s", snd_strerror(err));
220 goto error; 230 goto error;
221 } 231 }
222 /* allow the transfer when at least period_size samples can be processed */ 232 /* allow the transfer when at least period_size samples can be processed */
223 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); 233 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
224 if (err < 0) 234 if (err < 0)
225 { 235 {
226 logf("Unable to set avail min for playback: %s\n", snd_strerror(err)); 236 logf("Unable to set avail min for playback: %s", snd_strerror(err));
227 goto error; 237 goto error;
228 } 238 }
229 /* write the parameters to the playback device */ 239 /* write the parameters to the playback device */
230 err = snd_pcm_sw_params(handle, swparams); 240 err = snd_pcm_sw_params(handle, swparams);
231 if (err < 0) 241 if (err < 0)
232 { 242 {
233 logf("Unable to set sw params for playback: %s\n", snd_strerror(err)); 243 logf("Unable to set sw params for playback: %s", snd_strerror(err));
234 goto error; 244 goto error;
235 } 245 }
236 246
@@ -273,20 +283,20 @@ void pcm_alsa_set_digital_volume(int vol_db_l, int vol_db_r)
273 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 2); 283 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 2);
274 else 284 else
275 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 1); 285 dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 1);
276 logf("l: %d dB -> factor = %d\n", vol_db_l - 48, dig_vol_mult_l); 286 logf("l: %d dB -> factor = %d", vol_db_l - 48, dig_vol_mult_l);
277 if(r_r == 0) 287 if(r_r == 0)
278 dig_vol_mult_r = 1 << vol_shift_r; 288 dig_vol_mult_r = 1 << vol_shift_r;
279 else if(r_r == 1) 289 else if(r_r == 1)
280 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 2); 290 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 2);
281 else 291 else
282 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 1); 292 dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 1);
283 logf("r: %d dB -> factor = %d\n", vol_db_r - 48, dig_vol_mult_r); 293 logf("r: %d dB -> factor = %d", vol_db_r - 48, dig_vol_mult_r);
284} 294}
285 295
286/* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */ 296/* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */
287static bool fill_frames(void) 297static bool copy_frames(bool first)
288{ 298{
289 ssize_t copy_n, frames_left = period_size; 299 ssize_t nframes, frames_left = period_size;
290 bool new_buffer = false; 300 bool new_buffer = false;
291 301
292 while (frames_left > 0) 302 while (frames_left > 0)
@@ -294,24 +304,41 @@ static bool fill_frames(void)
294 if (!pcm_size) 304 if (!pcm_size)
295 { 305 {
296 new_buffer = true; 306 new_buffer = true;
297 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &pcm_data, 307#ifdef HAVE_RECORDING
298 &pcm_size)) 308 switch (current_alsa_mode)
299 { 309 {
300 return false; 310 case SND_PCM_STREAM_PLAYBACK:
311#endif
312 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &pcm_data, &pcm_size))
313 {
314 return false;
315 }
316#ifdef HAVE_RECORDING
317 break;
318 case SND_PCM_STREAM_CAPTURE:
319 if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &pcm_data, &pcm_size))
320 {
321 return false;
322 }
323 break;
324 default:
325 break;
301 } 326 }
327#endif
302 } 328 }
303 329
330 /* Note: This assumes stereo 16-bit */
304 if (pcm_size % 4) 331 if (pcm_size % 4)
305 panicf("Wrong pcm_size"); 332 panicf("Wrong pcm_size");
306 /* the compiler will optimize this test away */ 333 /* the compiler will optimize this test away */
307 copy_n = MIN((ssize_t)pcm_size/4, frames_left); 334 nframes = MIN((ssize_t)pcm_size/4, frames_left);
308 if (format == SND_PCM_FORMAT_S32_LE) 335 if (format == SND_PCM_FORMAT_S32_LE)
309 { 336 {
310 /* We have to convert 16-bit to 32-bit, the need to multiply the 337 /* We have to convert 16-bit to 32-bit, the need to multiply the
311 * sample by some value so the sound is not too low */ 338 * sample by some value so the sound is not too low */
312 const short *pcm_ptr = pcm_data; 339 const short *pcm_ptr = pcm_data;
313 sample_t *sample_ptr = &frames[2*(period_size-frames_left)]; 340 sample_t *sample_ptr = &frames[2*(period_size-frames_left)];
314 for (int i = 0; i < copy_n; i++) 341 for (int i = 0; i < nframes; i++)
315 { 342 {
316 *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_l; 343 *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_l;
317 *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_r; 344 *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_r;
@@ -319,66 +346,203 @@ static bool fill_frames(void)
319 } 346 }
320 else 347 else
321 { 348 {
322 /* Rockbox and PCM have same format: memcopy */ 349#ifdef HAVE_RECORDING
323 memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n * 4); 350 switch (current_alsa_mode)
351 {
352 case SND_PCM_STREAM_PLAYBACK:
353#endif
354 /* Rockbox and PCM have same format: memcopy */
355 memcpy(&frames[2*(period_size-frames_left)], pcm_data, nframes * 4);
356#ifdef HAVE_RECORDING
357 break;
358 case SND_PCM_STREAM_CAPTURE:
359 memcpy(pcm_data_rec, &frames[2*(period_size-frames_left)], nframes * 4);
360 break;
361 default:
362 break;
363 }
364#endif
324 } 365 }
325 pcm_data += copy_n*4; 366 pcm_data += nframes*4;
326 pcm_size -= copy_n*4; 367 pcm_size -= nframes*4;
327 frames_left -= copy_n; 368 frames_left -= nframes;
328 369
329 if (new_buffer) 370 if (new_buffer && !first)
330 { 371 {
331 new_buffer = false; 372 new_buffer = false;
332 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 373#ifdef HAVE_RECORDING
374 switch (current_alsa_mode)
375 {
376 case SND_PCM_STREAM_PLAYBACK:
377#endif
378 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
379#ifdef HAVE_RECORDING
380 break;
381 case SND_PCM_STREAM_CAPTURE:
382 pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
383 break;
384 default:
385 break;
386 }
387#endif
333 } 388 }
334 } 389 }
335 390
336 return true; 391 return true;
337} 392}
338 393
339#ifdef USE_ASYNC_CALLBACK
340static void async_callback(snd_async_handler_t *ahandler) 394static void async_callback(snd_async_handler_t *ahandler)
341{ 395{
396 int err;
397
398 if (!ahandler) return;
399
342 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); 400 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
343 401
402 if (!handle) return;
403
344 if (pthread_mutex_trylock(&pcm_mtx) != 0) 404 if (pthread_mutex_trylock(&pcm_mtx) != 0)
345 return; 405 return;
346#else
347static void pcm_tick(void)
348{
349 if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
350 return;
351#endif
352 406
353 while (snd_pcm_avail_update(handle) >= period_size) 407 snd_pcm_state_t state = snd_pcm_state(handle);
408
409 if (state == SND_PCM_STATE_XRUN)
410 {
411 logf("underrun!");
412 err = snd_pcm_recover(handle, -EPIPE, 0);
413 if (err < 0) {
414 logf("XRUN Recovery error: %s", snd_strerror(err));
415 goto abort;
416 }
417 }
418 else if (state == SND_PCM_STATE_DRAINING)
354 { 419 {
355 if (fill_frames()) 420 logf("draining...");
421 goto abort;
422 }
423 else if (state == SND_PCM_STATE_SETUP)
424 {
425 goto abort;
426 }
427
428#ifdef HAVE_RECORDING
429 if (current_alsa_mode == SND_PCM_STREAM_PLAYBACK)
430 {
431#endif
432 while (snd_pcm_avail_update(handle) >= period_size)
356 { 433 {
357 int err = snd_pcm_writei(handle, frames, period_size); 434 if (copy_frames(false))
358 if (err < 0 && err != period_size && err != -EAGAIN) 435 {
436 err = snd_pcm_writei(handle, frames, period_size);
437 if (err < 0 && err != period_size && err != -EAGAIN)
438 {
439 logf("Write error: written %i expected %li", err, period_size);
440 break;
441 }
442 }
443 else
359 { 444 {
360 logf("Write error: written %i expected %li\n", err, period_size); 445 logf("%s: No Data (%d).", __func__, state);
361 break; 446 break;
362 } 447 }
363 } 448 }
364 else 449#ifdef HAVE_RECORDING
450 }
451 else if (current_alsa_mode == SND_PCM_STREAM_CAPTURE)
452 {
453 while (snd_pcm_avail_update(handle) >= period_size)
365 { 454 {
366 logf("%s: No Data.\n", __func__); 455 int err = snd_pcm_readi(handle, frames, period_size);
367 break; 456 if (err < 0 && err != period_size && err != -EAGAIN)
457 {
458 logf("Read error: read %i expected %li", err, period_size);
459 break;
460 }
461
462 /* start the fake DMA transfer */
463 if (!copy_frames(false))
464 {
465 /* do not spam logf */
466 /* logf("%s: No Data.", __func__); */
467 break;
468 }
368 } 469 }
369 } 470 }
370#ifdef USE_ASYNC_CALLBACK 471#endif
472
473 if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED)
474 {
475 err = snd_pcm_start(handle);
476 if (err < 0) {
477 logf("cb start error: %s", snd_strerror(err));
478 goto abort;
479 }
480 }
481
482abort:
371 pthread_mutex_unlock(&pcm_mtx); 483 pthread_mutex_unlock(&pcm_mtx);
484}
485
486static void close_hwdev(void)
487{
488 logf("closedev (%p)", handle);
489
490 if (handle) {
491 snd_pcm_drain(handle);
492#ifdef AUDIOHW_MUTE_ON_PAUSE
493 audiohw_mute(true);
372#endif 494#endif
495 if (ahandler) {
496 snd_async_del_handler(ahandler);
497 ahandler = NULL;
498 }
499 snd_pcm_close(handle);
500
501 handle = NULL;
502 }
503 current_alsa_device = NULL;
504
505#ifdef HAVE_RECORDING
506 pcm_data_rec = NULL;
507#endif
508}
509
510static void alsadev_cleanup(void)
511{
512 free(frames);
513 frames = NULL;
514 close_hwdev();
373} 515}
374 516
375static int async_rw(snd_pcm_t *handle) 517static void open_hwdev(const char *device, snd_pcm_stream_t mode)
376{ 518{
377 int err; 519 int err;
378 snd_pcm_sframes_t sample_size;
379 sample_t *samples;
380 520
381#ifdef USE_ASYNC_CALLBACK 521 logf("opendev %s (%p)", device, handle);
522
523 if (handle && device == current_alsa_device
524#ifdef HAVE_RECORDING
525 && current_alsa_mode == mode
526#endif
527 )
528 {
529 return;
530 }
531
532 /* Close old handle */
533 close_hwdev();
534
535 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
536 {
537 panicf("%s(): Cannot open device %s: %s", __func__, device, snd_strerror(err));
538 }
539 last_sample_rate = 0;
540
541 pthread_mutexattr_t attr;
542 pthread_mutexattr_init(&attr);
543 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
544 pthread_mutex_init(&pcm_mtx, &attr);
545
382 /* assign alternative stack for the signal handlers */ 546 /* assign alternative stack for the signal handlers */
383 stack_t ss = { 547 stack_t ss = {
384 .ss_sp = signal_stack, 548 .ss_sp = signal_stack,
@@ -390,15 +554,13 @@ static int async_rw(snd_pcm_t *handle)
390 err = sigaltstack(&ss, NULL); 554 err = sigaltstack(&ss, NULL);
391 if (err < 0) 555 if (err < 0)
392 { 556 {
393 logf("Unable to install alternative signal stack: %s", strerror(err)); 557 panicf("Unable to install alternative signal stack: %s", strerror(err));
394 return err;
395 } 558 }
396 559
397 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, NULL); 560 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, NULL);
398 if (err < 0) 561 if (err < 0)
399 { 562 {
400 logf("Unable to register async handler: %s\n", snd_strerror(err)); 563 panicf("Unable to register async handler: %s", snd_strerror(err));
401 return err;
402 } 564 }
403 565
404 /* only modify the stack the handler runs on */ 566 /* only modify the stack the handler runs on */
@@ -407,76 +569,17 @@ static int async_rw(snd_pcm_t *handle)
407 err = sigaction(SIGIO, &sa, NULL); 569 err = sigaction(SIGIO, &sa, NULL);
408 if (err < 0) 570 if (err < 0)
409 { 571 {
410 logf("Unable to install alternative signal stack: %s", strerror(err)); 572 panicf("Unable to install alternative signal stack: %s", strerror(err));
411 return err;
412 }
413#endif
414
415 /* fill buffer with silence to initiate playback without noisy click */
416 sample_size = buffer_size;
417 samples = calloc(1, sample_size * channels * sizeof(sample_t));
418
419 snd_pcm_format_set_silence(format, samples, sample_size);
420 err = snd_pcm_writei(handle, samples, sample_size);
421 free(samples);
422
423 if (err < 0)
424 {
425 logf("Initial write error: %s\n", snd_strerror(err));
426 return err;
427 }
428 if (err != (ssize_t)sample_size)
429 {
430 logf("Initial write error: written %i expected %li\n", err, sample_size);
431 return err;
432 } 573 }
433 574
434 snd_pcm_state_t state = snd_pcm_state(handle); 575#ifdef HAVE_RECORDING
435 logf("PCM RW State %d", state); 576 current_alsa_mode = mode;
436 if (state == SND_PCM_STATE_PREPARED) 577#else
437 { 578 (void)mode;
438 err = snd_pcm_start(handle); 579#endif
439 if (err < 0) 580 current_alsa_device = device;
440 {
441 logf("Start error: %s\n", snd_strerror(err));
442 return err;
443 }
444 } else {
445 return state;
446 }
447
448 return 0;
449}
450
451void cleanup(void)
452{
453 free(frames);
454 frames = NULL;
455 snd_pcm_close(handle);
456 handle = NULL;
457}
458
459static void open_hwdev(const char *device)
460{
461 int err;
462
463 logf("opendev %s (%p)", device, handle);
464
465 /* Close old handle first, if needed */
466 if (handle) {
467 pcm_play_dma_stop();
468 snd_pcm_close(handle);
469 handle = NULL;
470 }
471
472 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
473 {
474 panicf("%s(): Cannot open device %s: %s\n", __func__, device, snd_strerror(err));
475 }
476 if ((err = snd_pcm_nonblock(handle, 1)))
477 panicf("Could not set non-block mode: %s\n", snd_strerror(err));
478 581
479 last_sample_rate = 0; 582 atexit(alsadev_cleanup);
480} 583}
481 584
482void pcm_play_dma_init(void) 585void pcm_play_dma_init(void)
@@ -485,51 +588,30 @@ void pcm_play_dma_init(void)
485 588
486 audiohw_preinit(); 589 audiohw_preinit();
487 590
488 open_hwdev(DEFAULT_PLAYBACK_DEVICE); 591 open_hwdev(playback_dev, SND_PCM_STREAM_PLAYBACK);
489
490#ifdef USE_ASYNC_CALLBACK
491 pthread_mutexattr_t attr;
492 pthread_mutexattr_init(&attr);
493 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
494 pthread_mutex_init(&pcm_mtx, &attr);
495#else
496 tick_add_task(pcm_tick);
497#endif
498 592
499 atexit(cleanup);
500 return; 593 return;
501} 594}
502 595
503void pcm_play_lock(void) 596void pcm_play_lock(void)
504{ 597{
505#ifdef USE_ASYNC_CALLBACK
506 pthread_mutex_lock(&pcm_mtx); 598 pthread_mutex_lock(&pcm_mtx);
507#else
508 if (recursion++ == 0)
509 tick_remove_task(pcm_tick);
510#endif
511} 599}
512 600
513void pcm_play_unlock(void) 601void pcm_play_unlock(void)
514{ 602{
515#ifdef USE_ASYNC_CALLBACK
516 pthread_mutex_unlock(&pcm_mtx); 603 pthread_mutex_unlock(&pcm_mtx);
517#else
518 if (--recursion == 0)
519 tick_add_task(pcm_tick);
520#endif
521} 604}
522 605
523static void pcm_dma_apply_settings_nolock(void) 606static void pcm_dma_apply_settings_nolock(void)
524{ 607{
525 logf("PCM DMA Settings %d %d", last_sample_rate, pcm_sampr); 608 logf("PCM DMA Settings %d %lu", last_sample_rate, pcm_sampr);
526 609
527 if (last_sample_rate != pcm_sampr) 610 if (last_sample_rate != pcm_sampr)
528 { 611 {
529 last_sample_rate = pcm_sampr; 612 last_sample_rate = pcm_sampr;
530 613
531#ifdef AUDIOHW_MUTE_ON_SRATE_CHANGE 614#ifdef AUDIOHW_MUTE_ON_SRATE_CHANGE
532 // XXX AK4450 (xDuoo X3ii) needs to be muted when switching rates.
533 audiohw_mute(true); 615 audiohw_mute(true);
534#endif 616#endif
535 snd_pcm_drop(handle); 617 snd_pcm_drop(handle);
@@ -555,6 +637,8 @@ void pcm_dma_apply_settings(void)
555void pcm_play_dma_pause(bool pause) 637void pcm_play_dma_pause(bool pause)
556{ 638{
557 logf("PCM DMA pause %d", pause); 639 logf("PCM DMA pause %d", pause);
640 if (!handle) return;
641
558#ifdef AUDIOHW_MUTE_ON_PAUSE 642#ifdef AUDIOHW_MUTE_ON_PAUSE
559 if (pause) audiohw_mute(true); 643 if (pause) audiohw_mute(true);
560#endif 644#endif
@@ -566,14 +650,15 @@ void pcm_play_dma_pause(bool pause)
566 650
567void pcm_play_dma_stop(void) 651void pcm_play_dma_stop(void)
568{ 652{
569 snd_pcm_nonblock(handle, 0); 653 logf("PCM DMA stop (%d)", snd_pcm_state(handle));
570 snd_pcm_drain(handle); 654
571 snd_pcm_nonblock(handle, 1); 655 int err = snd_pcm_drain(handle);
572// last_sample_rate = 0; 656 if (err < 0)
657 if (err < 0)
658 logf("Drain failed: %s", snd_strerror(err));
573#ifdef AUDIOHW_MUTE_ON_PAUSE 659#ifdef AUDIOHW_MUTE_ON_PAUSE
574 audiohw_mute(true); 660 audiohw_mute(true);
575#endif 661#endif
576 logf("PCM DMA stopped");
577} 662}
578 663
579void pcm_play_dma_start(const void *addr, size_t size) 664void pcm_play_dma_start(const void *addr, size_t size)
@@ -591,40 +676,67 @@ void pcm_play_dma_start(const void *addr, size_t size)
591 while (1) 676 while (1)
592 { 677 {
593 snd_pcm_state_t state = snd_pcm_state(handle); 678 snd_pcm_state_t state = snd_pcm_state(handle);
594 logf("PCM State %d", state); 679 logf("PCM State %d", state);
595 680
596 switch (state) 681 switch (state)
597 { 682 {
598 case SND_PCM_STATE_RUNNING: 683 case SND_PCM_STATE_RUNNING:
684#if defined(AUDIOHW_MUTE_ON_PAUSE)
685 audiohw_mute(false);
686#endif
599 return; 687 return;
600 case SND_PCM_STATE_XRUN: 688 case SND_PCM_STATE_XRUN:
601 { 689 {
602 logf("Trying to recover from error\n"); 690 logf("Trying to recover from error");
603 int err = snd_pcm_recover(handle, -EPIPE, 0); 691 int err = snd_pcm_recover(handle, -EPIPE, 0);
604 if (err < 0) 692 if (err < 0)
605 logf("Recovery failed: %s\n", snd_strerror(err)); 693 logf("Recovery failed: %s", snd_strerror(err));
606 continue; 694 continue;
607 } 695 }
608 case SND_PCM_STATE_SETUP: 696 case SND_PCM_STATE_SETUP:
609 { 697 {
610 int err = snd_pcm_prepare(handle); 698 int err = snd_pcm_prepare(handle);
611 if (err < 0) 699 if (err < 0)
612 logf("Prepare error: %s\n", snd_strerror(err)); 700 logf("Prepare error: %s", snd_strerror(err));
613 /* fall through */
614 } 701 }
702 /* fall through */
615 case SND_PCM_STATE_PREPARED: 703 case SND_PCM_STATE_PREPARED:
616 { /* prepared state, we need to fill the buffer with silence before 704 {
617 * starting */ 705 int err;
618 int err = async_rw(handle); 706#if 0
619 if (err < 0) { 707 /* fill buffer with silence to initiate playback without noisy click */
620 logf("Start error: %s\n", snd_strerror(err)); 708 snd_pcm_sframes_t sample_size = buffer_size;
709 sample_t *samples = calloc(1, sample_size * channels * sizeof(sample_t));
710
711 snd_pcm_format_set_silence(format, samples, sample_size);
712 err = snd_pcm_writei(handle, samples, sample_size);
713 free(samples);
714
715 if (err != (ssize_t)sample_size)
716 {
717 logf("Initial write error: written %i expected %li", err, sample_size);
621 return; 718 return;
622 } 719 }
623#if defined(AUDIOHW_MUTE_ON_PAUSE) 720#else
624 audiohw_mute(false); 721 /* Fill buffer with proper sample data */
722 while (snd_pcm_avail_update(handle) >= period_size)
723 {
724 if (copy_frames(true))
725 {
726 err = snd_pcm_writei(handle, frames, period_size);
727 if (err < 0 && err != period_size && err != -EAGAIN)
728 {
729 logf("Write error: written %i expected %li", err, period_size);
730 break;
731 }
732 }
733 }
625#endif 734#endif
626 if (err == 0) 735 err = snd_pcm_start(handle);
627 return; 736 if (err < 0) {
737 logf("start error: %s", snd_strerror(err));
738 }
739
628 break; 740 break;
629 } 741 }
630 case SND_PCM_STATE_PAUSED: 742 case SND_PCM_STATE_PAUSED:
@@ -636,7 +748,7 @@ void pcm_play_dma_start(const void *addr, size_t size)
636 /* run until drained */ 748 /* run until drained */
637 continue; 749 continue;
638 default: 750 default:
639 logf("Unhandled state: %s\n", snd_pcm_state_name(state)); 751 logf("Unhandled state: %s", snd_pcm_state_name(state));
640 return; 752 return;
641 } 753 }
642 } 754 }
@@ -676,40 +788,100 @@ int pcm_alsa_get_rate(void)
676#ifdef HAVE_RECORDING 788#ifdef HAVE_RECORDING
677void pcm_rec_lock(void) 789void pcm_rec_lock(void)
678{ 790{
791 pcm_play_lock();
679} 792}
680 793
681void pcm_rec_unlock(void) 794void pcm_rec_unlock(void)
682{ 795{
796 pcm_play_unlock();
683} 797}
684 798
685void pcm_rec_dma_init(void) 799void pcm_rec_dma_init(void)
686{ 800{
801 logf("PCM REC DMA Init");
802
803 open_hwdev(capture_dev, SND_PCM_STREAM_CAPTURE);
687} 804}
688 805
689void pcm_rec_dma_close(void) 806void pcm_rec_dma_close(void)
690{ 807{
808 logf("Rec DMA Close");
809 close_hwdev();
691} 810}
692 811
693void pcm_rec_dma_start(void *start, size_t size) 812void pcm_rec_dma_start(void *start, size_t size)
694{ 813{
695 (void)start; 814 logf("PCM REC DMA start (%p %d)", start, size);
696 (void)size; 815 pcm_dma_apply_settings_nolock();
816 pcm_data_rec = start;
817 pcm_size = size;
818
819 if (!handle) return;
820
821 while (1)
822 {
823 snd_pcm_state_t state = snd_pcm_state(handle);
824
825 switch (state)
826 {
827 case SND_PCM_STATE_RUNNING:
828 return;
829 case SND_PCM_STATE_XRUN:
830 {
831 logf("Trying to recover from error");
832 int err = snd_pcm_recover(handle, -EPIPE, 0);
833 if (err < 0)
834 panicf("Recovery failed: %s", snd_strerror(err));
835 continue;
836 }
837 case SND_PCM_STATE_SETUP:
838 {
839 int err = snd_pcm_prepare(handle);
840 if (err < 0)
841 panicf("Prepare error: %s", snd_strerror(err));
842 }
843 /* fall through */
844 case SND_PCM_STATE_PREPARED:
845 {
846 int err = snd_pcm_start(handle);
847 if (err < 0)
848 panicf("Start error: %s", snd_strerror(err));
849 return;
850 }
851 case SND_PCM_STATE_PAUSED:
852 { /* paused, simply resume */
853 pcm_play_dma_pause(0);
854 return;
855 }
856 case SND_PCM_STATE_DRAINING:
857 /* run until drained */
858 continue;
859 default:
860 logf("Unhandled state: %s", snd_pcm_state_name(state));
861 return;
862 }
863 }
697} 864}
698 865
699void pcm_rec_dma_stop(void) 866void pcm_rec_dma_stop(void)
700{ 867{
868 logf("Rec DMA Stop");
869 close_hwdev();
701} 870}
702 871
703const void * pcm_rec_dma_get_peak_buffer(void) 872const void * pcm_rec_dma_get_peak_buffer(void)
704{ 873{
705 return NULL; 874 uintptr_t addr = (uintptr_t)pcm_data_rec;
875 return (void*)((addr + 3) & ~3);
706} 876}
707 877
878#ifdef SIMULATOR
708void audiohw_set_recvol(int left, int right, int type) 879void audiohw_set_recvol(int left, int right, int type)
709{ 880{
710 (void)left; 881 (void)left;
711 (void)right; 882 (void)right;
712 (void)type; 883 (void)type;
713} 884}
885#endif
714 886
715#endif /* HAVE_RECORDING */ 887#endif /* HAVE_RECORDING */
diff --git a/firmware/target/hosted/pcm-alsa.h b/firmware/target/hosted/pcm-alsa.h
index b746f0d259..f10574a6da 100644
--- a/firmware/target/hosted/pcm-alsa.h
+++ b/firmware/target/hosted/pcm-alsa.h
@@ -28,6 +28,12 @@
28void pcm_alsa_set_digital_volume(int vol_db_l, int vol_db_r); 28void pcm_alsa_set_digital_volume(int vol_db_l, int vol_db_r);
29#endif 29#endif
30 30
31/* These two should be invoked in your audiohw_preinit() call! */
32void pcm_alsa_set_playback_device(const char *device);
33#if defined(HAVE_RECORDING)
34void pcm_alsa_set_capture_device(const char *device);
35#endif
36
31int pcm_alsa_get_rate(void); 37int pcm_alsa_get_rate(void);
32 38
33#endif /* __PCM_ALSA_RB_H__ */ 39#endif /* __PCM_ALSA_RB_H__ */