blob: 20888ebffa21b4ee6eeed5c2aa328021d68cb4a4 [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
21#include <stdio.h>
22#include <stdlib.h>
23#include <pthread.h>
24#include <alsa/asoundlib.h>
25#include <sys/time.h>
26#include <math.h>
27
28#include "cras_client.h"
29
30#define CAPTURE_MORE_COUNT 50
31#define PLAYBACK_COUNT 50
32#define PLAYBACK_SILENT_COUNT 50
33#define PLAYBACK_TIMEOUT_COUNT 100
34
35static double phase = M_PI / 2;
36static unsigned rate = 48000;
37static unsigned channels = 2;
38static snd_pcm_uframes_t buffer_frames = 480;
39static snd_pcm_uframes_t period_size = 240;
40static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
41
42static struct timeval *cras_play_time = NULL;
43static struct timeval *cras_cap_time = NULL;
44static int noise_threshold = 0x4000;
45
46static int capture_count;
47static int playback_count;
48static snd_pcm_sframes_t playback_delay_frames;
49static struct timeval sine_start_tv;
50
51// Mutex and the variables to protect.
52static pthread_mutex_t latency_test_mutex;
53static pthread_cond_t terminate_test;
54static pthread_cond_t sine_start;
55static int terminate_playback;
56static int terminate_capture;
57static int sine_started = 0;
58
59static void generate_sine(const snd_pcm_channel_area_t *areas,
60 snd_pcm_uframes_t offset, int count,
61 double *_phase)
62{
63 static double max_phase = 2. * M_PI;
64 double phase = *_phase;
65 double step = max_phase * 1000 / (double)rate;
66 unsigned char *samples[channels];
67 int steps[channels];
68 unsigned int chn;
69 int format_bits = snd_pcm_format_width(format);
70 unsigned int maxval = (1 << (format_bits - 1)) - 1;
71 int bps = format_bits / 8; /* bytes per sample */
72 int phys_bps = snd_pcm_format_physical_width(format) / 8;
73 int big_endian = snd_pcm_format_big_endian(format) == 1;
74 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
75 int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
76 format == SND_PCM_FORMAT_FLOAT_BE);
77
78 /* Verify and prepare the contents of areas */
79 for (chn = 0; chn < channels; chn++) {
80 if ((areas[chn].first % 8) != 0) {
81 fprintf(stderr, "areas[%i].first == %i, aborting...\n", chn,
82 areas[chn].first);
83 exit(EXIT_FAILURE);
84 }
85 if ((areas[chn].step % 16) != 0) {
86 fprintf(stderr, "areas[%i].step == %i, aborting...\n", chn, areas
87 [chn].step);
88 exit(EXIT_FAILURE);
89 }
90 steps[chn] = areas[chn].step / 8;
91 samples[chn] = ((unsigned char *)areas[chn].addr) +
92 (areas[chn].first / 8) + offset * steps[chn];
93 }
94
95 /* Fill the channel areas */
96 while (count-- > 0) {
97 union {
98 float f;
99 int i;
100 } fval;
101 int res, i;
102 if (is_float) {
103 fval.f = sin(phase) * maxval;
104 res = fval.i;
105 } else
106 res = sin(phase) * maxval;
107 if (to_unsigned)
108 res ^= 1U << (format_bits - 1);
109 for (chn = 0; chn < channels; chn++) {
110 /* Generate data in native endian format */
111 if (big_endian) {
112 for (i = 0; i < bps; i++)
113 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
114 } else {
115 for (i = 0; i < bps; i++)
116 *(samples[chn] + i) = (res >> i * 8) & 0xff;
117 }
118 samples[chn] += steps[chn];
119 }
120 phase += step;
121 if (phase >= max_phase)
122 phase -= max_phase;
123 }
124 *_phase = phase;
125}
126
127static void config_pcm(snd_pcm_t *handle,
128 unsigned int rate,
129 unsigned int channels,
130 snd_pcm_format_t format,
131 snd_pcm_uframes_t *buffer_size,
132 snd_pcm_uframes_t *period_size)
133{
134 int err;
135 snd_pcm_hw_params_t *hw_params;
136
137 if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
138 fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",
139 snd_strerror(err));
140 exit(1);
141 }
142
143 if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
144 fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",
145 snd_strerror(err));
146 exit(1);
147 }
148
149 if ((err = snd_pcm_hw_params_set_access(handle, hw_params,
150 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
151 fprintf(stderr, "cannot set access type (%s)\n",
152 snd_strerror(err));
153 exit(1);
154 }
155
156 if ((err = snd_pcm_hw_params_set_format(handle, hw_params,
157 format)) < 0) {
158 fprintf(stderr, "cannot set sample format (%s)\n",
159 snd_strerror(err));
160 exit(1);
161 }
162
163 if ((err = snd_pcm_hw_params_set_rate_near(
164 handle, hw_params, &rate, 0)) < 0) {
165 fprintf(stderr, "cannot set sample rate (%s)\n",
166 snd_strerror(err));
167 exit(1);
168 }
169
170 if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, 2)) < 0) {
171 fprintf(stderr, "cannot set channel count (%s)\n",
172 snd_strerror(err));
173 exit(1);
174 }
175
176 if ((err = snd_pcm_hw_params_set_buffer_size_near(
177 handle, hw_params, buffer_size)) < 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_period_size_near(
184 handle, hw_params, period_size, 0)) < 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(handle, hw_params)) < 0) {
191 fprintf(stderr, "cannot set parameters (%s)\n",
192 snd_strerror(err));
193 exit(1);
194 }
195
196 snd_pcm_hw_params_free(hw_params);
197
198 if ((err = snd_pcm_prepare(handle)) < 0) {
199 fprintf(stderr, "cannot prepare audio interface for use (%s)\n",
200 snd_strerror(err));
201 exit(1);
202 }
203}
204
205static int capture_some(snd_pcm_t *pcm, short *buf, unsigned len,
206 snd_pcm_sframes_t *cap_delay_frames)
207{
208 snd_pcm_sframes_t frames = snd_pcm_avail(pcm);
209 int err;
210
211 if (frames > 0) {
212 frames = frames > len ? len : frames;
213
214 snd_pcm_delay(pcm, cap_delay_frames);
215 if ((err = snd_pcm_readi(pcm, buf, frames)) != frames) {
216 fprintf(stderr, "read from audio interface failed (%s)\n",
217 snd_strerror(err));
218 exit(1);
219 }
220 }
221
222 return (int)frames;
223}
224
225/* Looks for the first sample in buffer whose absolute value exceeds
226 * noise_threshold. Returns the index of found sample in frames, -1
227 * if not found. */
228static int check_for_noise(short *buf, unsigned len, unsigned channels)
229{
230 unsigned int i;
231 for (i = 0; i < len * channels; i++)
232 if (abs(buf[i]) > noise_threshold)
233 return i / channels;
234 return -1;
235}
236
237static unsigned long subtract_timevals(const struct timeval *end,
238 const struct timeval *beg)
239{
240 struct timeval diff;
241 /* If end is before geb, return 0. */
242 if ((end->tv_sec < beg->tv_sec) ||
243 ((end->tv_sec == beg->tv_sec) && (end->tv_usec <= beg->tv_usec)))
244 diff.tv_sec = diff.tv_usec = 0;
245 else {
246 if (end->tv_usec < beg->tv_usec) {
247 diff.tv_sec = end->tv_sec - beg->tv_sec - 1;
248 diff.tv_usec =
249 end->tv_usec + 1000000L - beg->tv_usec;
250 } else {
251 diff.tv_sec = end->tv_sec - beg->tv_sec;
252 diff.tv_usec = end->tv_usec - beg->tv_usec;
253 }
254 }
255 return diff.tv_sec * 1000000 + diff.tv_usec;
256}
257
258static int cras_capture_tone(struct cras_client *client,
259 cras_stream_id_t stream_id,
260 uint8_t *samples, size_t frames,
261 const struct timespec *sample_time,
262 void *arg)
263{
264 assert(snd_pcm_format_physical_width(format) == 16);
265
266 short *data = (short *)samples;
267 int cap_frames_index;
268
269 if (!sine_started || terminate_capture) {
270 return frames;
271 }
272
273 if ((cap_frames_index = check_for_noise(data, frames, channels)) >= 0) {
274 fprintf(stderr, "Got noise\n");
275
276 struct timespec shifted_time = *sample_time;
277 shifted_time.tv_nsec += 1000000000L / rate * cap_frames_index;
278 while (shifted_time.tv_nsec > 1000000000L) {
279 shifted_time.tv_sec++;
280 shifted_time.tv_nsec -= 1000000000L;
281 }
282 cras_client_calc_capture_latency(&shifted_time, (struct timespec*)arg);
283 cras_cap_time = (struct timeval*)malloc(sizeof(*cras_cap_time));
284 gettimeofday(cras_cap_time, NULL);
285
286 // Terminate the test since noise got captured.
287 pthread_mutex_lock(&latency_test_mutex);
288 terminate_capture = 1;
289 pthread_cond_signal(&terminate_test);
290 pthread_mutex_unlock(&latency_test_mutex);
291 }
292
293 return frames;
294}
295
296/* Callback for tone playback. Playback latency will be passed
297 * as arg and updated at the first sine tone right after silent.
298 */
299static int cras_play_tone(struct cras_client *client,
300 cras_stream_id_t stream_id,
301 uint8_t *samples, size_t frames,
302 const struct timespec *sample_time,
303 void *arg)
304{
305 snd_pcm_channel_area_t *areas;
306 int chn;
307 size_t sample_bytes;
308
309 sample_bytes = snd_pcm_format_physical_width(format) / 8;
310
311 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
312 for (chn = 0; chn < channels; chn++) {
313 areas[chn].addr = samples + chn * sample_bytes;
314 areas[chn].first = 0;
315 areas[chn].step = channels *
316 snd_pcm_format_physical_width(format);
317 }
318
319 /* Write zero first when playback_count < PLAYBACK_SILENT_COUNT
320 * or noise got captured. */
321 if (playback_count < PLAYBACK_SILENT_COUNT) {
322 memset(samples, 0, sample_bytes * frames * channels);
323 } else if (playback_count > PLAYBACK_TIMEOUT_COUNT) {
324 // Timeout, terminate test.
325 pthread_mutex_lock(&latency_test_mutex);
326 terminate_capture = 1;
327 pthread_cond_signal(&terminate_test);
328 pthread_mutex_unlock(&latency_test_mutex);
329 } else {
330 generate_sine(areas, 0, frames, &phase);
331
332 if (!sine_started) {
333 /* Signal that sine tone started playing and update playback time
334 * and latency at first played frame. */
335 sine_started = 1;
336 cras_client_calc_playback_latency(sample_time,
337 (struct timespec*)arg);
338 cras_play_time =
339 (struct timeval*)malloc(sizeof(*cras_play_time));
340 gettimeofday(cras_play_time, NULL);
341 }
342 }
343
344 playback_count++;
345 return frames;
346}
347
348static int stream_error(struct cras_client *client,
349 cras_stream_id_t stream_id,
350 int err,
351 void *arg)
352{
353 fprintf(stderr, "Stream error %d\n", err);
354 return 0;
355}
356
357/* Adds stream to cras client. */
358static int cras_add_stream(struct cras_client *client,
359 struct cras_stream_params *params,
360 enum CRAS_STREAM_DIRECTION direction,
361 struct timespec *user_data)
362{
363 struct cras_audio_format *aud_format;
364 cras_playback_cb_t aud_cb;
365 cras_error_cb_t error_cb;
366 size_t cb_threshold = buffer_frames / 10;
367 size_t min_cb_level = buffer_frames / 10;
368 int rc = 0;
369 cras_stream_id_t stream_id = 0;
370
371 aud_format = cras_audio_format_create(format, rate, channels);
372 if (aud_format == NULL)
373 return -ENOMEM;
374
375 /* Create and start stream */
376 aud_cb = (direction == CRAS_STREAM_OUTPUT)
377 ? cras_play_tone
378 : cras_capture_tone;
379 error_cb = stream_error;
380 params = cras_client_stream_params_create(direction,
381 buffer_frames,
382 cb_threshold,
383 min_cb_level,
384 0,
385 0,
386 user_data,
387 aud_cb,
388 error_cb,
389 aud_format);
390 if (params == NULL)
391 return -ENOMEM;
392
393 rc = cras_client_add_stream(client, &stream_id, params);
394 if (rc < 0) {
395 fprintf(stderr, "Add a stream fail.\n");
396 return rc;
397 }
398 cras_audio_format_destroy(aud_format);
399 return 0;
400}
401
402static void *alsa_play(void *arg) {
403 snd_pcm_t *handle = (snd_pcm_t *)arg;
404 short *play_buf;
405 snd_pcm_channel_area_t *areas;
406 unsigned int chn, num_buffers;
407 int err;
408
409 play_buf = calloc(buffer_frames * channels, sizeof(play_buf[0]));
410 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
411
412 for (chn = 0; chn < channels; chn++) {
413 areas[chn].addr = play_buf;
414 areas[chn].first = chn * snd_pcm_format_physical_width(format);
415 areas[chn].step = channels * snd_pcm_format_physical_width(format);
416 }
417
418 for (num_buffers = 0; num_buffers < PLAYBACK_SILENT_COUNT; num_buffers++) {
419 if ((err = snd_pcm_writei(handle, play_buf, period_size))
420 != period_size) {
421 fprintf(stderr, "write to audio interface failed (%s)\n",
422 snd_strerror(err));
423 exit(1);
424 }
425 }
426
427 generate_sine(areas, 0, period_size, &phase);
428 snd_pcm_delay(handle, &playback_delay_frames);
429 gettimeofday(&sine_start_tv, NULL);
430
431 num_buffers = 0;
432 int avail_frames;
433
434 /* Play a sine wave and look for it on capture thread.
435 * This will fail for latency > 500mS. */
436 while (!terminate_playback && num_buffers < PLAYBACK_COUNT) {
437 avail_frames = snd_pcm_avail(handle);
438 if (avail_frames >= period_size) {
439 pthread_mutex_lock(&latency_test_mutex);
440 if (!sine_started) {
441 sine_started = 1;
442 pthread_cond_signal(&sine_start);
443 }
444 pthread_mutex_unlock(&latency_test_mutex);
445 if ((err = snd_pcm_writei(handle, play_buf, period_size))
446 != period_size) {
447 fprintf(stderr, "write to audio interface failed (%s)\n",
448 snd_strerror(err));
449 }
450 num_buffers++;
451 }
452 }
453 terminate_playback = 1;
454
455 if (num_buffers == PLAYBACK_COUNT)
456 fprintf(stdout, "Audio not detected.\n");
457
458 free(play_buf);
459 free(areas);
460 return 0;
461}
462
463static void *alsa_capture(void *arg) {
464 int err;
465 short *cap_buf;
466 snd_pcm_t *capture_handle = (snd_pcm_t *)arg;
467 snd_pcm_sframes_t cap_delay_frames;
468 int num_cap, noise_delay_frames;
469
470
471 cap_buf = calloc(buffer_frames * channels, sizeof(cap_buf[0]));
472
473 pthread_mutex_lock(&latency_test_mutex);
474 while (!sine_started) {
475 pthread_cond_wait(&sine_start, &latency_test_mutex);
476 }
477 pthread_mutex_unlock(&latency_test_mutex);
478
479 /* Begin capture. */
480 if ((err = snd_pcm_start(capture_handle)) < 0) {
481 fprintf(stderr, "cannot start audio interface for use (%s)\n",
482 snd_strerror(err));
483 exit(1);
484 }
485
486 while (!terminate_capture) {
487 num_cap = capture_some(capture_handle, cap_buf,
488 buffer_frames, &cap_delay_frames);
489
490 if (num_cap > 0 && (noise_delay_frames = check_for_noise(cap_buf,
491 num_cap, channels)) >= 0) {
492 struct timeval cap_time;
493 unsigned long latency_us;
494 gettimeofday(&cap_time, NULL);
495
496 fprintf(stderr, "Found audio\n");
497 fprintf(stderr, "Played at %llu %llu, %ld delay\n",
498 (unsigned long long)sine_start_tv.tv_sec,
499 (unsigned long long)sine_start_tv.tv_usec,
500 playback_delay_frames);
501 fprintf(stderr, "Capture at %llu %llu, %ld delay sample %d\n",
502 (unsigned long long)cap_time.tv_sec,
503 (unsigned long long)cap_time.tv_usec,
504 cap_delay_frames, noise_delay_frames);
505
506 latency_us = subtract_timevals(&cap_time, &sine_start_tv);
507 fprintf(stdout, "Measured Latency: %lu uS\n", latency_us);
508
509 latency_us = (playback_delay_frames + cap_delay_frames -
510 noise_delay_frames) * 1000000 / rate;
511 fprintf(stdout, "Reported Latency: %lu uS\n", latency_us);
512
513 // Noise captured, terminate both threads.
514 terminate_playback = 1;
515 terminate_capture = 1;
516 } else {
517 // Capture some more buffers after playback thread has terminated.
518 if (terminate_playback && capture_count++ < CAPTURE_MORE_COUNT)
519 terminate_capture = 1;
520 }
521 }
522
523 free(cap_buf);
524 return 0;
525}
526
527void cras_test_latency()
528{
529 int rc;
530 struct cras_client *client = NULL;
531 struct cras_stream_params *playback_params = NULL;
532 struct cras_stream_params *capture_params = NULL;
533
534 struct timespec playback_latency;
535 struct timespec capture_latency;
536
537 rc = cras_client_create(&client);
538 if (rc < 0) {
539 fprintf(stderr, "Create client fail.\n");
540 exit(1);
541 }
542 rc = cras_client_connect(client);
543 if (rc < 0) {
544 fprintf(stderr, "Connect to server fail.\n");
545 cras_client_destroy(client);
546 exit(1);
547 }
548
549 pthread_mutex_init(&latency_test_mutex, NULL);
550 pthread_cond_init(&sine_start, NULL);
551 pthread_cond_init(&terminate_test, NULL);
552
553 cras_client_run_thread(client);
554 rc = cras_add_stream(client,
555 playback_params,
556 CRAS_STREAM_OUTPUT,
557 &playback_latency);
558 if (rc < 0) {
559 fprintf(stderr, "Fail to add playback stream.\n");
560 exit(1);
561 }
562 rc = cras_add_stream(client,
563 capture_params,
564 CRAS_STREAM_INPUT,
565 &capture_latency);
566 if (rc < 0) {
567 fprintf(stderr, "Fail to add capture stream.\n");
568 exit(1);
569 }
570
571 pthread_mutex_lock(&latency_test_mutex);
572 while (!terminate_capture) {
573 pthread_cond_wait(&terminate_test, &latency_test_mutex);
574 }
575 pthread_mutex_unlock(&latency_test_mutex);
576
577 if (cras_cap_time && cras_play_time) {
578 unsigned long latency = subtract_timevals(cras_cap_time,
579 cras_play_time);
580 fprintf(stdout, "Measured Latency: %lu uS.\n", latency);
581
582 latency = (playback_latency.tv_sec + capture_latency.tv_sec) * 1000000 +
583 (playback_latency.tv_nsec + capture_latency.tv_nsec) / 1000;
584 fprintf(stdout, "Reported Latency: %lu uS.\n", latency);
585 } else {
586 fprintf(stdout, "Audio not detected.\n");
587 }
588
589 /* Destruct things. */
590 cras_client_stop(client);
591 cras_client_stream_params_destroy(playback_params);
592 cras_client_stream_params_destroy(capture_params);
593 if (cras_play_time)
594 free(cras_play_time);
595 if (cras_cap_time)
596 free(cras_cap_time);
597}
598
599void alsa_test_latency(char *play_dev, char* cap_dev)
600{
601 int err;
602 snd_pcm_t *playback_handle;
603 snd_pcm_t *capture_handle;
604
605 pthread_t capture_thread;
606 pthread_t playback_thread;
607
608 if ((err = snd_pcm_open(&playback_handle, play_dev,
609 SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
610 fprintf(stderr, "cannot open audio device %s (%s)\n",
611 play_dev, snd_strerror(err));
612 exit(1);
613 }
614 config_pcm(playback_handle, rate, channels, format, &buffer_frames,
615 &period_size);
616
617 if ((err = snd_pcm_open(&capture_handle, cap_dev,
618 SND_PCM_STREAM_CAPTURE, 0)) < 0) {
619 fprintf(stderr, "cannot open audio device %s (%s)\n",
620 cap_dev, snd_strerror(err));
621 exit(1);
622 }
623 config_pcm(capture_handle, rate, channels, format, &buffer_frames,
624 &period_size);
625
626 pthread_mutex_init(&latency_test_mutex, NULL);
627 pthread_cond_init(&sine_start, NULL);
628
629 pthread_create(&playback_thread, NULL, alsa_play, playback_handle);
630 pthread_create(&capture_thread, NULL, alsa_capture, capture_handle);
631
632 pthread_join(capture_thread, NULL);
633 pthread_join(playback_thread, NULL);
634
635 snd_pcm_close(playback_handle);
636 snd_pcm_close(capture_handle);
637}
638
639int main (int argc, char *argv[])
640{
641 int cras_only = 0;
642 char *play_dev = "default";
643 char *cap_dev = "default";
644
645 int arg;
646 while ((arg = getopt(argc, argv, "b:i:o:n:r:p:c")) != -1) {
647 switch (arg) {
648 case 'b':
649 buffer_frames = atoi(optarg);
650 break;
651 case 'c':
652 cras_only = 1;
653 break;
654 case 'i':
655 cap_dev = optarg;
656 fprintf(stderr, "Assign cap_dev %s\n", cap_dev);
657 break;
658 case 'n':
659 noise_threshold = atoi(optarg);
660 break;
661 case 'r':
662 rate = atoi(optarg);
663 break;
664 case 'o':
665 play_dev = optarg;
666 fprintf(stderr, "Assign play_dev %s\n", play_dev);
667 break;
668 case 'p':
669 period_size = atoi(optarg);
670 break;
671 default:
672 return 1;
673 }
674 }
675
676 if (cras_only)
677 cras_test_latency();
678 else
679 alsa_test_latency(play_dev, cap_dev);
680 exit(0);
681}