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 "latencytest.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 struct format_map_table {
45 enum _bat_pcm_format format_bat;
46 snd_pcm_format_t format_alsa;
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, },
58 static int format_convert(struct bat *bat, snd_pcm_format_t *fmt)
60 struct format_map_table *t = map_tables;
62 for (; t->format_bat != BAT_PCM_FORMAT_MAX; t++) {
63 if (t->format_bat == bat->format) {
64 *fmt = t->format_alsa;
68 fprintf(bat->err, _("Invalid format!\n"));
72 static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
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;
82 const char *device_name = snd_pcm_name(sndpcm->handle);
84 /* Convert common format to ALSA format */
85 err = format_convert(bat, &format);
89 /* Allocate a hardware parameters object. */
90 snd_pcm_hw_params_alloca(¶ms);
92 /* Fill it in with default values. */
93 err = snd_pcm_hw_params_any(sndpcm->handle, params);
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);
101 /* Set access mode */
102 err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
103 SND_PCM_ACCESS_RW_INTERLEAVED);
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);
112 err = snd_pcm_hw_params_set_format(sndpcm->handle, params, format);
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);
121 err = snd_pcm_hw_params_set_channels(sndpcm->handle,
122 params, bat->channels);
124 fprintf(bat->err, _("Set parameter to device error: "));
125 fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
127 device_name, snd_strerror(err), err);
131 /* Set sampling rate */
133 err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
137 fprintf(bat->err, _("Set parameter to device error: "));
138 fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
140 device_name, snd_strerror(err), err);
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"),
151 if (bat->buffer_size > 0 && bat->period_size == 0)
152 bat->period_size = bat->buffer_size / DIV_BUFFERSIZE;
154 if (bat->roundtriplatency && bat->buffer_size == 0) {
155 /* Set to minimum buffer size and period size
157 if (snd_pcm_hw_params_get_buffer_size_min(params,
160 _("Get parameter from device error: "));
161 fprintf(bat->err, _("buffer size min: %d %s: %s(%d)\n"),
163 device_name, snd_strerror(err), err);
167 if (snd_pcm_hw_params_get_period_size_min(params,
168 &period_size, 0) < 0) {
170 _("Get parameter from device error: "));
171 fprintf(bat->err, _("period size min: %d %s: %s(%d)\n"),
173 device_name, snd_strerror(err), err);
176 bat->buffer_size = (int) buffer_size;
177 bat->period_size = (int) period_size;
180 if (bat->buffer_size > 0) {
181 buffer_size = bat->buffer_size;
182 period_size = bat->period_size;
184 fprintf(bat->log, _("Set period size: %d buffer size: %d\n"),
185 (int) period_size, (int) buffer_size);
187 err = snd_pcm_hw_params_set_buffer_size_near(sndpcm->handle,
188 params, &buffer_size);
190 fprintf(bat->err, _("Set parameter to device error: "));
191 fprintf(bat->err, _("buffer size: %d %s: %s(%d)\n"),
193 device_name, snd_strerror(err), err);
197 err = snd_pcm_hw_params_set_period_size_near(sndpcm->handle,
198 params, &period_size, 0);
200 fprintf(bat->err, _("Set parameter to device error: "));
201 fprintf(bat->err, _("period size: %d %s: %s(%d)\n"),
203 device_name, snd_strerror(err), err);
207 if (snd_pcm_hw_params_get_buffer_time_max(params,
208 &buffer_time, 0) < 0) {
210 _("Get parameter from device error: "));
211 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
213 device_name, snd_strerror(err), err);
217 if (buffer_time > MAX_BUFFERTIME)
218 buffer_time = MAX_BUFFERTIME;
220 period_time = buffer_time / DIV_BUFFERTIME;
222 /* Set buffer time and period time */
223 err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,
224 params, &buffer_time, 0);
226 fprintf(bat->err, _("Set parameter to device error: "));
227 fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
229 device_name, snd_strerror(err), err);
233 err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
234 params, &period_time, 0);
236 fprintf(bat->err, _("Set parameter to device error: "));
237 fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
239 device_name, snd_strerror(err), err);
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);
252 err = snd_pcm_hw_params_get_period_size(params,
253 &sndpcm->period_size, 0);
255 fprintf(bat->err, _("Get parameter from device error: "));
256 fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
258 device_name, snd_strerror(err), err);
262 err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
264 fprintf(bat->err, _("Get parameter from device error: "));
265 fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
267 device_name, snd_strerror(err), err);
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);
278 fprintf(bat->log, _("Get period size: %d buffer size: %d\n"),
279 (int) sndpcm->period_size, (int) sndpcm->buffer_size);
281 err = snd_pcm_format_physical_width(format);
283 fprintf(bat->err, _("Invalid parameters: "));
284 fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
288 sndpcm->sample_bits = err;
290 sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
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);
304 static int write_to_pcm(const struct pcm_container *sndpcm,
305 int frames, struct bat *bat)
312 err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
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 */
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);
335 offset += err * sndpcm->frame_bits / 8;
343 * Process output data for latency test
345 static int latencytest_process_output(struct pcm_container *sndpcm,
349 int bytes = sndpcm->period_bytes; /* playback buffer size */
350 int frames = sndpcm->period_size; /* frame count */
352 bat->latency.is_playing = true;
355 /* generate output data */
356 err = handleoutput(bat, sndpcm->buffer, bytes, frames);
360 err = write_to_pcm(sndpcm, frames, bat);
364 /* Xrun error, terminate the playback thread*/
365 if (bat->latency.xrun_error == true)
368 if (bat->latency.state == LATENCY_STATE_COMPLETE_SUCCESS)
371 bat->periods_played++;
374 bat->latency.is_playing = false;
379 static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
382 int bytes = sndpcm->period_bytes; /* playback buffer size */
383 int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
387 if (bat->debugplay) {
388 fp = fopen(bat->debugplay, "wb");
391 fprintf(bat->err, _("Cannot open file: %s %d\n"),
392 bat->debugplay, err);
395 /* leave space for wav header */
396 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
404 err = generate_input_data(bat, sndpcm->buffer, bytes, frames);
408 if (bat->debugplay) {
409 if (fwrite(sndpcm->buffer, 1, bytes, fp) != bytes) {
413 bytes_total += bytes;
416 bat->periods_played++;
417 if (bat->period_is_limited
418 && bat->periods_played >= bat->periods_total)
421 err = write_to_pcm(sndpcm, frames, bat);
426 if (bat->debugplay) {
427 update_wav_header(bat, fp, bytes_total);
431 snd_pcm_drain(sndpcm->handle);
439 void *playback_alsa(struct bat *bat)
442 struct pcm_container sndpcm;
444 fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
447 memset(&sndpcm, 0, sizeof(sndpcm));
449 err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
450 SND_PCM_STREAM_PLAYBACK, 0);
452 fprintf(bat->err, _("Cannot open PCM playback device: "));
453 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
458 err = set_snd_pcm_params(bat, &sndpcm);
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"));
470 fprintf(bat->log, _("Playing input audio file: %s\n"),
472 bat->fp = fopen(bat->playback.file, "rb");
474 if (bat->fp == NULL) {
475 fprintf(bat->err, _("Cannot open file: %s %d\n"),
476 bat->playback.file, err);
481 err = read_wav_header(bat, bat->playback.file, bat->fp, true);
488 if (bat->roundtriplatency)
489 err = latencytest_process_output(&sndpcm, bat);
491 err = write_to_pcm_loop(&sndpcm, bat);
498 if (bat->playback.file)
503 snd_pcm_close(sndpcm.handle);
505 pthread_exit(&retval_play);
508 static int read_from_pcm(struct pcm_container *sndpcm,
509 int frames, struct bat *bat)
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 */
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);
539 offset += err * sndpcm->frame_bits / 8;
546 static int read_from_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
552 int bytes_count = bat->frames * bat->frame_size;
553 int remain = bytes_count;
555 remove(bat->capture.file);
556 fp = fopen(bat->capture.file, "wb");
559 fprintf(bat->err, _("Cannot open file: %s %d\n"),
560 bat->capture.file, err);
563 /* leave space for file header */
564 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
571 size = (remain <= sndpcm->period_bytes) ?
572 remain : sndpcm->period_bytes;
573 frames = size * 8 / sndpcm->frame_bits;
575 /* read a chunk from pcm device */
576 err = read_from_pcm(sndpcm, frames, bat);
580 /* write the chunk to file */
581 if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
588 bat->periods_played++;
590 if (bat->period_is_limited
591 && bat->periods_played >= bat->periods_total)
595 update_wav_header(bat, fp, bytes_read);
602 * Process input data for latency test
604 static int latencytest_process_input(struct pcm_container *sndpcm,
610 int frames = sndpcm->period_size;
611 int size = sndpcm->period_bytes;
612 int bytes_count = bat->frames * bat->frame_size;
614 remove(bat->capture.file);
615 fp = fopen(bat->capture.file, "wb");
618 fprintf(bat->err, _("Cannot open file: %s %d\n"),
619 bat->capture.file, err);
622 /* leave space for file header */
623 if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
628 bat->latency.is_capturing = true;
630 while (bytes_read < bytes_count) {
631 /* read a chunk from pcm device */
632 err = read_from_pcm(sndpcm, frames, bat);
636 /* Xrun error, terminate the capture thread*/
637 if (bat->latency.xrun_error == true)
640 err = handleinput(bat, sndpcm->buffer, frames);
644 if (bat->latency.is_playing == false)
647 /* write the chunk to file */
648 if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
656 bat->latency.is_capturing = false;
658 update_wav_header(bat, fp, bytes_read);
665 static void pcm_cleanup(void *p)
673 void *record_alsa(struct bat *bat)
676 struct pcm_container sndpcm;
678 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
680 fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
683 memset(&sndpcm, 0, sizeof(sndpcm));
685 err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
686 SND_PCM_STREAM_CAPTURE, 0);
688 fprintf(bat->err, _("Cannot open PCM capture device: "));
689 fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
694 err = set_snd_pcm_params(bat, &sndpcm);
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);
705 fprintf(bat->log, _("Recording ...\n"));
706 if (bat->roundtriplatency)
707 err = latencytest_process_input(&sndpcm, bat);
709 err = read_from_pcm_loop(&sndpcm, bat);
711 pthread_cleanup_pop(0);
712 pthread_cleanup_pop(0);
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);
728 snd_pcm_close(sndpcm.handle);
730 pthread_exit(&retval_record);