blob: 9df7ba9f387d226559ec9faa1b993c63aa388eec [file] [log] [blame]
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +08001/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
Earl Ou7ae955b2016-11-30 16:41:22 +080021#include <math.h>
22#include <pthread.h>
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080023#include <stdio.h>
24#include <stdlib.h>
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080025#include <sys/time.h>
Earl Ou7ae955b2016-11-30 16:41:22 +080026
27#include <alsa/asoundlib.h>
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080028
29#include "cras_client.h"
30
31#define CAPTURE_MORE_COUNT 50
32#define PLAYBACK_COUNT 50
33#define PLAYBACK_SILENT_COUNT 50
34#define PLAYBACK_TIMEOUT_COUNT 100
Louis Collard921fb152018-02-08 14:56:28 +080035#define TTY_OUTPUT_SIZE 1024
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080036
37static double phase = M_PI / 2;
38static unsigned rate = 48000;
39static unsigned channels = 2;
40static snd_pcm_uframes_t buffer_frames = 480;
41static snd_pcm_uframes_t period_size = 240;
42static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
43
44static struct timeval *cras_play_time = NULL;
45static struct timeval *cras_cap_time = NULL;
46static int noise_threshold = 0x4000;
Louis Collard921fb152018-02-08 14:56:28 +080047static char *tty_output_dev = NULL;
48static FILE *tty_output = NULL;
49static const char tty_zeros_block[TTY_OUTPUT_SIZE] = {0};
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080050
51static int capture_count;
52static int playback_count;
53static snd_pcm_sframes_t playback_delay_frames;
54static struct timeval sine_start_tv;
55
56// Mutex and the variables to protect.
57static pthread_mutex_t latency_test_mutex;
58static pthread_cond_t terminate_test;
59static pthread_cond_t sine_start;
60static int terminate_playback;
61static int terminate_capture;
62static int sine_started = 0;
63
64static void generate_sine(const snd_pcm_channel_area_t *areas,
65 snd_pcm_uframes_t offset, int count,
66 double *_phase)
67{
68 static double max_phase = 2. * M_PI;
69 double phase = *_phase;
70 double step = max_phase * 1000 / (double)rate;
71 unsigned char *samples[channels];
72 int steps[channels];
73 unsigned int chn;
74 int format_bits = snd_pcm_format_width(format);
75 unsigned int maxval = (1 << (format_bits - 1)) - 1;
76 int bps = format_bits / 8; /* bytes per sample */
77 int phys_bps = snd_pcm_format_physical_width(format) / 8;
78 int big_endian = snd_pcm_format_big_endian(format) == 1;
79 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
80 int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
81 format == SND_PCM_FORMAT_FLOAT_BE);
82
83 /* Verify and prepare the contents of areas */
84 for (chn = 0; chn < channels; chn++) {
85 if ((areas[chn].first % 8) != 0) {
86 fprintf(stderr, "areas[%i].first == %i, aborting...\n", chn,
87 areas[chn].first);
88 exit(EXIT_FAILURE);
89 }
90 if ((areas[chn].step % 16) != 0) {
91 fprintf(stderr, "areas[%i].step == %i, aborting...\n", chn, areas
92 [chn].step);
93 exit(EXIT_FAILURE);
94 }
95 steps[chn] = areas[chn].step / 8;
96 samples[chn] = ((unsigned char *)areas[chn].addr) +
97 (areas[chn].first / 8) + offset * steps[chn];
98 }
99
100 /* Fill the channel areas */
101 while (count-- > 0) {
102 union {
103 float f;
104 int i;
105 } fval;
106 int res, i;
107 if (is_float) {
108 fval.f = sin(phase) * maxval;
109 res = fval.i;
110 } else
111 res = sin(phase) * maxval;
112 if (to_unsigned)
113 res ^= 1U << (format_bits - 1);
114 for (chn = 0; chn < channels; chn++) {
115 /* Generate data in native endian format */
116 if (big_endian) {
117 for (i = 0; i < bps; i++)
118 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
119 } else {
120 for (i = 0; i < bps; i++)
121 *(samples[chn] + i) = (res >> i * 8) & 0xff;
122 }
123 samples[chn] += steps[chn];
124 }
125 phase += step;
126 if (phase >= max_phase)
127 phase -= max_phase;
128 }
129 *_phase = phase;
130}
131
132static void config_pcm(snd_pcm_t *handle,
133 unsigned int rate,
134 unsigned int channels,
135 snd_pcm_format_t format,
136 snd_pcm_uframes_t *buffer_size,
137 snd_pcm_uframes_t *period_size)
138{
139 int err;
140 snd_pcm_hw_params_t *hw_params;
141
142 if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
143 fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",
144 snd_strerror(err));
145 exit(1);
146 }
147
148 if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
149 fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",
150 snd_strerror(err));
151 exit(1);
152 }
153
154 if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
155 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
156 fprintf(stderr, "cannot set access type (%s)\n",
157 snd_strerror(err));
158 exit(1);
159 }
160
161 if ((err = snd_pcm_hw_params_set_format(handle, hw_params,
162 format)) < 0) {
163 fprintf(stderr, "cannot set sample format (%s)\n",
164 snd_strerror(err));
165 exit(1);
166 }
167
168 if ((err = snd_pcm_hw_params_set_rate_near(
169 handle, hw_params, &rate, 0)) < 0) {
170 fprintf(stderr, "cannot set sample rate (%s)\n",
171 snd_strerror(err));
172 exit(1);
173 }
174
175 if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, 2)) < 0) {
176 fprintf(stderr, "cannot set channel count (%s)\n",
177 snd_strerror(err));
178 exit(1);
179 }
180
181 if ((err = snd_pcm_hw_params_set_buffer_size_near(
182 handle, hw_params, buffer_size)) < 0) {
183 fprintf(stderr, "cannot set channel count (%s)\n",
184 snd_strerror(err));
185 exit(1);
186 }
187
188 if ((err = snd_pcm_hw_params_set_period_size_near(
189 handle, hw_params, period_size, 0)) < 0) {
190 fprintf(stderr, "cannot set channel count (%s)\n",
191 snd_strerror(err));
192 exit(1);
193 }
194
195 if ((err = snd_pcm_hw_params(handle, hw_params)) < 0) {
196 fprintf(stderr, "cannot set parameters (%s)\n",
197 snd_strerror(err));
198 exit(1);
199 }
200
201 snd_pcm_hw_params_free(hw_params);
202
203 if ((err = snd_pcm_prepare(handle)) < 0) {
204 fprintf(stderr, "cannot prepare audio interface for use (%s)\n",
205 snd_strerror(err));
206 exit(1);
207 }
208}
209
210static int capture_some(snd_pcm_t *pcm, short *buf, unsigned len,
211 snd_pcm_sframes_t *cap_delay_frames)
212{
213 snd_pcm_sframes_t frames = snd_pcm_avail(pcm);
214 int err;
215
216 if (frames > 0) {
217 frames = frames > len ? len : frames;
218
219 snd_pcm_delay(pcm, cap_delay_frames);
220 if ((err = snd_pcm_readi(pcm, buf, frames)) != frames) {
221 fprintf(stderr, "read from audio interface failed (%s)\n",
222 snd_strerror(err));
223 exit(1);
224 }
225 }
226
227 return (int)frames;
228}
229
230/* Looks for the first sample in buffer whose absolute value exceeds
231 * noise_threshold. Returns the index of found sample in frames, -1
232 * if not found. */
233static int check_for_noise(short *buf, unsigned len, unsigned channels)
234{
235 unsigned int i;
236 for (i = 0; i < len * channels; i++)
237 if (abs(buf[i]) > noise_threshold)
238 return i / channels;
239 return -1;
240}
241
242static unsigned long subtract_timevals(const struct timeval *end,
243 const struct timeval *beg)
244{
245 struct timeval diff;
246 /* If end is before geb, return 0. */
247 if ((end->tv_sec < beg->tv_sec) ||
248 ((end->tv_sec == beg->tv_sec) && (end->tv_usec <= beg->tv_usec)))
249 diff.tv_sec = diff.tv_usec = 0;
250 else {
251 if (end->tv_usec < beg->tv_usec) {
252 diff.tv_sec = end->tv_sec - beg->tv_sec - 1;
253 diff.tv_usec =
254 end->tv_usec + 1000000L - beg->tv_usec;
255 } else {
256 diff.tv_sec = end->tv_sec - beg->tv_sec;
257 diff.tv_usec = end->tv_usec - beg->tv_usec;
258 }
259 }
260 return diff.tv_sec * 1000000 + diff.tv_usec;
261}
262
263static int cras_capture_tone(struct cras_client *client,
264 cras_stream_id_t stream_id,
265 uint8_t *samples, size_t frames,
266 const struct timespec *sample_time,
267 void *arg)
268{
269 assert(snd_pcm_format_physical_width(format) == 16);
270
271 short *data = (short *)samples;
272 int cap_frames_index;
273
274 if (!sine_started || terminate_capture) {
275 return frames;
276 }
277
278 if ((cap_frames_index = check_for_noise(data, frames, channels)) >= 0) {
279 fprintf(stderr, "Got noise\n");
280
281 struct timespec shifted_time = *sample_time;
282 shifted_time.tv_nsec += 1000000000L / rate * cap_frames_index;
283 while (shifted_time.tv_nsec > 1000000000L) {
284 shifted_time.tv_sec++;
285 shifted_time.tv_nsec -= 1000000000L;
286 }
Earl Ou7ae955b2016-11-30 16:41:22 +0800287 cras_client_calc_capture_latency(&shifted_time, (struct timespec *)arg);
288 cras_cap_time = (struct timeval *)malloc(sizeof(*cras_cap_time));
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800289 gettimeofday(cras_cap_time, NULL);
290
291 // Terminate the test since noise got captured.
292 pthread_mutex_lock(&latency_test_mutex);
293 terminate_capture = 1;
294 pthread_cond_signal(&terminate_test);
295 pthread_mutex_unlock(&latency_test_mutex);
296 }
297
298 return frames;
299}
300
301/* Callback for tone playback. Playback latency will be passed
302 * as arg and updated at the first sine tone right after silent.
303 */
304static int cras_play_tone(struct cras_client *client,
305 cras_stream_id_t stream_id,
306 uint8_t *samples, size_t frames,
307 const struct timespec *sample_time,
308 void *arg)
309{
310 snd_pcm_channel_area_t *areas;
311 int chn;
312 size_t sample_bytes;
313
314 sample_bytes = snd_pcm_format_physical_width(format) / 8;
315
316 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
317 for (chn = 0; chn < channels; chn++) {
318 areas[chn].addr = samples + chn * sample_bytes;
319 areas[chn].first = 0;
320 areas[chn].step = channels *
321 snd_pcm_format_physical_width(format);
322 }
323
324 /* Write zero first when playback_count < PLAYBACK_SILENT_COUNT
325 * or noise got captured. */
326 if (playback_count < PLAYBACK_SILENT_COUNT) {
327 memset(samples, 0, sample_bytes * frames * channels);
328 } else if (playback_count > PLAYBACK_TIMEOUT_COUNT) {
329 // Timeout, terminate test.
330 pthread_mutex_lock(&latency_test_mutex);
331 terminate_capture = 1;
332 pthread_cond_signal(&terminate_test);
333 pthread_mutex_unlock(&latency_test_mutex);
334 } else {
335 generate_sine(areas, 0, frames, &phase);
336
337 if (!sine_started) {
338 /* Signal that sine tone started playing and update playback time
339 * and latency at first played frame. */
340 sine_started = 1;
341 cras_client_calc_playback_latency(sample_time,
Earl Ou7ae955b2016-11-30 16:41:22 +0800342 (struct timespec *)arg);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800343 cras_play_time =
Earl Ou7ae955b2016-11-30 16:41:22 +0800344 (struct timeval *)malloc(sizeof(*cras_play_time));
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800345 gettimeofday(cras_play_time, NULL);
Louis Collard921fb152018-02-08 14:56:28 +0800346 if (tty_output) {
347 fwrite(tty_zeros_block, sizeof(char),
348 TTY_OUTPUT_SIZE, tty_output);
349 fflush(tty_output);
350 }
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800351 }
352 }
353
354 playback_count++;
355 return frames;
356}
357
358static int stream_error(struct cras_client *client,
359 cras_stream_id_t stream_id,
360 int err,
361 void *arg)
362{
363 fprintf(stderr, "Stream error %d\n", err);
364 return 0;
365}
366
367/* Adds stream to cras client. */
368static int cras_add_stream(struct cras_client *client,
369 struct cras_stream_params *params,
370 enum CRAS_STREAM_DIRECTION direction,
371 struct timespec *user_data)
372{
373 struct cras_audio_format *aud_format;
374 cras_playback_cb_t aud_cb;
375 cras_error_cb_t error_cb;
Wu-Cheng Liee6aafc2017-11-03 18:37:49 +0800376 size_t cb_threshold = buffer_frames;
377 size_t min_cb_level = buffer_frames;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800378 int rc = 0;
379 cras_stream_id_t stream_id = 0;
380
381 aud_format = cras_audio_format_create(format, rate, channels);
382 if (aud_format == NULL)
383 return -ENOMEM;
384
385 /* Create and start stream */
386 aud_cb = (direction == CRAS_STREAM_OUTPUT)
387 ? cras_play_tone
388 : cras_capture_tone;
389 error_cb = stream_error;
390 params = cras_client_stream_params_create(direction,
391 buffer_frames,
392 cb_threshold,
393 min_cb_level,
394 0,
395 0,
396 user_data,
397 aud_cb,
398 error_cb,
399 aud_format);
400 if (params == NULL)
401 return -ENOMEM;
402
403 rc = cras_client_add_stream(client, &stream_id, params);
404 if (rc < 0) {
405 fprintf(stderr, "Add a stream fail.\n");
406 return rc;
407 }
408 cras_audio_format_destroy(aud_format);
409 return 0;
410}
411
412static void *alsa_play(void *arg) {
413 snd_pcm_t *handle = (snd_pcm_t *)arg;
414 short *play_buf;
415 snd_pcm_channel_area_t *areas;
416 unsigned int chn, num_buffers;
417 int err;
418
419 play_buf = calloc(buffer_frames * channels, sizeof(play_buf[0]));
420 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
421
422 for (chn = 0; chn < channels; chn++) {
423 areas[chn].addr = play_buf;
424 areas[chn].first = chn * snd_pcm_format_physical_width(format);
425 areas[chn].step = channels * snd_pcm_format_physical_width(format);
426 }
427
428 for (num_buffers = 0; num_buffers < PLAYBACK_SILENT_COUNT; num_buffers++) {
429 if ((err = snd_pcm_writei(handle, play_buf, period_size))
430 != period_size) {
431 fprintf(stderr, "write to audio interface failed (%s)\n",
432 snd_strerror(err));
433 exit(1);
434 }
435 }
436
437 generate_sine(areas, 0, period_size, &phase);
438 snd_pcm_delay(handle, &playback_delay_frames);
439 gettimeofday(&sine_start_tv, NULL);
440
441 num_buffers = 0;
442 int avail_frames;
443
444 /* Play a sine wave and look for it on capture thread.
445 * This will fail for latency > 500mS. */
446 while (!terminate_playback && num_buffers < PLAYBACK_COUNT) {
447 avail_frames = snd_pcm_avail(handle);
448 if (avail_frames >= period_size) {
449 pthread_mutex_lock(&latency_test_mutex);
450 if (!sine_started) {
451 sine_started = 1;
452 pthread_cond_signal(&sine_start);
453 }
454 pthread_mutex_unlock(&latency_test_mutex);
455 if ((err = snd_pcm_writei(handle, play_buf, period_size))
456 != period_size) {
457 fprintf(stderr, "write to audio interface failed (%s)\n",
458 snd_strerror(err));
459 }
460 num_buffers++;
461 }
462 }
463 terminate_playback = 1;
464
465 if (num_buffers == PLAYBACK_COUNT)
466 fprintf(stdout, "Audio not detected.\n");
467
468 free(play_buf);
469 free(areas);
470 return 0;
471}
472
473static void *alsa_capture(void *arg) {
474 int err;
475 short *cap_buf;
476 snd_pcm_t *capture_handle = (snd_pcm_t *)arg;
477 snd_pcm_sframes_t cap_delay_frames;
478 int num_cap, noise_delay_frames;
479
480
481 cap_buf = calloc(buffer_frames * channels, sizeof(cap_buf[0]));
482
483 pthread_mutex_lock(&latency_test_mutex);
484 while (!sine_started) {
485 pthread_cond_wait(&sine_start, &latency_test_mutex);
486 }
487 pthread_mutex_unlock(&latency_test_mutex);
488
489 /* Begin capture. */
490 if ((err = snd_pcm_start(capture_handle)) < 0) {
491 fprintf(stderr, "cannot start audio interface for use (%s)\n",
492 snd_strerror(err));
493 exit(1);
494 }
495
496 while (!terminate_capture) {
497 num_cap = capture_some(capture_handle, cap_buf,
498 buffer_frames, &cap_delay_frames);
499
500 if (num_cap > 0 && (noise_delay_frames = check_for_noise(cap_buf,
501 num_cap, channels)) >= 0) {
502 struct timeval cap_time;
503 unsigned long latency_us;
504 gettimeofday(&cap_time, NULL);
505
506 fprintf(stderr, "Found audio\n");
507 fprintf(stderr, "Played at %llu %llu, %ld delay\n",
508 (unsigned long long)sine_start_tv.tv_sec,
509 (unsigned long long)sine_start_tv.tv_usec,
510 playback_delay_frames);
511 fprintf(stderr, "Capture at %llu %llu, %ld delay sample %d\n",
512 (unsigned long long)cap_time.tv_sec,
513 (unsigned long long)cap_time.tv_usec,
514 cap_delay_frames, noise_delay_frames);
515
516 latency_us = subtract_timevals(&cap_time, &sine_start_tv);
517 fprintf(stdout, "Measured Latency: %lu uS\n", latency_us);
518
519 latency_us = (playback_delay_frames + cap_delay_frames -
520 noise_delay_frames) * 1000000 / rate;
521 fprintf(stdout, "Reported Latency: %lu uS\n", latency_us);
522
523 // Noise captured, terminate both threads.
524 terminate_playback = 1;
525 terminate_capture = 1;
526 } else {
527 // Capture some more buffers after playback thread has terminated.
528 if (terminate_playback && capture_count++ < CAPTURE_MORE_COUNT)
529 terminate_capture = 1;
530 }
531 }
532
533 free(cap_buf);
534 return 0;
535}
536
537void cras_test_latency()
538{
539 int rc;
540 struct cras_client *client = NULL;
541 struct cras_stream_params *playback_params = NULL;
542 struct cras_stream_params *capture_params = NULL;
543
544 struct timespec playback_latency;
545 struct timespec capture_latency;
546
547 rc = cras_client_create(&client);
548 if (rc < 0) {
549 fprintf(stderr, "Create client fail.\n");
550 exit(1);
551 }
552 rc = cras_client_connect(client);
553 if (rc < 0) {
554 fprintf(stderr, "Connect to server fail.\n");
555 cras_client_destroy(client);
556 exit(1);
557 }
558
Louis Collard921fb152018-02-08 14:56:28 +0800559 if (tty_output_dev) {
560 tty_output = fopen(tty_output_dev, "w");
561 if (!tty_output)
562 fprintf(stderr, "Failed to open TTY output device: %s",
563 tty_output_dev);
564 else
565 fprintf(stdout, "Opened %s for UART signal\n",
566 tty_output_dev);
567 }
568
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800569 pthread_mutex_init(&latency_test_mutex, NULL);
570 pthread_cond_init(&sine_start, NULL);
571 pthread_cond_init(&terminate_test, NULL);
572
573 cras_client_run_thread(client);
574 rc = cras_add_stream(client,
575 playback_params,
576 CRAS_STREAM_OUTPUT,
577 &playback_latency);
578 if (rc < 0) {
579 fprintf(stderr, "Fail to add playback stream.\n");
580 exit(1);
581 }
582 rc = cras_add_stream(client,
583 capture_params,
584 CRAS_STREAM_INPUT,
585 &capture_latency);
586 if (rc < 0) {
587 fprintf(stderr, "Fail to add capture stream.\n");
588 exit(1);
589 }
590
591 pthread_mutex_lock(&latency_test_mutex);
592 while (!terminate_capture) {
593 pthread_cond_wait(&terminate_test, &latency_test_mutex);
594 }
595 pthread_mutex_unlock(&latency_test_mutex);
596
597 if (cras_cap_time && cras_play_time) {
Louis Collard921fb152018-02-08 14:56:28 +0800598 unsigned long playback_latency_us, capture_latency_us;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800599 unsigned long latency = subtract_timevals(cras_cap_time,
600 cras_play_time);
601 fprintf(stdout, "Measured Latency: %lu uS.\n", latency);
602
Louis Collard921fb152018-02-08 14:56:28 +0800603 playback_latency_us = (playback_latency.tv_sec * 1000000) +
604 (playback_latency.tv_nsec / 1000);
605 capture_latency_us = (capture_latency.tv_sec * 1000000) +
606 (capture_latency.tv_nsec / 1000);
607
608 fprintf(stdout,
609 "Reported Latency: %lu uS.\n"
610 "Reported Output Latency: %lu uS.\n"
611 "Reported Input Latency: %lu uS.\n",
612 playback_latency_us + capture_latency_us,
613 playback_latency_us,
614 capture_latency_us);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800615 } else {
616 fprintf(stdout, "Audio not detected.\n");
617 }
618
619 /* Destruct things. */
620 cras_client_stop(client);
621 cras_client_stream_params_destroy(playback_params);
622 cras_client_stream_params_destroy(capture_params);
Louis Collard921fb152018-02-08 14:56:28 +0800623 if (tty_output)
624 fclose(tty_output);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800625 if (cras_play_time)
626 free(cras_play_time);
627 if (cras_cap_time)
628 free(cras_cap_time);
629}
630
Earl Ou7ae955b2016-11-30 16:41:22 +0800631void alsa_test_latency(char *play_dev, char *cap_dev)
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800632{
633 int err;
634 snd_pcm_t *playback_handle;
635 snd_pcm_t *capture_handle;
636
637 pthread_t capture_thread;
638 pthread_t playback_thread;
639
640 if ((err = snd_pcm_open(&playback_handle, play_dev,
641 SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
642 fprintf(stderr, "cannot open audio device %s (%s)\n",
643 play_dev, snd_strerror(err));
644 exit(1);
645 }
646 config_pcm(playback_handle, rate, channels, format, &buffer_frames,
647 &period_size);
648
649 if ((err = snd_pcm_open(&capture_handle, cap_dev,
650 SND_PCM_STREAM_CAPTURE, 0)) < 0) {
651 fprintf(stderr, "cannot open audio device %s (%s)\n",
652 cap_dev, snd_strerror(err));
653 exit(1);
654 }
655 config_pcm(capture_handle, rate, channels, format, &buffer_frames,
656 &period_size);
657
658 pthread_mutex_init(&latency_test_mutex, NULL);
659 pthread_cond_init(&sine_start, NULL);
660
661 pthread_create(&playback_thread, NULL, alsa_play, playback_handle);
662 pthread_create(&capture_thread, NULL, alsa_capture, capture_handle);
663
664 pthread_join(capture_thread, NULL);
665 pthread_join(playback_thread, NULL);
666
667 snd_pcm_close(playback_handle);
668 snd_pcm_close(capture_handle);
669}
670
671int main (int argc, char *argv[])
672{
673 int cras_only = 0;
Wu-Cheng Li25874662017-11-09 17:54:14 +0800674 char *play_dev = NULL;
675 char *cap_dev = NULL;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800676
677 int arg;
Louis Collard921fb152018-02-08 14:56:28 +0800678 while ((arg = getopt(argc, argv, "b:i:o:n:r:p:ct:")) != -1) {
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800679 switch (arg) {
680 case 'b':
681 buffer_frames = atoi(optarg);
682 break;
683 case 'c':
684 cras_only = 1;
685 break;
686 case 'i':
687 cap_dev = optarg;
688 fprintf(stderr, "Assign cap_dev %s\n", cap_dev);
689 break;
690 case 'n':
691 noise_threshold = atoi(optarg);
692 break;
693 case 'r':
694 rate = atoi(optarg);
695 break;
696 case 'o':
697 play_dev = optarg;
698 fprintf(stderr, "Assign play_dev %s\n", play_dev);
699 break;
700 case 'p':
701 period_size = atoi(optarg);
702 break;
Louis Collard921fb152018-02-08 14:56:28 +0800703 case 't':
704 tty_output_dev = optarg;
705 break;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800706 default:
707 return 1;
708 }
709 }
710
711 if (cras_only)
712 cras_test_latency();
Wu-Cheng Li25874662017-11-09 17:54:14 +0800713 else {
714 if (play_dev == NULL || cap_dev == NULL) {
715 fprintf(stderr, "Input/output devices must be set in Alsa mode.\n");
716 exit(1);
717 }
718 alsa_test_latency(play_dev, cap_dev);
719 }
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800720 exit(0);
721}