OSDN Git Service

bat: Don't pass incompatible function pointers to pthread_cleanup_push()
[android-x86/external-alsa-utils.git] / bat / alsa.c
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 <math.h>
20 #include <stdint.h>
21 #include <pthread.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 "bat-signal.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 static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
45 {
46         snd_pcm_hw_params_t *params;
47         unsigned int buffer_time = 0;
48         unsigned int period_time = 0;
49         unsigned int rate;
50         int err;
51         const char *device_name = snd_pcm_name(sndpcm->handle);
52
53         /* Allocate a hardware parameters object. */
54         snd_pcm_hw_params_alloca(&params);
55
56         /* Fill it in with default values. */
57         err = snd_pcm_hw_params_any(sndpcm->handle, params);
58         if (err < 0) {
59                 fprintf(bat->err, _("Set parameter to device error: "));
60                 fprintf(bat->err, _("default params: %s: %s(%d)\n"),
61                                 device_name, snd_strerror(err), err);
62                 return err;
63         }
64
65         /* Set access mode */
66         err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
67                         SND_PCM_ACCESS_RW_INTERLEAVED);
68         if (err < 0) {
69                 fprintf(bat->err, _("Set parameter to device error: "));
70                 fprintf(bat->err, _("access type: %s: %s(%d)\n"),
71                                 device_name, snd_strerror(err), err);
72                 return err;
73         }
74
75         /* Set format */
76         err = snd_pcm_hw_params_set_format(sndpcm->handle, params, bat->format);
77         if (err < 0) {
78                 fprintf(bat->err, _("Set parameter to device error: "));
79                 fprintf(bat->err, _("PCM format: %d %s: %s(%d)\n"),
80                                 bat->format,
81                                 device_name, snd_strerror(err), err);
82                 return err;
83         }
84
85         /* Set channels */
86         err = snd_pcm_hw_params_set_channels(sndpcm->handle,
87                         params, bat->channels);
88         if (err < 0) {
89                 fprintf(bat->err, _("Set parameter to device error: "));
90                 fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
91                                 bat->channels,
92                                 device_name, snd_strerror(err), err);
93                 return err;
94         }
95
96         /* Set sampling rate */
97         rate = bat->rate;
98         err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
99                         params, &bat->rate,
100                         0);
101         if (err < 0) {
102                 fprintf(bat->err, _("Set parameter to device error: "));
103                 fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
104                                 bat->rate,
105                                 device_name, snd_strerror(err), err);
106                 return err;
107         }
108         if ((float) rate * (1 + RATE_RANGE) < bat->rate
109                         || (float) rate * (1 - RATE_RANGE) > bat->rate) {
110                 fprintf(bat->err, _("Invalid parameters: sample rate: "));
111                 fprintf(bat->err, _("requested %dHz, got %dHz\n"),
112                                 rate, bat->rate);
113                 return -EINVAL;
114         }
115
116         if (snd_pcm_hw_params_get_buffer_time_max(params,
117                         &buffer_time, 0) < 0) {
118                 fprintf(bat->err, _("Get parameter from device error: "));
119                 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
120                                 buffer_time,
121                                 device_name, snd_strerror(err), err);
122                 return -EINVAL;
123         }
124
125         if (buffer_time > MAX_BUFFERTIME)
126                 buffer_time = MAX_BUFFERTIME;
127
128         period_time = buffer_time / DIV_BUFFERTIME;
129
130         /* Set buffer time and period time */
131         err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle, params,
132                         &buffer_time, 0);
133         if (err < 0) {
134                 fprintf(bat->err, _("Set parameter to device error: "));
135                 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
136                                 buffer_time,
137                                 device_name, snd_strerror(err), err);
138                 return err;
139         }
140
141         err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle, params,
142                         &period_time, 0);
143         if (err < 0) {
144                 fprintf(bat->err, _("Set parameter to device error: "));
145                 fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
146                                 period_time,
147                                 device_name, snd_strerror(err), err);
148                 return err;
149         }
150
151         /* Write the parameters to the driver */
152         if (snd_pcm_hw_params(sndpcm->handle, params) < 0) {
153                 fprintf(bat->err, _("Set parameter to device error: "));
154                 fprintf(bat->err, _("hw params: %s: %s(%d)\n"),
155                                 device_name, snd_strerror(err), err);
156                 return -EINVAL;
157         }
158
159         err = snd_pcm_hw_params_get_period_size(params,
160                         &sndpcm->period_size, 0);
161         if (err < 0) {
162                 fprintf(bat->err, _("Get parameter from device error: "));
163                 fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
164                                 sndpcm->period_size,
165                                 device_name, snd_strerror(err), err);
166                 return err;
167         }
168
169         err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
170         if (err < 0) {
171                 fprintf(bat->err, _("Get parameter from device error: "));
172                 fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
173                                 sndpcm->buffer_size,
174                                 device_name, snd_strerror(err), err);
175                 return err;
176         }
177
178         if (sndpcm->period_size == sndpcm->buffer_size) {
179                 fprintf(bat->err, _("Invalid parameters: can't use period "));
180                 fprintf(bat->err, _("equal to buffer size (%zd)\n"),
181                                 sndpcm->period_size);
182                 return -EINVAL;
183         }
184
185         err = snd_pcm_format_physical_width(bat->format);
186         if (err < 0) {
187                 fprintf(bat->err, _("Invalid parameters: "));
188                 fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
189                                 err);
190                 return err;
191         }
192         sndpcm->sample_bits = err;
193
194         sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
195
196         /* Calculate the period bytes */
197         sndpcm->period_bytes = sndpcm->period_size * sndpcm->frame_bits / 8;
198         sndpcm->buffer = (char *) malloc(sndpcm->period_bytes);
199         if (sndpcm->buffer == NULL) {
200                 fprintf(bat->err, _("Not enough memory: size=%zd\n"),
201                                 sndpcm->period_bytes);
202                 return -ENOMEM;
203         }
204
205         return 0;
206 }
207
208 /*
209  * Generate buffer to be played either from input file or from generated data
210  * Return value
211  * <0 error
212  * 0 ok
213  * >0 break
214  */
215 static int generate_input_data(struct pcm_container *sndpcm, int bytes,
216                 struct bat *bat)
217 {
218         int err;
219         static int load;
220         int frames = bytes * 8 / sndpcm->frame_bits;
221
222         if (bat->playback.file != NULL) {
223                 /* From input file */
224                 load = 0;
225
226                 while (1) {
227                         err = fread(sndpcm->buffer + load, 1,
228                                         bytes - load, bat->fp);
229                         if (0 == err) {
230                                 if (feof(bat->fp)) {
231                                         fprintf(bat->log,
232                                                         _("End of playing.\n"));
233                                         return 1;
234                                 }
235                         } else if (err < bytes - load) {
236                                 if (ferror(bat->fp)) {
237                                         fprintf(bat->err, _("Read file error"));
238                                         fprintf(bat->err, _(": %d\n"), err);
239                                         return -EIO;
240                                 }
241                                 load += err;
242                         } else {
243                                 break;
244                         }
245                 }
246         } else {
247                 /* Generate sine wave */
248                 if ((bat->sinus_duration) && (load > bat->sinus_duration))
249                         return 1;
250
251                 err = generate_sine_wave(bat, frames, (void *)sndpcm->buffer);
252                 if (err != 0)
253                         return err;
254
255                 load += frames;
256         }
257
258         return 0;
259 }
260
261 static int write_to_pcm(const struct pcm_container *sndpcm,
262                 int frames, struct bat *bat)
263 {
264         int err;
265         int offset = 0;
266         int remain = frames;
267
268         while (remain > 0) {
269                 err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
270                                 remain);
271                 if (err == -EAGAIN || (err >= 0 && err < frames)) {
272                         snd_pcm_wait(sndpcm->handle, 500);
273                 } else if (err == -EPIPE) {
274                         fprintf(bat->err, _("Underrun: %s(%d)\n"),
275                                         snd_strerror(err), err);
276                         snd_pcm_prepare(sndpcm->handle);
277                 } else if (err < 0) {
278                         fprintf(bat->err, _("Write PCM device error: %s(%d)\n"),
279                                         snd_strerror(err), err);
280                         return err;
281                 }
282
283                 if (err > 0) {
284                         remain -= err;
285                         offset += err * sndpcm->frame_bits / 8;
286                 }
287         }
288
289         return 0;
290 }
291
292 static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
293 {
294         int err;
295         int bytes = sndpcm->period_bytes; /* playback buffer size */
296         int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
297         FILE *fp = NULL;
298         struct wav_container wav;
299         int bytes_total = 0;
300
301         if (bat->debugplay) {
302                 fp = fopen(bat->debugplay, "wb");
303                 if (fp == NULL) {
304                         fprintf(bat->err, _("Cannot open file for capture: "));
305                         fprintf(bat->err, _("%s %d\n"), bat->debugplay, -errno);
306                         return -errno;
307                 }
308                 /* leave space for wav header */
309                 err = fseek(fp, sizeof(wav), SEEK_SET);
310                 if (err != 0) {
311                         fprintf(bat->err, _("Seek file error: %d %d\n"),
312                                         err, -errno);
313                         return -errno;
314                 }
315         }
316
317         while (1) {
318                 err = generate_input_data(sndpcm, bytes, bat);
319                 if (err < 0)
320                         return err;
321                 else if (err > 0)
322                         break;
323
324                 if (bat->debugplay) {
325                         err = fwrite(sndpcm->buffer, 1, bytes, fp);
326                         if (err != bytes) {
327                                 fprintf(bat->err, _("Write file error: "));
328                                 fprintf(bat->err, _("%s(%d)\n"),
329                                                 snd_strerror(err), err);
330                                 return -EIO;
331                         }
332                         bytes_total += bytes;
333                 }
334
335                 bat->periods_played++;
336                 if (bat->period_is_limited
337                                 && bat->periods_played >= bat->periods_total)
338                         break;
339
340                 err = write_to_pcm(sndpcm, frames, bat);
341                 if (err != 0)
342                         return err;
343         }
344
345         if (bat->debugplay) {
346                 /* update wav header */
347                 prepare_wav_info(&wav, bat);
348                 wav.chunk.length = bytes_total;
349                 wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
350                         + sizeof(wav.format) + sizeof(wav.header) - 8;
351
352                 rewind(fp);
353                 err = write_wav_header(fp, &wav, bat);
354                 if (err != 0) {
355                         fprintf(bat->err, _("Write file error: %s %s(%d)\n"),
356                                         bat->debugplay, snd_strerror(err), err);
357                         return err;
358                 }
359                 fclose(fp);
360         }
361
362         snd_pcm_drain(sndpcm->handle);
363
364         return 0;
365 }
366
367 /**
368  * Play
369  */
370 void *playback_alsa(struct bat *bat)
371 {
372         int err = 0;
373         struct pcm_container sndpcm;
374
375         fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
376
377         retval_play = 0;
378         memset(&sndpcm, 0, sizeof(sndpcm));
379
380         if (bat->playback.device == NULL) {
381                 fprintf(bat->err, _("No PCM device for playback: exit\n"));
382                 retval_play = 1;
383                 goto exit1;
384         }
385
386         err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
387                         SND_PCM_STREAM_PLAYBACK, 0);
388         if (err != 0) {
389                 fprintf(bat->err, _("Cannot open PCM playback device: "));
390                 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
391                 retval_play = 1;
392                 goto exit1;
393         }
394
395         err = set_snd_pcm_params(bat, &sndpcm);
396         if (err != 0) {
397                 retval_play = 1;
398                 goto exit2;
399         }
400
401         if (bat->playback.file == NULL) {
402                 fprintf(bat->log, _("Playing generated audio sine wave"));
403                 bat->sinus_duration == 0 ?
404                         fprintf(bat->log, _(" endlessly\n")) :
405                         fprintf(bat->log, _("\n"));
406         } else {
407                 fprintf(bat->log, _("Playing input audio file: %s\n"),
408                                 bat->playback.file);
409                 bat->fp = fopen(bat->playback.file, "rb");
410                 if (bat->fp == NULL) {
411                         fprintf(bat->err, _("Cannot open file for capture: "));
412                         fprintf(bat->err, _("%s %d\n"),
413                                         bat->playback.file, -errno);
414                         retval_play = 1;
415                         goto exit3;
416                 }
417                 /* Skip header */
418                 err = read_wav_header(bat, bat->playback.file, bat->fp, true);
419                 if (err != 0) {
420                         retval_play = 1;
421                         goto exit4;
422                 }
423         }
424
425         err = write_to_pcm_loop(&sndpcm, bat);
426         if (err != 0) {
427                 retval_play = 1;
428                 goto exit4;
429         }
430
431 exit4:
432         if (bat->playback.file)
433                 fclose(bat->fp);
434 exit3:
435         free(sndpcm.buffer);
436 exit2:
437         snd_pcm_close(sndpcm.handle);
438 exit1:
439         pthread_exit(&retval_play);
440 }
441
442 static int read_from_pcm(struct pcm_container *sndpcm,
443                 int frames, struct bat *bat)
444 {
445         int err = 0;
446         int offset = 0;
447         int remain = frames;
448
449         while (remain > 0) {
450                 err = snd_pcm_readi(sndpcm->handle,
451                                 sndpcm->buffer + offset, remain);
452                 if (err == -EAGAIN || (err >= 0 && err < remain)) {
453                         snd_pcm_wait(sndpcm->handle, 500);
454                 } else if (err == -EPIPE) {
455                         snd_pcm_prepare(sndpcm->handle);
456                         fprintf(bat->err, _("Overrun: %s(%d)\n"),
457                                         snd_strerror(err), err);
458                 } else if (err < 0) {
459                         fprintf(bat->err, _("Read PCM device error: %s(%d)\n"),
460                                         snd_strerror(err), err);
461                         return err;
462                 }
463
464                 if (err > 0) {
465                         remain -= err;
466                         offset += err * sndpcm->frame_bits / 8;
467                 }
468         }
469
470         return 0;
471 }
472
473 static int read_from_pcm_loop(FILE *fp, int count,
474                 struct pcm_container *sndpcm, struct bat *bat)
475 {
476         int err = 0;
477         int size, frames;
478         int remain = count;
479
480         while (remain > 0) {
481                 size = (remain <= sndpcm->period_bytes) ?
482                         remain : sndpcm->period_bytes;
483                 frames = size * 8 / sndpcm->frame_bits;
484
485                 /* read a chunk from pcm device */
486                 err = read_from_pcm(sndpcm, frames, bat);
487                 if (err != 0)
488                         return err;
489
490                 /* write the chunk to file */
491                 err = fwrite(sndpcm->buffer, 1, size, fp);
492                 if (err != size) {
493                         fprintf(bat->err, _("Write file error: %s(%d)\n"),
494                                         snd_strerror(err), err);
495                         return -EIO;
496                 }
497                 remain -= size;
498                 bat->periods_played++;
499
500                 if (bat->period_is_limited
501                                 && bat->periods_played >= bat->periods_total)
502                         break;
503         }
504
505         return 0;
506 }
507
508 static void pcm_cleanup(void *p)
509 {
510         snd_pcm_close(p);
511 }
512
513 static void file_cleanup(void *p)
514 {
515         fclose(p);
516 }
517
518 /**
519  * Record
520  */
521 void *record_alsa(struct bat *bat)
522 {
523         int err = 0;
524         FILE *fp = NULL;
525         struct pcm_container sndpcm;
526         struct wav_container wav;
527         int count;
528
529         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
530
531         fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
532
533         retval_record = 0;
534         memset(&sndpcm, 0, sizeof(sndpcm));
535
536         if (bat->capture.device == NULL) {
537                 fprintf(bat->err, _("No PCM device for capture: exit\n"));
538                 retval_record = 1;
539                 goto exit1;
540         }
541
542         err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
543                         SND_PCM_STREAM_CAPTURE, 0);
544         if (err != 0) {
545                 fprintf(bat->err, _("Cannot open PCM capture device: "));
546                 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
547                 retval_record = 1;
548                 goto exit1;
549         }
550
551         err = set_snd_pcm_params(bat, &sndpcm);
552         if (err != 0) {
553                 retval_record = 1;
554                 goto exit2;
555         }
556
557         remove(bat->capture.file);
558         fp = fopen(bat->capture.file, "w+");
559         if (fp == NULL) {
560                 fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
561                                 bat->capture.file, -errno);
562                 retval_record = 1;
563                 goto exit3;
564         }
565
566         prepare_wav_info(&wav, bat);
567
568         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
569         pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
570         pthread_cleanup_push(pcm_cleanup, sndpcm.handle);
571         pthread_cleanup_push(free, sndpcm.buffer);
572         pthread_cleanup_push(file_cleanup, fp);
573
574         err = write_wav_header(fp, &wav, bat);
575         if (err != 0) {
576                 retval_record = 1;
577                 goto exit4;
578         }
579
580         count = wav.chunk.length;
581         fprintf(bat->log, _("Recording ...\n"));
582         err = read_from_pcm_loop(fp, count, &sndpcm, bat);
583         if (err != 0) {
584                 retval_record = 1;
585                 goto exit4;
586         }
587
588         /* Normally we will never reach this part of code (before fail_exit) as
589            this thread will be cancelled by end of play thread. */
590         pthread_cleanup_pop(0);
591         pthread_cleanup_pop(0);
592         pthread_cleanup_pop(0);
593
594         snd_pcm_drain(sndpcm.handle);
595
596 exit4:
597         fclose(fp);
598 exit3:
599         free(sndpcm.buffer);
600 exit2:
601         snd_pcm_close(sndpcm.handle);
602 exit1:
603         pthread_exit(&retval_record);
604 }