blob: 34f28a36ee4f1a52612f0c0c7967366b541412c5 [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
Tzung-Bi Shih65830a62019-01-24 13:55:34 +080051static int loop;
52static int cold;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +080053static int capture_count;
54static int playback_count;
55static snd_pcm_sframes_t playback_delay_frames;
56static struct timeval sine_start_tv;
57
58// Mutex and the variables to protect.
59static pthread_mutex_t latency_test_mutex;
60static pthread_cond_t terminate_test;
61static pthread_cond_t sine_start;
62static int terminate_playback;
63static int terminate_capture;
64static int sine_started = 0;
65
66static void generate_sine(const snd_pcm_channel_area_t *areas,
67 snd_pcm_uframes_t offset, int count,
68 double *_phase)
69{
70 static double max_phase = 2. * M_PI;
71 double phase = *_phase;
72 double step = max_phase * 1000 / (double)rate;
73 unsigned char *samples[channels];
74 int steps[channels];
75 unsigned int chn;
76 int format_bits = snd_pcm_format_width(format);
77 unsigned int maxval = (1 << (format_bits - 1)) - 1;
78 int bps = format_bits / 8; /* bytes per sample */
79 int phys_bps = snd_pcm_format_physical_width(format) / 8;
80 int big_endian = snd_pcm_format_big_endian(format) == 1;
81 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
82 int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
83 format == SND_PCM_FORMAT_FLOAT_BE);
84
85 /* Verify and prepare the contents of areas */
86 for (chn = 0; chn < channels; chn++) {
87 if ((areas[chn].first % 8) != 0) {
88 fprintf(stderr, "areas[%i].first == %i, aborting...\n", chn,
89 areas[chn].first);
90 exit(EXIT_FAILURE);
91 }
92 if ((areas[chn].step % 16) != 0) {
93 fprintf(stderr, "areas[%i].step == %i, aborting...\n", chn, areas
94 [chn].step);
95 exit(EXIT_FAILURE);
96 }
97 steps[chn] = areas[chn].step / 8;
98 samples[chn] = ((unsigned char *)areas[chn].addr) +
99 (areas[chn].first / 8) + offset * steps[chn];
100 }
101
102 /* Fill the channel areas */
103 while (count-- > 0) {
104 union {
105 float f;
106 int i;
107 } fval;
108 int res, i;
109 if (is_float) {
110 fval.f = sin(phase) * maxval;
111 res = fval.i;
112 } else
113 res = sin(phase) * maxval;
114 if (to_unsigned)
115 res ^= 1U << (format_bits - 1);
116 for (chn = 0; chn < channels; chn++) {
En-Shuo Hsuaa0a03d2021-04-12 20:06:24 +0800117 /* Generate data based on endian format */
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800118 if (big_endian) {
119 for (i = 0; i < bps; i++)
120 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
121 } else {
122 for (i = 0; i < bps; i++)
123 *(samples[chn] + i) = (res >> i * 8) & 0xff;
124 }
125 samples[chn] += steps[chn];
126 }
127 phase += step;
128 if (phase >= max_phase)
129 phase -= max_phase;
130 }
131 *_phase = phase;
132}
133
134static void config_pcm(snd_pcm_t *handle,
135 unsigned int rate,
136 unsigned int channels,
137 snd_pcm_format_t format,
138 snd_pcm_uframes_t *buffer_size,
139 snd_pcm_uframes_t *period_size)
140{
141 int err;
142 snd_pcm_hw_params_t *hw_params;
143
144 if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
145 fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",
146 snd_strerror(err));
147 exit(1);
148 }
149
150 if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
151 fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",
152 snd_strerror(err));
153 exit(1);
154 }
155
156 if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
157 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
158 fprintf(stderr, "cannot set access type (%s)\n",
159 snd_strerror(err));
160 exit(1);
161 }
162
163 if ((err = snd_pcm_hw_params_set_format(handle, hw_params,
164 format)) < 0) {
165 fprintf(stderr, "cannot set sample format (%s)\n",
166 snd_strerror(err));
167 exit(1);
168 }
169
170 if ((err = snd_pcm_hw_params_set_rate_near(
171 handle, hw_params, &rate, 0)) < 0) {
172 fprintf(stderr, "cannot set sample rate (%s)\n",
173 snd_strerror(err));
174 exit(1);
175 }
176
177 if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, 2)) < 0) {
178 fprintf(stderr, "cannot set channel count (%s)\n",
179 snd_strerror(err));
180 exit(1);
181 }
182
183 if ((err = snd_pcm_hw_params_set_buffer_size_near(
184 handle, hw_params, buffer_size)) < 0) {
185 fprintf(stderr, "cannot set channel count (%s)\n",
186 snd_strerror(err));
187 exit(1);
188 }
189
190 if ((err = snd_pcm_hw_params_set_period_size_near(
191 handle, hw_params, period_size, 0)) < 0) {
192 fprintf(stderr, "cannot set channel count (%s)\n",
193 snd_strerror(err));
194 exit(1);
195 }
196
197 if ((err = snd_pcm_hw_params(handle, hw_params)) < 0) {
198 fprintf(stderr, "cannot set parameters (%s)\n",
199 snd_strerror(err));
200 exit(1);
201 }
202
203 snd_pcm_hw_params_free(hw_params);
204
205 if ((err = snd_pcm_prepare(handle)) < 0) {
206 fprintf(stderr, "cannot prepare audio interface for use (%s)\n",
207 snd_strerror(err));
208 exit(1);
209 }
210}
211
212static int capture_some(snd_pcm_t *pcm, short *buf, unsigned len,
213 snd_pcm_sframes_t *cap_delay_frames)
214{
215 snd_pcm_sframes_t frames = snd_pcm_avail(pcm);
216 int err;
217
218 if (frames > 0) {
219 frames = frames > len ? len : frames;
220
221 snd_pcm_delay(pcm, cap_delay_frames);
222 if ((err = snd_pcm_readi(pcm, buf, frames)) != frames) {
223 fprintf(stderr, "read from audio interface failed (%s)\n",
224 snd_strerror(err));
225 exit(1);
226 }
227 }
228
229 return (int)frames;
230}
231
232/* Looks for the first sample in buffer whose absolute value exceeds
233 * noise_threshold. Returns the index of found sample in frames, -1
234 * if not found. */
235static int check_for_noise(short *buf, unsigned len, unsigned channels)
236{
237 unsigned int i;
238 for (i = 0; i < len * channels; i++)
239 if (abs(buf[i]) > noise_threshold)
240 return i / channels;
241 return -1;
242}
243
244static unsigned long subtract_timevals(const struct timeval *end,
245 const struct timeval *beg)
246{
247 struct timeval diff;
248 /* If end is before geb, return 0. */
249 if ((end->tv_sec < beg->tv_sec) ||
250 ((end->tv_sec == beg->tv_sec) && (end->tv_usec <= beg->tv_usec)))
251 diff.tv_sec = diff.tv_usec = 0;
252 else {
253 if (end->tv_usec < beg->tv_usec) {
254 diff.tv_sec = end->tv_sec - beg->tv_sec - 1;
255 diff.tv_usec =
256 end->tv_usec + 1000000L - beg->tv_usec;
257 } else {
258 diff.tv_sec = end->tv_sec - beg->tv_sec;
259 diff.tv_usec = end->tv_usec - beg->tv_usec;
260 }
261 }
262 return diff.tv_sec * 1000000 + diff.tv_usec;
263}
264
265static int cras_capture_tone(struct cras_client *client,
266 cras_stream_id_t stream_id,
267 uint8_t *samples, size_t frames,
268 const struct timespec *sample_time,
269 void *arg)
270{
271 assert(snd_pcm_format_physical_width(format) == 16);
272
273 short *data = (short *)samples;
274 int cap_frames_index;
275
276 if (!sine_started || terminate_capture) {
277 return frames;
278 }
279
280 if ((cap_frames_index = check_for_noise(data, frames, channels)) >= 0) {
281 fprintf(stderr, "Got noise\n");
282
283 struct timespec shifted_time = *sample_time;
284 shifted_time.tv_nsec += 1000000000L / rate * cap_frames_index;
285 while (shifted_time.tv_nsec > 1000000000L) {
286 shifted_time.tv_sec++;
287 shifted_time.tv_nsec -= 1000000000L;
288 }
Earl Ou7ae955b2016-11-30 16:41:22 +0800289 cras_client_calc_capture_latency(&shifted_time, (struct timespec *)arg);
290 cras_cap_time = (struct timeval *)malloc(sizeof(*cras_cap_time));
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800291 gettimeofday(cras_cap_time, NULL);
292
293 // Terminate the test since noise got captured.
294 pthread_mutex_lock(&latency_test_mutex);
295 terminate_capture = 1;
296 pthread_cond_signal(&terminate_test);
297 pthread_mutex_unlock(&latency_test_mutex);
298 }
299
300 return frames;
301}
302
303/* Callback for tone playback. Playback latency will be passed
304 * as arg and updated at the first sine tone right after silent.
305 */
306static int cras_play_tone(struct cras_client *client,
307 cras_stream_id_t stream_id,
308 uint8_t *samples, size_t frames,
309 const struct timespec *sample_time,
310 void *arg)
311{
312 snd_pcm_channel_area_t *areas;
313 int chn;
314 size_t sample_bytes;
315
316 sample_bytes = snd_pcm_format_physical_width(format) / 8;
317
318 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
319 for (chn = 0; chn < channels; chn++) {
320 areas[chn].addr = samples + chn * sample_bytes;
321 areas[chn].first = 0;
322 areas[chn].step = channels *
323 snd_pcm_format_physical_width(format);
324 }
325
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800326 if (cold)
327 goto play_tone;
328
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800329 /* Write zero first when playback_count < PLAYBACK_SILENT_COUNT
330 * or noise got captured. */
331 if (playback_count < PLAYBACK_SILENT_COUNT) {
332 memset(samples, 0, sample_bytes * frames * channels);
333 } else if (playback_count > PLAYBACK_TIMEOUT_COUNT) {
334 // Timeout, terminate test.
335 pthread_mutex_lock(&latency_test_mutex);
336 terminate_capture = 1;
337 pthread_cond_signal(&terminate_test);
338 pthread_mutex_unlock(&latency_test_mutex);
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800339
340 /* for loop mode: to avoid underrun */
341 memset(samples, 0, sample_bytes * frames * channels);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800342 } else {
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800343play_tone:
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800344 generate_sine(areas, 0, frames, &phase);
345
346 if (!sine_started) {
347 /* Signal that sine tone started playing and update playback time
348 * and latency at first played frame. */
349 sine_started = 1;
350 cras_client_calc_playback_latency(sample_time,
Earl Ou7ae955b2016-11-30 16:41:22 +0800351 (struct timespec *)arg);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800352 cras_play_time =
Earl Ou7ae955b2016-11-30 16:41:22 +0800353 (struct timeval *)malloc(sizeof(*cras_play_time));
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800354 gettimeofday(cras_play_time, NULL);
Louis Collard921fb152018-02-08 14:56:28 +0800355 if (tty_output) {
356 fwrite(tty_zeros_block, sizeof(char),
357 TTY_OUTPUT_SIZE, tty_output);
358 fflush(tty_output);
359 }
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800360 }
361 }
362
363 playback_count++;
364 return frames;
365}
366
367static int stream_error(struct cras_client *client,
368 cras_stream_id_t stream_id,
369 int err,
370 void *arg)
371{
372 fprintf(stderr, "Stream error %d\n", err);
373 return 0;
374}
375
376/* Adds stream to cras client. */
377static int cras_add_stream(struct cras_client *client,
378 struct cras_stream_params *params,
379 enum CRAS_STREAM_DIRECTION direction,
380 struct timespec *user_data)
381{
382 struct cras_audio_format *aud_format;
383 cras_playback_cb_t aud_cb;
384 cras_error_cb_t error_cb;
Wu-Cheng Liee6aafc2017-11-03 18:37:49 +0800385 size_t cb_threshold = buffer_frames;
386 size_t min_cb_level = buffer_frames;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800387 int rc = 0;
388 cras_stream_id_t stream_id = 0;
389
390 aud_format = cras_audio_format_create(format, rate, channels);
391 if (aud_format == NULL)
392 return -ENOMEM;
393
394 /* Create and start stream */
395 aud_cb = (direction == CRAS_STREAM_OUTPUT)
396 ? cras_play_tone
397 : cras_capture_tone;
398 error_cb = stream_error;
399 params = cras_client_stream_params_create(direction,
400 buffer_frames,
401 cb_threshold,
402 min_cb_level,
403 0,
404 0,
405 user_data,
406 aud_cb,
407 error_cb,
408 aud_format);
409 if (params == NULL)
410 return -ENOMEM;
411
412 rc = cras_client_add_stream(client, &stream_id, params);
413 if (rc < 0) {
414 fprintf(stderr, "Add a stream fail.\n");
415 return rc;
416 }
417 cras_audio_format_destroy(aud_format);
418 return 0;
419}
420
421static void *alsa_play(void *arg) {
422 snd_pcm_t *handle = (snd_pcm_t *)arg;
423 short *play_buf;
424 snd_pcm_channel_area_t *areas;
425 unsigned int chn, num_buffers;
426 int err;
427
428 play_buf = calloc(buffer_frames * channels, sizeof(play_buf[0]));
429 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
430
431 for (chn = 0; chn < channels; chn++) {
432 areas[chn].addr = play_buf;
433 areas[chn].first = chn * snd_pcm_format_physical_width(format);
434 areas[chn].step = channels * snd_pcm_format_physical_width(format);
435 }
436
437 for (num_buffers = 0; num_buffers < PLAYBACK_SILENT_COUNT; num_buffers++) {
438 if ((err = snd_pcm_writei(handle, play_buf, period_size))
439 != period_size) {
440 fprintf(stderr, "write to audio interface failed (%s)\n",
441 snd_strerror(err));
442 exit(1);
443 }
444 }
445
446 generate_sine(areas, 0, period_size, &phase);
447 snd_pcm_delay(handle, &playback_delay_frames);
448 gettimeofday(&sine_start_tv, NULL);
449
450 num_buffers = 0;
451 int avail_frames;
452
453 /* Play a sine wave and look for it on capture thread.
454 * This will fail for latency > 500mS. */
455 while (!terminate_playback && num_buffers < PLAYBACK_COUNT) {
456 avail_frames = snd_pcm_avail(handle);
457 if (avail_frames >= period_size) {
458 pthread_mutex_lock(&latency_test_mutex);
459 if (!sine_started) {
460 sine_started = 1;
461 pthread_cond_signal(&sine_start);
462 }
463 pthread_mutex_unlock(&latency_test_mutex);
464 if ((err = snd_pcm_writei(handle, play_buf, period_size))
465 != period_size) {
466 fprintf(stderr, "write to audio interface failed (%s)\n",
467 snd_strerror(err));
468 }
469 num_buffers++;
470 }
471 }
472 terminate_playback = 1;
473
474 if (num_buffers == PLAYBACK_COUNT)
475 fprintf(stdout, "Audio not detected.\n");
476
477 free(play_buf);
478 free(areas);
479 return 0;
480}
481
482static void *alsa_capture(void *arg) {
483 int err;
484 short *cap_buf;
485 snd_pcm_t *capture_handle = (snd_pcm_t *)arg;
486 snd_pcm_sframes_t cap_delay_frames;
487 int num_cap, noise_delay_frames;
488
489
490 cap_buf = calloc(buffer_frames * channels, sizeof(cap_buf[0]));
491
492 pthread_mutex_lock(&latency_test_mutex);
493 while (!sine_started) {
494 pthread_cond_wait(&sine_start, &latency_test_mutex);
495 }
496 pthread_mutex_unlock(&latency_test_mutex);
497
498 /* Begin capture. */
499 if ((err = snd_pcm_start(capture_handle)) < 0) {
500 fprintf(stderr, "cannot start audio interface for use (%s)\n",
501 snd_strerror(err));
502 exit(1);
503 }
504
505 while (!terminate_capture) {
506 num_cap = capture_some(capture_handle, cap_buf,
507 buffer_frames, &cap_delay_frames);
508
509 if (num_cap > 0 && (noise_delay_frames = check_for_noise(cap_buf,
510 num_cap, channels)) >= 0) {
511 struct timeval cap_time;
512 unsigned long latency_us;
513 gettimeofday(&cap_time, NULL);
514
515 fprintf(stderr, "Found audio\n");
516 fprintf(stderr, "Played at %llu %llu, %ld delay\n",
517 (unsigned long long)sine_start_tv.tv_sec,
518 (unsigned long long)sine_start_tv.tv_usec,
519 playback_delay_frames);
520 fprintf(stderr, "Capture at %llu %llu, %ld delay sample %d\n",
521 (unsigned long long)cap_time.tv_sec,
522 (unsigned long long)cap_time.tv_usec,
523 cap_delay_frames, noise_delay_frames);
524
525 latency_us = subtract_timevals(&cap_time, &sine_start_tv);
526 fprintf(stdout, "Measured Latency: %lu uS\n", latency_us);
527
528 latency_us = (playback_delay_frames + cap_delay_frames -
529 noise_delay_frames) * 1000000 / rate;
530 fprintf(stdout, "Reported Latency: %lu uS\n", latency_us);
531
532 // Noise captured, terminate both threads.
533 terminate_playback = 1;
534 terminate_capture = 1;
535 } else {
536 // Capture some more buffers after playback thread has terminated.
537 if (terminate_playback && capture_count++ < CAPTURE_MORE_COUNT)
538 terminate_capture = 1;
539 }
540 }
541
542 free(cap_buf);
543 return 0;
544}
545
546void cras_test_latency()
547{
548 int rc;
549 struct cras_client *client = NULL;
550 struct cras_stream_params *playback_params = NULL;
551 struct cras_stream_params *capture_params = NULL;
552
553 struct timespec playback_latency;
554 struct timespec capture_latency;
555
556 rc = cras_client_create(&client);
557 if (rc < 0) {
558 fprintf(stderr, "Create client fail.\n");
559 exit(1);
560 }
561 rc = cras_client_connect(client);
562 if (rc < 0) {
563 fprintf(stderr, "Connect to server fail.\n");
564 cras_client_destroy(client);
565 exit(1);
566 }
567
Louis Collard921fb152018-02-08 14:56:28 +0800568 if (tty_output_dev) {
569 tty_output = fopen(tty_output_dev, "w");
570 if (!tty_output)
571 fprintf(stderr, "Failed to open TTY output device: %s",
572 tty_output_dev);
573 else
574 fprintf(stdout, "Opened %s for UART signal\n",
575 tty_output_dev);
576 }
577
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800578 pthread_mutex_init(&latency_test_mutex, NULL);
579 pthread_cond_init(&sine_start, NULL);
580 pthread_cond_init(&terminate_test, NULL);
581
582 cras_client_run_thread(client);
paulhsia025c2a22019-06-25 19:46:37 +0800583 // Sleep 500ms to skip input cold start time.
584 fprintf(stderr, "Create capture stream and wait for 500ms.\n");
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800585 rc = cras_add_stream(client,
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800586 capture_params,
587 CRAS_STREAM_INPUT,
588 &capture_latency);
589 if (rc < 0) {
590 fprintf(stderr, "Fail to add capture stream.\n");
591 exit(1);
592 }
paulhsia025c2a22019-06-25 19:46:37 +0800593 struct timespec delay = { .tv_sec = 0, .tv_nsec = 500000000 };
594 nanosleep(&delay, NULL);
595
596 fprintf(stderr, "Create playback stream.\n");
Dylan Reidf15c8302018-03-06 19:19:50 -0800597 rc = cras_add_stream(client,
598 playback_params,
599 CRAS_STREAM_OUTPUT,
600 &playback_latency);
601 if (rc < 0) {
602 fprintf(stderr, "Fail to add playback stream.\n");
603 exit(1);
604 }
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800605
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800606again:
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800607 pthread_mutex_lock(&latency_test_mutex);
608 while (!terminate_capture) {
609 pthread_cond_wait(&terminate_test, &latency_test_mutex);
610 }
611 pthread_mutex_unlock(&latency_test_mutex);
612
613 if (cras_cap_time && cras_play_time) {
Louis Collard921fb152018-02-08 14:56:28 +0800614 unsigned long playback_latency_us, capture_latency_us;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800615 unsigned long latency = subtract_timevals(cras_cap_time,
616 cras_play_time);
617 fprintf(stdout, "Measured Latency: %lu uS.\n", latency);
618
Louis Collard921fb152018-02-08 14:56:28 +0800619 playback_latency_us = (playback_latency.tv_sec * 1000000) +
620 (playback_latency.tv_nsec / 1000);
621 capture_latency_us = (capture_latency.tv_sec * 1000000) +
622 (capture_latency.tv_nsec / 1000);
623
624 fprintf(stdout,
625 "Reported Latency: %lu uS.\n"
626 "Reported Output Latency: %lu uS.\n"
627 "Reported Input Latency: %lu uS.\n",
628 playback_latency_us + capture_latency_us,
629 playback_latency_us,
630 capture_latency_us);
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800631 fflush(stdout);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800632 } else {
633 fprintf(stdout, "Audio not detected.\n");
634 }
635
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800636 if (--loop > 0) {
637 if (cras_play_time)
638 free(cras_play_time);
639 if (cras_cap_time)
640 free(cras_cap_time);
641 usleep(50000);
642 cras_play_time = cras_cap_time = NULL;
643 playback_count = 0;
644
645 pthread_mutex_lock(&latency_test_mutex);
646 terminate_capture = 0;
647 sine_started = 0;
648 pthread_mutex_unlock(&latency_test_mutex);
649 goto again;
650 }
651
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800652 /* Destruct things. */
653 cras_client_stop(client);
654 cras_client_stream_params_destroy(playback_params);
655 cras_client_stream_params_destroy(capture_params);
Louis Collard921fb152018-02-08 14:56:28 +0800656 if (tty_output)
657 fclose(tty_output);
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800658 if (cras_play_time)
659 free(cras_play_time);
660 if (cras_cap_time)
661 free(cras_cap_time);
662}
663
Earl Ou7ae955b2016-11-30 16:41:22 +0800664void alsa_test_latency(char *play_dev, char *cap_dev)
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800665{
666 int err;
667 snd_pcm_t *playback_handle;
668 snd_pcm_t *capture_handle;
669
670 pthread_t capture_thread;
671 pthread_t playback_thread;
672
673 if ((err = snd_pcm_open(&playback_handle, play_dev,
674 SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
675 fprintf(stderr, "cannot open audio device %s (%s)\n",
676 play_dev, snd_strerror(err));
677 exit(1);
678 }
679 config_pcm(playback_handle, rate, channels, format, &buffer_frames,
680 &period_size);
681
682 if ((err = snd_pcm_open(&capture_handle, cap_dev,
683 SND_PCM_STREAM_CAPTURE, 0)) < 0) {
684 fprintf(stderr, "cannot open audio device %s (%s)\n",
685 cap_dev, snd_strerror(err));
686 exit(1);
687 }
688 config_pcm(capture_handle, rate, channels, format, &buffer_frames,
689 &period_size);
690
691 pthread_mutex_init(&latency_test_mutex, NULL);
692 pthread_cond_init(&sine_start, NULL);
693
694 pthread_create(&playback_thread, NULL, alsa_play, playback_handle);
695 pthread_create(&capture_thread, NULL, alsa_capture, capture_handle);
696
697 pthread_join(capture_thread, NULL);
698 pthread_join(playback_thread, NULL);
699
700 snd_pcm_close(playback_handle);
701 snd_pcm_close(capture_handle);
702}
703
704int main (int argc, char *argv[])
705{
706 int cras_only = 0;
Wu-Cheng Li25874662017-11-09 17:54:14 +0800707 char *play_dev = NULL;
708 char *cap_dev = NULL;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800709
710 int arg;
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800711 while ((arg = getopt(argc, argv, "b:i:o:n:r:p:ct:l:C")) != -1) {
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800712 switch (arg) {
713 case 'b':
714 buffer_frames = atoi(optarg);
715 break;
716 case 'c':
717 cras_only = 1;
718 break;
719 case 'i':
720 cap_dev = optarg;
721 fprintf(stderr, "Assign cap_dev %s\n", cap_dev);
722 break;
723 case 'n':
724 noise_threshold = atoi(optarg);
725 break;
726 case 'r':
727 rate = atoi(optarg);
728 break;
729 case 'o':
730 play_dev = optarg;
731 fprintf(stderr, "Assign play_dev %s\n", play_dev);
732 break;
733 case 'p':
734 period_size = atoi(optarg);
735 break;
Louis Collard921fb152018-02-08 14:56:28 +0800736 case 't':
737 tty_output_dev = optarg;
738 break;
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800739 case 'l':
740 loop = atoi(optarg);
741 break;
742 case 'C':
743 cold = 1;
744 break;
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800745 default:
746 return 1;
747 }
748 }
749
Tzung-Bi Shih65830a62019-01-24 13:55:34 +0800750 if (loop && cold) {
751 fprintf(stderr, "Cold and loop are exclusive.\n");
752 exit(1);
753 }
754
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800755 if (cras_only)
756 cras_test_latency();
Wu-Cheng Li25874662017-11-09 17:54:14 +0800757 else {
758 if (play_dev == NULL || cap_dev == NULL) {
759 fprintf(stderr, "Input/output devices must be set in Alsa mode.\n");
760 exit(1);
761 }
762 alsa_test_latency(play_dev, cap_dev);
763 }
Hsin-Yu Chao9a2dfb22013-08-06 16:43:56 +0800764 exit(0);
765}