blob: 916b6aefc291a744776ed213b7a39492ca107df8 [file] [log] [blame]
Amy Lin8c768a72016-08-01 15:44:16 +08001// Copyright 2016 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
Earl Ou06c1ad62016-12-01 17:21:50 +08005#include <assert.h>
Earl Ou7ae955b2016-11-30 16:41:22 +08006#include <getopt.h>
Amy Lin8c768a72016-08-01 15:44:16 +08007#include <stdlib.h>
Earl Ou7ae955b2016-11-30 16:41:22 +08008#include <string.h>
9#include <sys/types.h>
10#include <unistd.h>
Amy Lin8c768a72016-08-01 15:44:16 +080011
Earl Ou5afc8d32016-12-01 11:30:52 +080012#include "include/binary_client.h"
Earl Ou7ae955b2016-11-30 16:41:22 +080013#include "include/common.h"
Amy Lin8c768a72016-08-01 15:44:16 +080014#include "include/evaluator.h"
Earl Ou06c1ad62016-12-01 17:21:50 +080015#include "include/generator_player.h"
16#include "include/sample_format.h"
17#include "include/tone_generators.h"
Amy Lin8c768a72016-08-01 15:44:16 +080018
Earl Ou06c1ad62016-12-01 17:21:50 +080019constexpr static const char *short_options =
20 "a:d:n:o:w:P:f:R:F:r:t:c:C:T:l:hv";
Earl Ou7ae955b2016-11-30 16:41:22 +080021
22constexpr static const struct option long_options[] = {
23 {"active-speaker-channels", 1, NULL, 'a'},
24 {"allowed-delay", 1, NULL, 'd'},
25 {"fft-size", 1, NULL, 'n'},
26 {"confidence-threshold", 1, NULL, 'o'},
Earl Ou06c1ad62016-12-01 17:21:50 +080027 {"match-window-size", 1, NULL, 'w'},
Earl Ou7ae955b2016-11-30 16:41:22 +080028 {"player-command", 1, NULL, 'P'},
29 {"player-fifo", 1, NULL, 'f'},
30 {"recorder-command", 1, NULL, 'R'},
31 {"recorder-fifo", 1, NULL, 'F'},
32 {"sample-rate", 1, NULL, 'r'},
33 {"sample-format", 1, NULL, 't'},
34 {"num-mic-channels", 1, NULL, 'c'},
35 {"num-speaker-channels", 1, NULL, 'C'},
36 {"test-rounds", 1, NULL, 'T'},
37 {"tone-length", 1, NULL, 'l'},
38
39 // Other helper args.
40 {"help", 0, NULL, 'h'},
41 {"verbose", 0, NULL, 'v'},
42};
43
44// Parse the sample format. The input should be one of the string in
45// SampleFormat:Type.
46SampleFormat ParseSampleFormat(const char *arg) {
47 SampleFormat sample_format;
48 for (int format = SampleFormat::kPcmU8;
49 format != SampleFormat::kPcmInvalid;
50 format++) {
51 sample_format = SampleFormat(SampleFormat::Type(format));
52 if (strcmp(sample_format.to_string(), optarg) == 0) {
53 return sample_format;
54 }
55 }
56 fprintf(stderr, "Unknown sample format %s, using S16 instead.", arg);
57 return SampleFormat(SampleFormat::kPcmS16);
58}
59
60bool ParseOptions(int argc, char *const argv[], AudioFunTestConfig *config) {
61 int opt = 0;
62 int optindex = -1;
63
64 while ((opt = getopt_long(argc, argv, short_options,
65 long_options,
66 &optindex)) != -1) {
67 switch (opt) {
68 case 'a':
69 ParseActiveChannels(optarg, &(config->active_speaker_channels));
70 break;
71 case 'd':
72 config->allowed_delay_sec = atof(optarg);
73 break;
74 case 'n':
75 config->fft_size = atoi(optarg);
76 if ((config->fft_size == 0) ||
77 (config->fft_size & (config->fft_size - 1))) {
78 fprintf(stderr, "FFT size needs to be positive & power of 2\n");
79 return false;
80 }
81 break;
82 case 'o':
83 config->confidence_threshold = atof(optarg);
84 break;
Earl Ou06c1ad62016-12-01 17:21:50 +080085 case 'w':
86 config->match_window_size = atoi(optarg);
87 if (config->match_window_size % 2 == 0) {
88 fprintf(stderr, "Match window size must be an odd value.\n");
89 return false;
90 }
91 break;
Earl Ou7ae955b2016-11-30 16:41:22 +080092 case 'P':
93 config->player_command = std::string(optarg);
94 break;
95 case 'f':
96 config->player_fifo = std::string(optarg);
97 break;
98 case 'R':
99 config->recorder_command = std::string(optarg);
100 break;
101 case 'F':
102 config->recorder_fifo = std::string(optarg);
103 break;
104 case 'r':
105 config->sample_rate = atoi(optarg);
106 break;
107 case 't':
108 config->sample_format = ParseSampleFormat(optarg);
109 break;
110 case 'c':
111 config->num_mic_channels = atoi(optarg);
112 break;
113 case 'C':
114 config->num_speaker_channels = atoi(optarg);
115 break;
116 case 'T':
117 config->test_rounds = atoi(optarg);
118 break;
119 case 'l':
120 config->tone_length_sec = atof(optarg);
121 // Avoid overly short tones.
122 if (config->tone_length_sec < 0.01) {
123 fprintf(stderr,
124 "Tone length too short. Must be 0.01s or greater.\n");
125 return false;
126 }
127 break;
128 case 'v':
129 config->verbose = true;
130 break;
131 case 'h':
132 return false;
133 default:
134 fprintf(stderr, "Unknown arguments %c\n", opt);
135 assert(false);
136 }
137 }
138
139 if (config->player_command.empty()) {
140 fprintf(stderr, "player-command is not set.\n");
141 return false;
142 }
143
144 if (config->recorder_command.empty()) {
145 fprintf(stderr, "recorder-command is not set.\n");
146 return false;
147 }
148
149 if (config->active_speaker_channels.empty()) {
150 for (int i = 0; i < config->num_speaker_channels; ++i) {
151 config->active_speaker_channels.insert(i);
152 }
153 }
154 return true;
155}
156
157void PrintUsage(const char *name, FILE *fd = stderr) {
158 AudioFunTestConfig default_config;
159
160 fprintf(fd,
161 "Usage %s -P <player_command> -R <recorder_command> [options]\n",
162 name);
163 fprintf(fd,
164 "\t-a, --active-speaker-channels:\n"
165 "\t\tComma-separated list of speaker channels to play on. "
166 "(def all channels)\n");
167 fprintf(fd,
168 "\t-d, --allowed-delay:\n"
169 "\t\tAllowed latency between player & recorder "
170 "(def %.4f).\n", default_config.allowed_delay_sec);
171 fprintf(fd,
172 "\t-n, --fftsize:\n"
173 "\t\tLonger fftsize has more carriers but longer latency."
174 " Also, fftsize needs to be power of 2"
175 "(def %d)\n", default_config.fft_size);
176 fprintf(fd,
177 "\t-o, --confidence-threshold:\n"
178 "\t\tThreshold of accumulated confidence to pass evaluation "
179 "(def %.4f)\n", default_config.confidence_threshold);
180 fprintf(fd,
Earl Ou06c1ad62016-12-01 17:21:50 +0800181 "\t-w, --match-window-size:\n"
182 "\t\tNumber of bin to be used for calculating matching confidence. "
183 "Should be an odd number."
184 "(def %d)\n", default_config.match_window_size);
185 fprintf(fd,
Earl Ou7ae955b2016-11-30 16:41:22 +0800186 "\t-P, --player-command:\n"
187 "\t\tThe command used to play sound.\n");
188 fprintf(fd,
189 "\t-f, --player-fifo:\n"
190 "\t\tThe named pipe used to send wave to the player. If not set, "
191 "wave is send to player program via its stdin.\n");
192 fprintf(fd,
193 "\t-R, --recorder-command:\n"
194 "\t\tThe command used to record sound.\n");
195 fprintf(fd,
196 "\t-F, --recorder-fifo:\n"
197 "\t\tThe named pipe used to read recorded wave from the recorder "
198 "program. If not set, wave is read from recorder program via "
199 "its stdout.\n");
200 fprintf(fd,
201 "\t-r, --sample-rate:\n"
202 "\t\tSample rate of generated wave in HZ "
203 "(def %d)\n", default_config.sample_rate);
204 fprintf(fd,
205 "\t-t, --sample-format:\n"
206 "\t\tFormat of recording & playing samples, should be one of u8, "
207 "s16, s24, s32."
Ting Shen95141ed2017-01-05 18:41:43 +0800208 "(def %s).\n", default_config.sample_format.to_string());
Earl Ou7ae955b2016-11-30 16:41:22 +0800209 fprintf(fd,
210 "\t-c, --num-mic-channels:\n"
211 "\t\tThe number of microphone channels "
212 "(def %d)\n", default_config.num_mic_channels);
213 fprintf(fd,
214 "\t-C, --num-speaker-channels:\n"
215 "\t\tThe number of speaker channels "
216 "(def %d)\n", default_config.num_speaker_channels);
217 fprintf(fd,
218 "\t-T, --test-rounds:\n"
219 "\t\tNumber of test rounds "
220 "(def %d)\n", default_config.test_rounds);
221 fprintf(fd,
222 "\t-l, --tone-length:\n"
223 "\t\tDecimal value of tone length in secs "
224 "(def %.4f)\n", default_config.tone_length_sec);
225 fprintf(fd,
226 "\t-v, --verbose: Show debugging information.\n");
227 fprintf(fd,
228 "\t-h, --help: Show this page.\n");
229}
230
231void PrintConfig(const AudioFunTestConfig &config, FILE *fd = stdout) {
232 fprintf(fd, "Config values.\n");
233
234 fprintf(fd, "\tSpeaker active channels: ");
235 bool first = true;
236 for (auto it : config.active_speaker_channels) {
237 if (!first)
238 fprintf(fd, ", ");
239 fprintf(fd, "%d", it);
240 first = false;
241 }
242 fprintf(fd, "\n");
243 fprintf(fd, "\tAllowed delay: %.4f(s)\n", config.allowed_delay_sec);
244 fprintf(fd, "\tFFT size: %d\n", config.fft_size);
245 fprintf(fd, "\tConfidence threshold: %.4f\n", config.confidence_threshold);
Earl Ou06c1ad62016-12-01 17:21:50 +0800246 fprintf(fd, "\tMatch window size: %d\n", config.match_window_size);
Earl Ou7ae955b2016-11-30 16:41:22 +0800247 fprintf(fd, "\tPlayer parameter: %s\n", config.player_command.c_str());
248 fprintf(fd, "\tPlayer FIFO name: %s\n", config.player_fifo.c_str());
249 fprintf(fd, "\tRecorder parameter: %s\n", config.recorder_command.c_str());
250 fprintf(fd, "\tRecorder FIFO name: %s\n", config.recorder_fifo.c_str());
251 fprintf(fd, "\tSample format: %s\n", config.sample_format.to_string());
252 fprintf(fd, "\tSample rate: %d\n", config.sample_rate);
253 fprintf(fd,
254 "\tNumber of Microphone channels: %d\n", config.num_mic_channels);
255 fprintf(fd, "\tNumber of Speaker channels: %d\n",
256 config.num_speaker_channels);
257 fprintf(fd, "\tNumber of test rounds: %d\n", config.test_rounds);
258 fprintf(fd, "\tTone length: %.4f(s)\n", config.tone_length_sec);
259
260 if (config.verbose)
261 fprintf(fd, "\t** Verbose **.\n");
262}
Amy Lin8c768a72016-08-01 15:44:16 +0800263
264// Randomly picks an integer from the given range [min, max],
265// including both end points.
266inline int RandomPick(int min, int max) {
267 if (min > max) {
Earl Ou7ae955b2016-11-30 16:41:22 +0800268 fprintf(stderr, "Range error: min > max\n");
Amy Lin8c768a72016-08-01 15:44:16 +0800269 assert(false);
270 }
271
272 static unsigned int seed = time(NULL) + getpid();
273 return (rand_r(&seed) % (max - min + 1)) + min;
274}
275
276// Controls the main process of audiofuntest.
Earl Ou7ae955b2016-11-30 16:41:22 +0800277void ControlLoop(const AudioFunTestConfig &config,
Amy Lin8c768a72016-08-01 15:44:16 +0800278 Evaluator *evaluator,
Earl Ou06c1ad62016-12-01 17:21:50 +0800279 PlayClient *player,
Amy Lin8c768a72016-08-01 15:44:16 +0800280 RecordClient *recorder,
Amy Lin8c768a72016-08-01 15:44:16 +0800281 const int min_frequency = 4000,
282 const int max_frequency = 10000) {
283 const double frequency_resolution =
284 static_cast<double>(config.sample_rate) / config.fft_size;
285 const int min_bin = min_frequency / frequency_resolution;
286 const int max_bin = max_frequency / frequency_resolution;
287
Earl Ou7ae955b2016-11-30 16:41:22 +0800288 std::vector<int> passes(config.num_mic_channels);
289 std::vector<bool> single_round_pass(config.num_mic_channels);
Amy Lin8c768a72016-08-01 15:44:16 +0800290
Earl Ou06c1ad62016-12-01 17:21:50 +0800291 size_t buf_size = config.fft_size * config.num_speaker_channels *
292 config.sample_format.bytes();
293 SineWaveGenerator generator(config.sample_rate, config.tone_length_sec);
294 GeneratorPlayer generatorPlayer(
295 buf_size,
296 config.num_speaker_channels,
297 config.active_speaker_channels,
298 config.sample_format,
299 player);
300
Amy Lin8c768a72016-08-01 15:44:16 +0800301 for (int round = 1; round <= config.test_rounds; ++round) {
302 std::fill(single_round_pass.begin(), single_round_pass.end(), false);
303 int bin = RandomPick(min_bin, max_bin);
304 double frequency = bin * frequency_resolution;
305
Earl Ou06c1ad62016-12-01 17:21:50 +0800306 generator.Reset(frequency);
307 generatorPlayer.Play(&generator);
Amy Lin8c768a72016-08-01 15:44:16 +0800308
Earl Ou06c1ad62016-12-01 17:21:50 +0800309 evaluator->Evaluate(bin, recorder, &single_round_pass);
Amy Lin8c768a72016-08-01 15:44:16 +0800310 for (int chn = 0; chn < config.num_mic_channels; ++chn) {
311 if (single_round_pass[chn]) {
312 ++passes[chn];
313 }
314 }
Earl Ou06c1ad62016-12-01 17:21:50 +0800315 generatorPlayer.Stop();
Amy Lin8c768a72016-08-01 15:44:16 +0800316
Earl Ou7ae955b2016-11-30 16:41:22 +0800317 printf("carrier = %d\n", bin);
318 for (int c = 0; c < config.num_mic_channels; ++c) {
Earl Ou733306b2016-11-14 14:26:04 +0800319 const char *res = single_round_pass[c] ? "O" : "X";
Earl Ou7ae955b2016-11-30 16:41:22 +0800320 printf("%s: channel = %d, success = %d, fail = %d, rate = %.4f\n",
321 res, c, passes[c], round - passes[c], 100.0 * passes[c] / round);
Amy Lin8c768a72016-08-01 15:44:16 +0800322 }
Amy Lin8c768a72016-08-01 15:44:16 +0800323 }
324}
325
326int main(int argc, char *argv[]) {
Earl Ou7ae955b2016-11-30 16:41:22 +0800327 // Parses configuration.ParamConfig
328 AudioFunTestConfig config;
329 if (!ParseOptions(argc, argv, &config)) {
330 PrintUsage(argv[0]);
331 return 1;
Amy Lin8c768a72016-08-01 15:44:16 +0800332 }
Earl Ou7ae955b2016-11-30 16:41:22 +0800333
334 PrintConfig(config);
Amy Lin8c768a72016-08-01 15:44:16 +0800335
Amy Lin8c768a72016-08-01 15:44:16 +0800336 PlayClient player(config);
Earl Ou5afc8d32016-12-01 11:30:52 +0800337 player.Start();
Amy Lin8c768a72016-08-01 15:44:16 +0800338
339 RecordClient recorder(config);
Earl Ou5afc8d32016-12-01 11:30:52 +0800340 recorder.Start();
Amy Lin8c768a72016-08-01 15:44:16 +0800341
342 Evaluator evaluator(config);
343
Amy Lin8c768a72016-08-01 15:44:16 +0800344 // Starts evaluation.
Earl Ou06c1ad62016-12-01 17:21:50 +0800345 ControlLoop(config, &evaluator, &player, &recorder);
Amy Lin8c768a72016-08-01 15:44:16 +0800346
347 // Terminates and cleans up.
348 recorder.Terminate();
Amy Lin8c768a72016-08-01 15:44:16 +0800349 player.Terminate();
350
351 return 0;
352}