1 /*
2 * Copyright (C) 2013-2015 Intel Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <pthread.h>
21 #include <errno.h>
22
23 #include <alsa/asoundlib.h>
24
25 #include "aconfig.h"
26 #include "gettext.h"
27
28 #include "common.h"
29 #include "alsa.h"
30 #include "latencytest.h"
31
32 struct pcm_container {
33 snd_pcm_t *handle;
34 snd_pcm_uframes_t period_size;
35 snd_pcm_uframes_t buffer_size;
36 snd_pcm_format_t format;
37 unsigned short channels;
38 size_t period_bytes;
39 size_t sample_bits;
40 size_t frame_bits;
41 char *buffer;
42 };
43
44 struct format_map_table {
45 enum _bat_pcm_format format_bat;
46 snd_pcm_format_t format_alsa;
47 };
48
49 static struct format_map_table map_tables[] = {
50 { BAT_PCM_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN },
51 { BAT_PCM_FORMAT_U8, SND_PCM_FORMAT_U8 },
52 { BAT_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE },
53 { BAT_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3LE },
54 { BAT_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE },
55 { BAT_PCM_FORMAT_MAX, },
56 };
57
format_convert(struct bat * bat,snd_pcm_format_t * fmt)58 static int format_convert(struct bat *bat, snd_pcm_format_t *fmt)
59 {
60 struct format_map_table *t = map_tables;
61
62 for (; t->format_bat != BAT_PCM_FORMAT_MAX; t++) {
63 if (t->format_bat == bat->format) {
64 *fmt = t->format_alsa;
65 return 0;
66 }
67 }
68 fprintf(bat->err, _("Invalid format!\n"));
69 return -EINVAL;
70 }
71
set_snd_pcm_params(struct bat * bat,struct pcm_container * sndpcm)72 static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
73 {
74 snd_pcm_hw_params_t *params;
75 snd_pcm_format_t format;
76 unsigned int buffer_time = 0;
77 unsigned int period_time = 0;
78 snd_pcm_uframes_t buffer_size = 0;
79 snd_pcm_uframes_t period_size = 0;
80 unsigned int rate;
81 int err;
82 const char *device_name = snd_pcm_name(sndpcm->handle);
83
84 /* Convert common format to ALSA format */
85 err = format_convert(bat, &format);
86 if (err != 0)
87 return err;
88
89 /* Allocate a hardware parameters object. */
90 snd_pcm_hw_params_alloca(¶ms);
91
92 /* Fill it in with default values. */
93 err = snd_pcm_hw_params_any(sndpcm->handle, params);
94 if (err < 0) {
95 fprintf(bat->err, _("Set parameter to device error: "));
96 fprintf(bat->err, _("default params: %s: %s(%d)\n"),
97 device_name, snd_strerror(err), err);
98 return err;
99 }
100
101 /* Set access mode */
102 err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
103 SND_PCM_ACCESS_RW_INTERLEAVED);
104 if (err < 0) {
105 fprintf(bat->err, _("Set parameter to device error: "));
106 fprintf(bat->err, _("access type: %s: %s(%d)\n"),
107 device_name, snd_strerror(err), err);
108 return err;
109 }
110
111 /* Set format */
112 err = snd_pcm_hw_params_set_format(sndpcm->handle, params, format);
113 if (err < 0) {
114 fprintf(bat->err, _("Set parameter to device error: "));
115 fprintf(bat->err, _("PCM format: %d %s: %s(%d)\n"), format,
116 device_name, snd_strerror(err), err);
117 return err;
118 }
119
120 /* Set channels */
121 err = snd_pcm_hw_params_set_channels(sndpcm->handle,
122 params, bat->channels);
123 if (err < 0) {
124 fprintf(bat->err, _("Set parameter to device error: "));
125 fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
126 bat->channels,
127 device_name, snd_strerror(err), err);
128 return err;
129 }
130
131 /* Set sampling rate */
132 rate = bat->rate;
133 err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
134 params, &bat->rate,
135 0);
136 if (err < 0) {
137 fprintf(bat->err, _("Set parameter to device error: "));
138 fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
139 bat->rate,
140 device_name, snd_strerror(err), err);
141 return err;
142 }
143 if ((float) rate * (1 + RATE_RANGE) < bat->rate
144 || (float) rate * (1 - RATE_RANGE) > bat->rate) {
145 fprintf(bat->err, _("Invalid parameters: sample rate: "));
146 fprintf(bat->err, _("requested %dHz, got %dHz\n"),
147 rate, bat->rate);
148 return -EINVAL;
149 }
150
151 if (bat->buffer_size > 0 && bat->period_size == 0)
152 bat->period_size = bat->buffer_size / DIV_BUFFERSIZE;
153
154 if (bat->roundtriplatency && bat->buffer_size == 0) {
155 /* Set to minimum buffer size and period size
156 for latency test */
157 if (snd_pcm_hw_params_get_buffer_size_min(params,
158 &buffer_size) < 0) {
159 fprintf(bat->err,
160 _("Get parameter from device error: "));
161 fprintf(bat->err, _("buffer size min: %d %s: %s(%d)\n"),
162 (int) buffer_size,
163 device_name, snd_strerror(err), err);
164 return -EINVAL;
165 }
166
167 if (snd_pcm_hw_params_get_period_size_min(params,
168 &period_size, 0) < 0) {
169 fprintf(bat->err,
170 _("Get parameter from device error: "));
171 fprintf(bat->err, _("period size min: %d %s: %s(%d)\n"),
172 (int) period_size,
173 device_name, snd_strerror(err), err);
174 return -EINVAL;
175 }
176 bat->buffer_size = (int) buffer_size;
177 bat->period_size = (int) period_size;
178 }
179
180 if (bat->buffer_size > 0) {
181 buffer_size = bat->buffer_size;
182 period_size = bat->period_size;
183
184 fprintf(bat->log, _("Set period size: %d buffer size: %d\n"),
185 (int) period_size, (int) buffer_size);
186
187 err = snd_pcm_hw_params_set_buffer_size_near(sndpcm->handle,
188 params, &buffer_size);
189 if (err < 0) {
190 fprintf(bat->err, _("Set parameter to device error: "));
191 fprintf(bat->err, _("buffer size: %d %s: %s(%d)\n"),
192 (int) buffer_size,
193 device_name, snd_strerror(err), err);
194 return err;
195 }
196
197 err = snd_pcm_hw_params_set_period_size_near(sndpcm->handle,
198 params, &period_size, 0);
199 if (err < 0) {
200 fprintf(bat->err, _("Set parameter to device error: "));
201 fprintf(bat->err, _("period size: %d %s: %s(%d)\n"),
202 (int) period_size,
203 device_name, snd_strerror(err), err);
204 return err;
205 }
206 } else {
207 if (snd_pcm_hw_params_get_buffer_time_max(params,
208 &buffer_time, 0) < 0) {
209 fprintf(bat->err,
210 _("Get parameter from device error: "));
211 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
212 buffer_time,
213 device_name, snd_strerror(err), err);
214 return -EINVAL;
215 }
216
217 if (buffer_time > MAX_BUFFERTIME)
218 buffer_time = MAX_BUFFERTIME;
219
220 period_time = buffer_time / DIV_BUFFERTIME;
221
222 /* Set buffer time and period time */
223 err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,
224 params, &buffer_time, 0);
225 if (err < 0) {
226 fprintf(bat->err, _("Set parameter to device error: "));
227 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
228 buffer_time,
229 device_name, snd_strerror(err), err);
230 return err;
231 }
232
233 err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
234 params, &period_time, 0);
235 if (err < 0) {
236 fprintf(bat->err, _("Set parameter to device error: "));
237 fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
238 period_time,
239 device_name, snd_strerror(err), err);
240 return err;
241 }
242 }
243
244 /* Write the parameters to the driver */
245 if (snd_pcm_hw_params(sndpcm->handle, params) < 0) {
246 fprintf(bat->err, _("Set parameter to device error: "));
247 fprintf(bat->err, _("hw params: %s: %s(%d)\n"),
248 device_name, snd_strerror(err), err);
249 return -EINVAL;
250 }
251
252 err = snd_pcm_hw_params_get_period_size(params,
253 &sndpcm->period_size, 0);
254 if (err < 0) {
255 fprintf(bat->err, _("Get parameter from device error: "));
256 fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
257 sndpcm->period_size,
258 device_name, snd_strerror(err), err);
259 return err;
260 }
261
262 err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
263 if (err < 0) {
264 fprintf(bat->err, _("Get parameter from device error: "));
265 fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
266 sndpcm->buffer_size,
267 device_name, snd_strerror(err), err);
268 return err;
269 }
270
271 if (sndpcm->period_size == sndpcm->buffer_size) {
272 fprintf(bat->err, _("Invalid parameters: can't use period "));
273 fprintf(bat->err, _("equal to buffer size (%zd)\n"),
274 sndpcm->period_size);
275 return -EINVAL;
276 }
277
278 fprintf(bat->log, _("Get period size: %d buffer size: %d\n"),
279 (int) sndpcm->period_size, (int) sndpcm->buffer_size);
280
281 err = snd_pcm_format_physical_width(format);
282 if (err < 0) {
283 fprintf(bat->err, _("Invalid parameters: "));
284 fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
285 err);
286 return err;
287 }
288 sndpcm->sample_bits = err;
289
290 sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
291
292 /* Calculate the period bytes */
293 sndpcm->period_bytes = sndpcm->period_size * sndpcm->frame_bits / 8;
294 sndpcm->buffer = (char *) malloc(sndpcm->period_bytes);
295 if (sndpcm->buffer == NULL) {
296 fprintf(bat->err, _("Not enough memory: size=%zd\n"),
297 sndpcm->period_bytes);
298 return -ENOMEM;
299 }
300
301 return 0;
302 }
303
write_to_pcm(const struct pcm_container * sndpcm,int frames,struct bat * bat)304 static int write_to_pcm(const struct pcm_container *sndpcm,
305 int frames, struct bat *bat)
306 {
307 int err;
308 int offset = 0;
309 int remain = frames;
310
311 while (remain > 0) {
312 err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
313 remain);
314 if (err == -EAGAIN || (err >= 0 && err < frames)) {
315 snd_pcm_wait(sndpcm->handle, 500);
316 } else if (err == -EPIPE) {
317 fprintf(bat->err, _("Underrun: %s(%d)\n"),
318 snd_strerror(err), err);
319 if (bat->roundtriplatency)
320 bat->latency.xrun_error = true;
321 snd_pcm_prepare(sndpcm->handle);
322 } else if (err == -ESTRPIPE) {
323 while ((err = snd_pcm_resume(sndpcm->handle)) == -EAGAIN)
324 sleep(1); /* wait until resume flag is released */
325 if (err < 0)
326 snd_pcm_prepare(sndpcm->handle);
327 } else if (err < 0) {
328 fprintf(bat->err, _("Write PCM device error: %s(%d)\n"),
329 snd_strerror(err), err);
330 return err;
331 }
332
333 if (err > 0) {
334 remain -= err;
335 offset += err * sndpcm->frame_bits / 8;
336 }
337 }
338
339 return 0;
340 }
341
342 /**
343 * Process output data for latency test
344 */
latencytest_process_output(struct pcm_container * sndpcm,struct bat * bat)345 static int latencytest_process_output(struct pcm_container *sndpcm,
346 struct bat *bat)
347 {
348 int err = 0;
349 int bytes = sndpcm->period_bytes; /* playback buffer size */
350 int frames = sndpcm->period_size; /* frame count */
351
352 bat->latency.is_playing = true;
353
354 while (1) {
355 /* generate output data */
356 err = handleoutput(bat, sndpcm->buffer, bytes, frames);
357 if (err != 0)
358 break;
359
360 err = write_to_pcm(sndpcm, frames, bat);
361 if (err != 0)
362 break;
363
364 /* Xrun error, terminate the playback thread*/
365 if (bat->latency.xrun_error == true)
366 break;
367
368 if (bat->latency.state == LATENCY_STATE_COMPLETE_SUCCESS)
369 break;
370
371 bat->periods_played++;
372 }
373
374 bat->latency.is_playing = false;
375
376 return err;
377 }
378
write_to_pcm_loop(struct pcm_container * sndpcm,struct bat * bat)379 static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
380 {
381 int err = 0;
382 int bytes = sndpcm->period_bytes; /* playback buffer size */
383 int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
384 FILE *fp = NULL;
385 int bytes_total = 0;
386
387 if (bat->debugplay) {
388 fp = fopen(bat->debugplay, "wb");
389 err = -errno;
390 if (fp == NULL) {
391 fprintf(bat->err, _("Cannot open file: %s %d\n"),
392 bat->debugplay, err);
393 return err;
394 }
395 /* leave space for wav header */
396 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
397 err = -errno;
398 fclose(fp);
399 return err;
400 }
401 }
402
403 while (1) {
404 err = generate_input_data(bat, sndpcm->buffer, bytes, frames);
405 if (err != 0)
406 break;
407
408 if (bat->debugplay) {
409 if (fwrite(sndpcm->buffer, 1, bytes, fp) != bytes) {
410 err = -EIO;
411 break;
412 }
413 bytes_total += bytes;
414 }
415
416 bat->periods_played++;
417 if (bat->period_is_limited
418 && bat->periods_played >= bat->periods_total)
419 break;
420
421 err = write_to_pcm(sndpcm, frames, bat);
422 if (err != 0)
423 break;
424 }
425
426 if (bat->debugplay) {
427 update_wav_header(bat, fp, bytes_total);
428 fclose(fp);
429 }
430
431 snd_pcm_drain(sndpcm->handle);
432
433 return err;
434 }
435
436 /**
437 * Play
438 */
playback_alsa(struct bat * bat)439 void *playback_alsa(struct bat *bat)
440 {
441 int err = 0;
442 struct pcm_container sndpcm;
443
444 fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
445
446 retval_play = 0;
447 memset(&sndpcm, 0, sizeof(sndpcm));
448
449 err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
450 SND_PCM_STREAM_PLAYBACK, 0);
451 if (err != 0) {
452 fprintf(bat->err, _("Cannot open PCM playback device: "));
453 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
454 retval_play = err;
455 goto exit1;
456 }
457
458 err = set_snd_pcm_params(bat, &sndpcm);
459 if (err != 0) {
460 retval_play = err;
461 goto exit2;
462 }
463
464 if (bat->playback.file == NULL) {
465 fprintf(bat->log, _("Playing generated audio sine wave"));
466 bat->sinus_duration == 0 ?
467 fprintf(bat->log, _(" endlessly\n")) :
468 fprintf(bat->log, _("\n"));
469 } else {
470 fprintf(bat->log, _("Playing input audio file: %s\n"),
471 bat->playback.file);
472 bat->fp = fopen(bat->playback.file, "rb");
473 err = -errno;
474 if (bat->fp == NULL) {
475 fprintf(bat->err, _("Cannot open file: %s %d\n"),
476 bat->playback.file, err);
477 retval_play = err;
478 goto exit3;
479 }
480 /* Skip header */
481 err = read_wav_header(bat, bat->playback.file, bat->fp, true);
482 if (err != 0) {
483 retval_play = err;
484 goto exit4;
485 }
486 }
487
488 if (bat->roundtriplatency)
489 err = latencytest_process_output(&sndpcm, bat);
490 else
491 err = write_to_pcm_loop(&sndpcm, bat);
492 if (err < 0) {
493 retval_play = err;
494 goto exit4;
495 }
496
497 exit4:
498 if (bat->playback.file)
499 fclose(bat->fp);
500 exit3:
501 free(sndpcm.buffer);
502 exit2:
503 snd_pcm_close(sndpcm.handle);
504 exit1:
505 pthread_exit(&retval_play);
506 }
507
read_from_pcm(struct pcm_container * sndpcm,int frames,struct bat * bat)508 static int read_from_pcm(struct pcm_container *sndpcm,
509 int frames, struct bat *bat)
510 {
511 int err = 0;
512 int offset = 0;
513 int remain = frames;
514
515 while (remain > 0) {
516 err = snd_pcm_readi(sndpcm->handle,
517 sndpcm->buffer + offset, remain);
518 if (err == -EAGAIN || (err >= 0 && err < remain)) {
519 snd_pcm_wait(sndpcm->handle, 500);
520 } else if (err == -EPIPE) {
521 snd_pcm_prepare(sndpcm->handle);
522 fprintf(bat->err, _("Overrun: %s(%d)\n"),
523 snd_strerror(err), err);
524 if (bat->roundtriplatency)
525 bat->latency.xrun_error = true;
526 } else if (err == -ESTRPIPE) {
527 while ((err = snd_pcm_resume(sndpcm->handle)) == -EAGAIN)
528 sleep(1); /* wait until resume flag is released */
529 if (err < 0)
530 snd_pcm_prepare(sndpcm->handle);
531 } else if (err < 0) {
532 fprintf(bat->err, _("Read PCM device error: %s(%d)\n"),
533 snd_strerror(err), err);
534 return err;
535 }
536
537 if (err > 0) {
538 remain -= err;
539 offset += err * sndpcm->frame_bits / 8;
540 }
541 }
542
543 return 0;
544 }
545
read_from_pcm_loop(struct pcm_container * sndpcm,struct bat * bat)546 static int read_from_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
547 {
548 int err = 0;
549 FILE *fp = NULL;
550 int size, frames;
551 int bytes_read = 0;
552 int bytes_count = bat->frames * bat->frame_size;
553 int remain = bytes_count;
554
555 remove(bat->capture.file);
556 fp = fopen(bat->capture.file, "wb");
557 err = -errno;
558 if (fp == NULL) {
559 fprintf(bat->err, _("Cannot open file: %s %d\n"),
560 bat->capture.file, err);
561 return err;
562 }
563 /* leave space for file header */
564 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
565 err = -errno;
566 fclose(fp);
567 return err;
568 }
569
570 while (remain > 0) {
571 size = (remain <= sndpcm->period_bytes) ?
572 remain : sndpcm->period_bytes;
573 frames = size * 8 / sndpcm->frame_bits;
574
575 /* read a chunk from pcm device */
576 err = read_from_pcm(sndpcm, frames, bat);
577 if (err != 0)
578 break;
579
580 /* write the chunk to file */
581 if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
582 err = -EIO;
583 break;
584 }
585
586 bytes_read += size;
587 remain -= size;
588 bat->periods_played++;
589
590 if (bat->period_is_limited
591 && bat->periods_played >= bat->periods_total)
592 break;
593 }
594
595 update_wav_header(bat, fp, bytes_read);
596
597 fclose(fp);
598 return err;
599 }
600
601 /**
602 * Process input data for latency test
603 */
latencytest_process_input(struct pcm_container * sndpcm,struct bat * bat)604 static int latencytest_process_input(struct pcm_container *sndpcm,
605 struct bat *bat)
606 {
607 int err = 0;
608 FILE *fp = NULL;
609 int bytes_read = 0;
610 int frames = sndpcm->period_size;
611 int size = sndpcm->period_bytes;
612 int bytes_count = bat->frames * bat->frame_size;
613
614 remove(bat->capture.file);
615 fp = fopen(bat->capture.file, "wb");
616 err = -errno;
617 if (fp == NULL) {
618 fprintf(bat->err, _("Cannot open file: %s %d\n"),
619 bat->capture.file, err);
620 return err;
621 }
622 /* leave space for file header */
623 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
624 fclose(fp);
625 return err;
626 }
627
628 bat->latency.is_capturing = true;
629
630 while (bytes_read < bytes_count) {
631 /* read a chunk from pcm device */
632 err = read_from_pcm(sndpcm, frames, bat);
633 if (err != 0)
634 break;
635
636 /* Xrun error, terminate the capture thread*/
637 if (bat->latency.xrun_error == true)
638 break;
639
640 err = handleinput(bat, sndpcm->buffer, frames);
641 if (err != 0)
642 break;
643
644 if (bat->latency.is_playing == false)
645 break;
646
647 /* write the chunk to file */
648 if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
649 err = -EIO;
650 break;
651 }
652
653 bytes_read += size;
654 }
655
656 bat->latency.is_capturing = false;
657
658 update_wav_header(bat, fp, bytes_read);
659
660 fclose(fp);
661 return err;
662 }
663
664
pcm_cleanup(void * p)665 static void pcm_cleanup(void *p)
666 {
667 snd_pcm_close(p);
668 }
669
670 /**
671 * Record
672 */
record_alsa(struct bat * bat)673 void *record_alsa(struct bat *bat)
674 {
675 int err = 0;
676 struct pcm_container sndpcm;
677
678 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
679
680 fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
681
682 retval_record = 0;
683 memset(&sndpcm, 0, sizeof(sndpcm));
684
685 err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
686 SND_PCM_STREAM_CAPTURE, 0);
687 if (err != 0) {
688 fprintf(bat->err, _("Cannot open PCM capture device: "));
689 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
690 retval_record = err;
691 goto exit1;
692 }
693
694 err = set_snd_pcm_params(bat, &sndpcm);
695 if (err != 0) {
696 retval_record = err;
697 goto exit2;
698 }
699
700 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
701 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
702 pthread_cleanup_push(pcm_cleanup, sndpcm.handle);
703 pthread_cleanup_push(free, sndpcm.buffer);
704
705 fprintf(bat->log, _("Recording ...\n"));
706 if (bat->roundtriplatency)
707 err = latencytest_process_input(&sndpcm, bat);
708 else
709 err = read_from_pcm_loop(&sndpcm, bat);
710
711 pthread_cleanup_pop(0);
712 pthread_cleanup_pop(0);
713
714 if (err != 0) {
715 retval_record = err;
716 goto exit3;
717 }
718
719 /* Normally we will never reach this part of code (unless error in
720 * previous call) (before exit3) as this thread will be cancelled
721 * by end of play thread. Except in single line mode. */
722 snd_pcm_drain(sndpcm.handle);
723 pthread_exit(&retval_record);
724
725 exit3:
726 free(sndpcm.buffer);
727 exit2:
728 snd_pcm_close(sndpcm.handle);
729 exit1:
730 pthread_exit(&retval_record);
731 }
732