blob: b7116fc4501da2f2923c585e026e3e137cc45c54 [file] [log] [blame]
Earl Ouedb71582016-11-29 15:59:57 +08001/*
2 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <stdio.h>
8#include <signal.h>
9#include <pthread.h>
10#include <time.h>
11#include <unistd.h>
12
Earl Ou7ae955b2016-11-30 16:41:22 +080013#include "include/libaudiodev.h"
Earl Ouedb71582016-11-29 15:59:57 +080014
15typedef struct {
16 unsigned char *data;
17} audio_buffer;
18
19static int verbose = 0;
20
21static int buffer_count; // Total number of buffer
22static pthread_mutex_t buf_mutex; // This protects the variables below
23audio_buffer *buffers;
24static int write_index; // buffer should be written next
25static int read_index; // buffer should be read next
26static int write_available; // number of buffers can be write
27static int read_available; // number of buffers can be read
28static pthread_cond_t has_data;
29static struct timespec cap_start_time, play_start_time;
30static int total_cap_frames, total_play_frames;
31
32/* Termination variable. */
33static int terminate;
34
35static void set_current_time(struct timespec *ts) {
36 clock_gettime(CLOCK_MONOTONIC, ts);
37}
38
39// Returns the time since the given time in nanoseconds.
40static long long since(struct timespec *ts) {
41 struct timespec now;
42 clock_gettime(CLOCK_MONOTONIC, &now);
43 long long t = now.tv_sec - ts->tv_sec;
44 t *= 1000000000;
45 t += (now.tv_nsec - ts->tv_nsec);
46 return t;
47}
48
49static void update_stat() {
50 if (verbose) {
51 double cap_rate = total_cap_frames * 1e9 / since(&cap_start_time);
52 double play_rate = total_play_frames * 1e9 / since(&play_start_time);
53 printf("Buffer: %d/%d, Capture: %d, Play: %d \r",
54 read_available, buffer_count,
55 (int) cap_rate, (int) play_rate);
56 }
57}
58
59static void *play_loop(void *arg) {
60 audio_device_t *device = (audio_device_t *)arg;
61 int buf_play;
62
63 pthread_mutex_lock(&buf_mutex);
64 // Wait until half of the buffers are filled.
65 while (!terminate && read_available < buffer_count / 2) {
66 pthread_cond_wait(&has_data, &buf_mutex);
67 }
68
69 // Now start playing
70 set_current_time(&play_start_time);
71 total_play_frames = 0;
72 while (!terminate) {
73 while (read_available == 0) {
74 pthread_cond_wait(&has_data, &buf_mutex);
75 }
76 buf_play = read_index;
77 read_index = (read_index + 1) % buffer_count;
78 read_available--;
79
80 pthread_mutex_unlock(&buf_mutex);
81 pcm_io(device, buffers[buf_play].data, chunk_size);
82 pthread_mutex_lock(&buf_mutex);
83
84 total_play_frames += chunk_size;
85 write_available++;
86 update_stat();
87 }
88 pthread_mutex_unlock(&buf_mutex);
89
90 return NULL;
91}
92
93static void *cap_loop(void *arg) {
94 audio_device_t *device = (audio_device_t *)arg;
95 int buf_cap;
96
97 pthread_mutex_lock(&buf_mutex);
98 total_cap_frames = 0;
99 set_current_time(&cap_start_time);
100 while (!terminate) {
101 // If we have no more buffer to write, drop the oldest one
102 if (write_available == 0) {
103 read_index = (read_index + 1) % buffer_count;
104 read_available--;
105 } else {
106 write_available--;
107 }
108 buf_cap = write_index;
109 write_index = (write_index + 1) % buffer_count;
110
111 pthread_mutex_unlock(&buf_mutex);
112 pcm_io(device, buffers[buf_cap].data, chunk_size);
113 pthread_mutex_lock(&buf_mutex);
114
115 total_cap_frames += chunk_size;
116 read_available++;
117 pthread_cond_signal(&has_data);
118 update_stat();
119 }
120 pthread_mutex_unlock(&buf_mutex);
121
122 return NULL;
123}
124
125static void signal_handler(int signal) {
126 printf("Signal Caught.\n");
127
128 terminate = 1;
129}
130
131static void dump_line(FILE *fp) {
132 int ch;
133 while ((ch = fgetc(fp)) != EOF && ch != '\n') {}
134}
135
136static void get_choice(char *direction_name, audio_device_info_list_t *list,
137 int *choice) {
138 int i;
139 while (1) {
140 printf("%s devices:\n", direction_name);
141 if (list->count == 0) {
142 printf("No devices :(\n");
143 exit(EXIT_FAILURE);
144 }
145
146 for (i = 0; i < list->count; i++) {
147 printf("(%d)\nCard %d: %s, %s\n Device %d: %s [%s], %s", i + 1,
148 list->devs[i].card, list->devs[i].dev_id,
149 list->devs[i].dev_name, list->devs[i].dev_no,
150 list->devs[i].pcm_id, list->devs[i].pcm_name,
151 list->devs[i].audio_device.hwdevname);
152 printf("\n");
153 }
154 printf("\nChoose one(1 - %d): ", list->count);
155
156 if (scanf("%d", choice) == 0) {
157 dump_line(stdin);
158 printf("\nThat was an invalid choice.\n");
159 } else if (*choice > 0 && *choice <= list->count) {
160 break;
161 } else {
162 printf("\nThat was an invalid choice.\n");
163 }
164 }
165}
166
167static void init_buffers(int size) {
168 int i;
169 buffers = (audio_buffer *)malloc(buffer_count * sizeof(audio_buffer));
170 if (!buffers) {
171 fprintf(stderr, "Error: Could not create audio buffer array.\n");
172 exit(EXIT_FAILURE);
173 }
174 pthread_mutex_init(&buf_mutex, NULL);
175 pthread_cond_init(&has_data, NULL);
176 for (i = 0; i < buffer_count; i++) {
177 buffers[i].data = (unsigned char *)malloc(size);
178 if (!buffers[i].data) {
179 fprintf(stderr, "Error: Could not create audio buffers.\n");
180 exit(EXIT_FAILURE);
181 }
182 }
183 read_index = write_index = 0;
184 read_available = 0;
185 write_available = buffer_count;
186}
187
188void test(int buffer_size, unsigned int ct, char *pdev_name, char *cdev_name) {
189 pthread_t capture_thread;
190 pthread_t playback_thread;
191 buffer_count = ct;
192
193 audio_device_info_list_t *playback_list = NULL;
194 audio_device_info_list_t *capture_list = NULL;
195
196 // Actual playback and capture devices we use to loop. Their
197 // pcm handle will be closed in close_sound_handle.
198 audio_device_t playback_device;
199 audio_device_t capture_device;
200
201 if (pdev_name) {
202 playback_device.direction = SND_PCM_STREAM_PLAYBACK;
203 playback_device.handle = NULL;
204 strcpy(playback_device.hwdevname, pdev_name);
205 } else {
206 playback_list = get_device_list(SND_PCM_STREAM_PLAYBACK);
207 int pdev;
208 get_choice("playback", playback_list, &pdev);
209 playback_device = playback_list->devs[pdev - 1].audio_device;
210 }
211
212 if (cdev_name) {
213 capture_device.direction = SND_PCM_STREAM_CAPTURE;
214 capture_device.handle = NULL;
215 strcpy(capture_device.hwdevname, cdev_name);
216 } else {
217 capture_list = get_device_list(SND_PCM_STREAM_CAPTURE);
218 int cdev;
219 get_choice("capture", capture_list, &cdev);
220 capture_device = capture_list->devs[cdev - 1].audio_device;
221 }
222
223 init_buffers(buffer_size);
224 terminate = 0;
225
226 signal(SIGINT, signal_handler);
227 signal(SIGTERM, signal_handler);
228 signal(SIGABRT, signal_handler);
229
230 if (create_sound_handle(&playback_device, buffer_size) ||
231 create_sound_handle(&capture_device, buffer_size))
232 exit(EXIT_FAILURE);
233
234 pthread_create(&playback_thread, NULL, play_loop, &playback_device);
235 pthread_create(&capture_thread, NULL, cap_loop, &capture_device);
236
237 pthread_join(capture_thread, NULL);
238 pthread_join(playback_thread, NULL);
239
240 close_sound_handle(&playback_device);
241 close_sound_handle(&capture_device);
242
243 if (playback_list)
244 free_device_list(playback_list);
245 if (capture_list)
246 free_device_list(capture_list);
247
248 printf("Exiting.\n");
249}
250
251int main(int argc, char **argv) {
252 char *play_dev = NULL;
253 char *cap_dev = NULL;
254 int count = 100;
255 int size = 1024;
256 int arg;
257
258 while ((arg = getopt(argc, argv, "i:o:c:s:v")) != -1) {
259 switch(arg) {
260 case 'i':
261 cap_dev = optarg;
262 break;
263 case 'o':
264 play_dev = optarg;
265 break;
266 case 'c':
267 count = atoi(optarg);
268 break;
269 case 's':
270 size = atoi(optarg);
271 break;
272 case 'v':
273 verbose = 1;
274 break;
275 case '?':
276 if (optopt == 'i' || optopt == 'o' || optopt == 'c' || optopt == 's') {
277 fprintf(stderr, "Option -%c requires an argument.\n", optopt);
278 } else {
279 fprintf(stderr, "Unknown Option -%c.\n", optopt);
280 }
281 default:
282 return 1;
283 }
284 }
285
286 test(size, count, play_dev, cap_dev);
287 return 0;
288}