blob: 484ad9ba233f5f986ad11d77c82be39a97c4134f [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 Ou7ae955b2016-11-30 16:41:22 +08005#include <getopt.h>
Amy Lin8c768a72016-08-01 15:44:16 +08006#include <stdlib.h>
Earl Ou7ae955b2016-11-30 16:41:22 +08007#include <string.h>
8#include <sys/types.h>
9#include <unistd.h>
Amy Lin8c768a72016-08-01 15:44:16 +080010
Earl Ou7ae955b2016-11-30 16:41:22 +080011#include "include/common.h"
Amy Lin8c768a72016-08-01 15:44:16 +080012#include "include/connector.h"
13#include "include/evaluator.h"
Amy Lin8c768a72016-08-01 15:44:16 +080014#include "include/frame_generator.h"
15
Earl Ou7ae955b2016-11-30 16:41:22 +080016constexpr static const char *short_options = "a:d:n:o:P:f:R:F:r:t:c:C:T:l:hv";
17
18constexpr static const struct option long_options[] = {
19 {"active-speaker-channels", 1, NULL, 'a'},
20 {"allowed-delay", 1, NULL, 'd'},
21 {"fft-size", 1, NULL, 'n'},
22 {"confidence-threshold", 1, NULL, 'o'},
23 {"player-command", 1, NULL, 'P'},
24 {"player-fifo", 1, NULL, 'f'},
25 {"recorder-command", 1, NULL, 'R'},
26 {"recorder-fifo", 1, NULL, 'F'},
27 {"sample-rate", 1, NULL, 'r'},
28 {"sample-format", 1, NULL, 't'},
29 {"num-mic-channels", 1, NULL, 'c'},
30 {"num-speaker-channels", 1, NULL, 'C'},
31 {"test-rounds", 1, NULL, 'T'},
32 {"tone-length", 1, NULL, 'l'},
33
34 // Other helper args.
35 {"help", 0, NULL, 'h'},
36 {"verbose", 0, NULL, 'v'},
37};
38
39// Parse the sample format. The input should be one of the string in
40// SampleFormat:Type.
41SampleFormat ParseSampleFormat(const char *arg) {
42 SampleFormat sample_format;
43 for (int format = SampleFormat::kPcmU8;
44 format != SampleFormat::kPcmInvalid;
45 format++) {
46 sample_format = SampleFormat(SampleFormat::Type(format));
47 if (strcmp(sample_format.to_string(), optarg) == 0) {
48 return sample_format;
49 }
50 }
51 fprintf(stderr, "Unknown sample format %s, using S16 instead.", arg);
52 return SampleFormat(SampleFormat::kPcmS16);
53}
54
55bool ParseOptions(int argc, char *const argv[], AudioFunTestConfig *config) {
56 int opt = 0;
57 int optindex = -1;
58
59 while ((opt = getopt_long(argc, argv, short_options,
60 long_options,
61 &optindex)) != -1) {
62 switch (opt) {
63 case 'a':
64 ParseActiveChannels(optarg, &(config->active_speaker_channels));
65 break;
66 case 'd':
67 config->allowed_delay_sec = atof(optarg);
68 break;
69 case 'n':
70 config->fft_size = atoi(optarg);
71 if ((config->fft_size == 0) ||
72 (config->fft_size & (config->fft_size - 1))) {
73 fprintf(stderr, "FFT size needs to be positive & power of 2\n");
74 return false;
75 }
76 break;
77 case 'o':
78 config->confidence_threshold = atof(optarg);
79 break;
80 case 'P':
81 config->player_command = std::string(optarg);
82 break;
83 case 'f':
84 config->player_fifo = std::string(optarg);
85 break;
86 case 'R':
87 config->recorder_command = std::string(optarg);
88 break;
89 case 'F':
90 config->recorder_fifo = std::string(optarg);
91 break;
92 case 'r':
93 config->sample_rate = atoi(optarg);
94 break;
95 case 't':
96 config->sample_format = ParseSampleFormat(optarg);
97 break;
98 case 'c':
99 config->num_mic_channels = atoi(optarg);
100 break;
101 case 'C':
102 config->num_speaker_channels = atoi(optarg);
103 break;
104 case 'T':
105 config->test_rounds = atoi(optarg);
106 break;
107 case 'l':
108 config->tone_length_sec = atof(optarg);
109 // Avoid overly short tones.
110 if (config->tone_length_sec < 0.01) {
111 fprintf(stderr,
112 "Tone length too short. Must be 0.01s or greater.\n");
113 return false;
114 }
115 break;
116 case 'v':
117 config->verbose = true;
118 break;
119 case 'h':
120 return false;
121 default:
122 fprintf(stderr, "Unknown arguments %c\n", opt);
123 assert(false);
124 }
125 }
126
127 if (config->player_command.empty()) {
128 fprintf(stderr, "player-command is not set.\n");
129 return false;
130 }
131
132 if (config->recorder_command.empty()) {
133 fprintf(stderr, "recorder-command is not set.\n");
134 return false;
135 }
136
137 if (config->active_speaker_channels.empty()) {
138 for (int i = 0; i < config->num_speaker_channels; ++i) {
139 config->active_speaker_channels.insert(i);
140 }
141 }
142 return true;
143}
144
145void PrintUsage(const char *name, FILE *fd = stderr) {
146 AudioFunTestConfig default_config;
147
148 fprintf(fd,
149 "Usage %s -P <player_command> -R <recorder_command> [options]\n",
150 name);
151 fprintf(fd,
152 "\t-a, --active-speaker-channels:\n"
153 "\t\tComma-separated list of speaker channels to play on. "
154 "(def all channels)\n");
155 fprintf(fd,
156 "\t-d, --allowed-delay:\n"
157 "\t\tAllowed latency between player & recorder "
158 "(def %.4f).\n", default_config.allowed_delay_sec);
159 fprintf(fd,
160 "\t-n, --fftsize:\n"
161 "\t\tLonger fftsize has more carriers but longer latency."
162 " Also, fftsize needs to be power of 2"
163 "(def %d)\n", default_config.fft_size);
164 fprintf(fd,
165 "\t-o, --confidence-threshold:\n"
166 "\t\tThreshold of accumulated confidence to pass evaluation "
167 "(def %.4f)\n", default_config.confidence_threshold);
168 fprintf(fd,
169 "\t-P, --player-command:\n"
170 "\t\tThe command used to play sound.\n");
171 fprintf(fd,
172 "\t-f, --player-fifo:\n"
173 "\t\tThe named pipe used to send wave to the player. If not set, "
174 "wave is send to player program via its stdin.\n");
175 fprintf(fd,
176 "\t-R, --recorder-command:\n"
177 "\t\tThe command used to record sound.\n");
178 fprintf(fd,
179 "\t-F, --recorder-fifo:\n"
180 "\t\tThe named pipe used to read recorded wave from the recorder "
181 "program. If not set, wave is read from recorder program via "
182 "its stdout.\n");
183 fprintf(fd,
184 "\t-r, --sample-rate:\n"
185 "\t\tSample rate of generated wave in HZ "
186 "(def %d)\n", default_config.sample_rate);
187 fprintf(fd,
188 "\t-t, --sample-format:\n"
189 "\t\tFormat of recording & playing samples, should be one of u8, "
190 "s16, s24, s32."
191 "(def %s).", default_config.sample_format.to_string());
192 fprintf(fd,
193 "\t-c, --num-mic-channels:\n"
194 "\t\tThe number of microphone channels "
195 "(def %d)\n", default_config.num_mic_channels);
196 fprintf(fd,
197 "\t-C, --num-speaker-channels:\n"
198 "\t\tThe number of speaker channels "
199 "(def %d)\n", default_config.num_speaker_channels);
200 fprintf(fd,
201 "\t-T, --test-rounds:\n"
202 "\t\tNumber of test rounds "
203 "(def %d)\n", default_config.test_rounds);
204 fprintf(fd,
205 "\t-l, --tone-length:\n"
206 "\t\tDecimal value of tone length in secs "
207 "(def %.4f)\n", default_config.tone_length_sec);
208 fprintf(fd,
209 "\t-v, --verbose: Show debugging information.\n");
210 fprintf(fd,
211 "\t-h, --help: Show this page.\n");
212}
213
214void PrintConfig(const AudioFunTestConfig &config, FILE *fd = stdout) {
215 fprintf(fd, "Config values.\n");
216
217 fprintf(fd, "\tSpeaker active channels: ");
218 bool first = true;
219 for (auto it : config.active_speaker_channels) {
220 if (!first)
221 fprintf(fd, ", ");
222 fprintf(fd, "%d", it);
223 first = false;
224 }
225 fprintf(fd, "\n");
226 fprintf(fd, "\tAllowed delay: %.4f(s)\n", config.allowed_delay_sec);
227 fprintf(fd, "\tFFT size: %d\n", config.fft_size);
228 fprintf(fd, "\tConfidence threshold: %.4f\n", config.confidence_threshold);
229 fprintf(fd, "\tPlayer parameter: %s\n", config.player_command.c_str());
230 fprintf(fd, "\tPlayer FIFO name: %s\n", config.player_fifo.c_str());
231 fprintf(fd, "\tRecorder parameter: %s\n", config.recorder_command.c_str());
232 fprintf(fd, "\tRecorder FIFO name: %s\n", config.recorder_fifo.c_str());
233 fprintf(fd, "\tSample format: %s\n", config.sample_format.to_string());
234 fprintf(fd, "\tSample rate: %d\n", config.sample_rate);
235 fprintf(fd,
236 "\tNumber of Microphone channels: %d\n", config.num_mic_channels);
237 fprintf(fd, "\tNumber of Speaker channels: %d\n",
238 config.num_speaker_channels);
239 fprintf(fd, "\tNumber of test rounds: %d\n", config.test_rounds);
240 fprintf(fd, "\tTone length: %.4f(s)\n", config.tone_length_sec);
241
242 if (config.verbose)
243 fprintf(fd, "\t** Verbose **.\n");
244}
Amy Lin8c768a72016-08-01 15:44:16 +0800245
246// Randomly picks an integer from the given range [min, max],
247// including both end points.
248inline int RandomPick(int min, int max) {
249 if (min > max) {
Earl Ou7ae955b2016-11-30 16:41:22 +0800250 fprintf(stderr, "Range error: min > max\n");
Amy Lin8c768a72016-08-01 15:44:16 +0800251 assert(false);
252 }
253
254 static unsigned int seed = time(NULL) + getpid();
255 return (rand_r(&seed) % (max - min + 1)) + min;
256}
257
258// Controls the main process of audiofuntest.
Earl Ou7ae955b2016-11-30 16:41:22 +0800259void ControlLoop(const AudioFunTestConfig &config,
Amy Lin8c768a72016-08-01 15:44:16 +0800260 Evaluator *evaluator,
261 RecordClient *recorder,
262 FrameGenerator *generator,
263 const int min_frequency = 4000,
264 const int max_frequency = 10000) {
265 const double frequency_resolution =
266 static_cast<double>(config.sample_rate) / config.fft_size;
267 const int min_bin = min_frequency / frequency_resolution;
268 const int max_bin = max_frequency / frequency_resolution;
269
Earl Ou7ae955b2016-11-30 16:41:22 +0800270 std::vector<int> passes(config.num_mic_channels);
271 std::vector<bool> single_round_pass(config.num_mic_channels);
Amy Lin8c768a72016-08-01 15:44:16 +0800272
273 for (int round = 1; round <= config.test_rounds; ++round) {
274 std::fill(single_round_pass.begin(), single_round_pass.end(), false);
275 int bin = RandomPick(min_bin, max_bin);
276 double frequency = bin * frequency_resolution;
277
278 // Sets the frequency to be generated.
279 generator->SetFrequency(frequency);
280
Earl Ou733306b2016-11-14 14:26:04 +0800281 evaluator->Evaluate(recorder, bin, &single_round_pass);
Amy Lin8c768a72016-08-01 15:44:16 +0800282 for (int chn = 0; chn < config.num_mic_channels; ++chn) {
283 if (single_round_pass[chn]) {
284 ++passes[chn];
285 }
286 }
287 generator->SetStopPlayTone();
288
Earl Ou7ae955b2016-11-30 16:41:22 +0800289 printf("carrier = %d\n", bin);
290 for (int c = 0; c < config.num_mic_channels; ++c) {
Earl Ou733306b2016-11-14 14:26:04 +0800291 const char *res = single_round_pass[c] ? "O" : "X";
Earl Ou7ae955b2016-11-30 16:41:22 +0800292 printf("%s: channel = %d, success = %d, fail = %d, rate = %.4f\n",
293 res, c, passes[c], round - passes[c], 100.0 * passes[c] / round);
Amy Lin8c768a72016-08-01 15:44:16 +0800294 }
Amy Lin8c768a72016-08-01 15:44:16 +0800295 }
296}
297
298int main(int argc, char *argv[]) {
Earl Ou7ae955b2016-11-30 16:41:22 +0800299 // Parses configuration.ParamConfig
300 AudioFunTestConfig config;
301 if (!ParseOptions(argc, argv, &config)) {
302 PrintUsage(argv[0]);
303 return 1;
Amy Lin8c768a72016-08-01 15:44:16 +0800304 }
Earl Ou7ae955b2016-11-30 16:41:22 +0800305
306 PrintConfig(config);
Amy Lin8c768a72016-08-01 15:44:16 +0800307
308 // Main role initialization.
309 FrameGenerator generator(config);
310
311 PlayClient player(config);
Earl Ou7ae955b2016-11-30 16:41:22 +0800312 player.InitProcess();
Amy Lin8c768a72016-08-01 15:44:16 +0800313
314 RecordClient recorder(config);
Earl Ou7ae955b2016-11-30 16:41:22 +0800315 recorder.InitProcess();
Amy Lin8c768a72016-08-01 15:44:16 +0800316
317 Evaluator evaluator(config);
318
319 generator.PlayTo(&player);
320
321 // Starts evaluation.
322 ControlLoop(config, &evaluator, &recorder, &generator);
323
324 // Terminates and cleans up.
325 recorder.Terminate();
326
327 // Stops generator before player to
328 // avoid sending tones to closed pipe.
329 generator.Stop();
330 player.Terminate();
331
332 return 0;
333}