2 * Copyright (C) 2013-2015 Intel Corporation
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.
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.
23 #include <alsa/asoundlib.h>
30 #include "bat-signal.h"
32 struct pcm_container {
34 snd_pcm_uframes_t period_size;
35 snd_pcm_uframes_t buffer_size;
36 snd_pcm_format_t format;
37 unsigned short channels;
44 static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
46 snd_pcm_hw_params_t *params;
47 unsigned int buffer_time = 0;
48 unsigned int period_time = 0;
51 const char *device_name = snd_pcm_name(sndpcm->handle);
53 /* Allocate a hardware parameters object. */
54 snd_pcm_hw_params_alloca(¶ms);
56 /* Fill it in with default values. */
57 err = snd_pcm_hw_params_any(sndpcm->handle, params);
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);
66 err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
67 SND_PCM_ACCESS_RW_INTERLEAVED);
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);
76 err = snd_pcm_hw_params_set_format(sndpcm->handle, params, bat->format);
78 fprintf(bat->err, _("Set parameter to device error: "));
79 fprintf(bat->err, _("PCM format: %d %s: %s(%d)\n"),
81 device_name, snd_strerror(err), err);
86 err = snd_pcm_hw_params_set_channels(sndpcm->handle,
87 params, bat->channels);
89 fprintf(bat->err, _("Set parameter to device error: "));
90 fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
92 device_name, snd_strerror(err), err);
96 /* Set sampling rate */
98 err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
102 fprintf(bat->err, _("Set parameter to device error: "));
103 fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
105 device_name, snd_strerror(err), err);
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"),
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"),
121 device_name, snd_strerror(err), err);
125 if (buffer_time > MAX_BUFFERTIME)
126 buffer_time = MAX_BUFFERTIME;
128 period_time = buffer_time / DIV_BUFFERTIME;
130 /* Set buffer time and period time */
131 err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle, params,
134 fprintf(bat->err, _("Set parameter to device error: "));
135 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
137 device_name, snd_strerror(err), err);
141 err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle, params,
144 fprintf(bat->err, _("Set parameter to device error: "));
145 fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
147 device_name, snd_strerror(err), err);
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);
159 err = snd_pcm_hw_params_get_period_size(params,
160 &sndpcm->period_size, 0);
162 fprintf(bat->err, _("Get parameter from device error: "));
163 fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
165 device_name, snd_strerror(err), err);
169 err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
171 fprintf(bat->err, _("Get parameter from device error: "));
172 fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
174 device_name, snd_strerror(err), err);
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);
185 err = snd_pcm_format_physical_width(bat->format);
187 fprintf(bat->err, _("Invalid parameters: "));
188 fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
192 sndpcm->sample_bits = err;
194 sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
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);
209 * Generate buffer to be played either from input file or from generated data
215 static int generate_input_data(struct pcm_container *sndpcm, int bytes,
220 int frames = bytes * 8 / sndpcm->frame_bits;
222 if (bat->playback.file != NULL) {
223 /* From input file */
227 err = fread(sndpcm->buffer + load, 1,
228 bytes - load, bat->fp);
232 _("End of playing.\n"));
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);
247 /* Generate sine wave */
248 if ((bat->sinus_duration) && (load > bat->sinus_duration))
251 err = generate_sine_wave(bat, frames, (void *)sndpcm->buffer);
261 static int write_to_pcm(const struct pcm_container *sndpcm,
262 int frames, struct bat *bat)
269 err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
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);
285 offset += err * sndpcm->frame_bits / 8;
292 static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
295 int bytes = sndpcm->period_bytes; /* playback buffer size */
296 int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
298 struct wav_container wav;
301 if (bat->debugplay) {
302 fp = fopen(bat->debugplay, "wb");
304 fprintf(bat->err, _("Cannot open file for capture: "));
305 fprintf(bat->err, _("%s %d\n"), bat->debugplay, -errno);
308 /* leave space for wav header */
309 err = fseek(fp, sizeof(wav), SEEK_SET);
311 fprintf(bat->err, _("Seek file error: %d %d\n"),
318 err = generate_input_data(sndpcm, bytes, bat);
324 if (bat->debugplay) {
325 err = fwrite(sndpcm->buffer, 1, bytes, fp);
327 fprintf(bat->err, _("Write file error: "));
328 fprintf(bat->err, _("%s(%d)\n"),
329 snd_strerror(err), err);
332 bytes_total += bytes;
335 bat->periods_played++;
336 if (bat->period_is_limited
337 && bat->periods_played >= bat->periods_total)
340 err = write_to_pcm(sndpcm, frames, bat);
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;
353 err = write_wav_header(fp, &wav, bat);
355 fprintf(bat->err, _("Write file error: %s %s(%d)\n"),
356 bat->debugplay, snd_strerror(err), err);
362 snd_pcm_drain(sndpcm->handle);
370 void *playback_alsa(struct bat *bat)
373 struct pcm_container sndpcm;
375 fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
378 memset(&sndpcm, 0, sizeof(sndpcm));
380 if (bat->playback.device == NULL) {
381 fprintf(bat->err, _("No PCM device for playback: exit\n"));
386 err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
387 SND_PCM_STREAM_PLAYBACK, 0);
389 fprintf(bat->err, _("Cannot open PCM playback device: "));
390 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
395 err = set_snd_pcm_params(bat, &sndpcm);
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"));
407 fprintf(bat->log, _("Playing input audio file: %s\n"),
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);
418 err = read_wav_header(bat, bat->playback.file, bat->fp, true);
425 err = write_to_pcm_loop(&sndpcm, bat);
432 if (bat->playback.file)
437 snd_pcm_close(sndpcm.handle);
439 pthread_exit(&retval_play);
442 static int read_from_pcm(struct pcm_container *sndpcm,
443 int frames, struct bat *bat)
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);
466 offset += err * sndpcm->frame_bits / 8;
473 static int read_from_pcm_loop(FILE *fp, int count,
474 struct pcm_container *sndpcm, struct bat *bat)
481 size = (remain <= sndpcm->period_bytes) ?
482 remain : sndpcm->period_bytes;
483 frames = size * 8 / sndpcm->frame_bits;
485 /* read a chunk from pcm device */
486 err = read_from_pcm(sndpcm, frames, bat);
490 /* write the chunk to file */
491 err = fwrite(sndpcm->buffer, 1, size, fp);
493 fprintf(bat->err, _("Write file error: %s(%d)\n"),
494 snd_strerror(err), err);
498 bat->periods_played++;
500 if (bat->period_is_limited
501 && bat->periods_played >= bat->periods_total)
508 static void pcm_cleanup(void *p)
513 static void file_cleanup(void *p)
521 void *record_alsa(struct bat *bat)
525 struct pcm_container sndpcm;
526 struct wav_container wav;
529 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
531 fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
534 memset(&sndpcm, 0, sizeof(sndpcm));
536 if (bat->capture.device == NULL) {
537 fprintf(bat->err, _("No PCM device for capture: exit\n"));
542 err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
543 SND_PCM_STREAM_CAPTURE, 0);
545 fprintf(bat->err, _("Cannot open PCM capture device: "));
546 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
551 err = set_snd_pcm_params(bat, &sndpcm);
557 remove(bat->capture.file);
558 fp = fopen(bat->capture.file, "w+");
560 fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
561 bat->capture.file, -errno);
566 prepare_wav_info(&wav, bat);
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);
574 err = write_wav_header(fp, &wav, bat);
580 count = wav.chunk.length;
581 fprintf(bat->log, _("Recording ...\n"));
582 err = read_from_pcm_loop(fp, count, &sndpcm, bat);
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);
594 snd_pcm_drain(sndpcm.handle);
601 snd_pcm_close(sndpcm.handle);
603 pthread_exit(&retval_record);