1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <getopt.h>
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <sys/stat.h>
21 #include <vector>
22
23 #include <audio_effects/effect_aec.h>
24 #include <audio_effects/effect_agc.h>
25 #include <audio_effects/effect_agc2.h>
26 #include <audio_effects/effect_ns.h>
27 #include <audio_utils/channels.h>
28 #include <log/log.h>
29
30 // This is the only symbol that needs to be imported
31 extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
32
33 //------------------------------------------------------------------------------
34 // local definitions
35 //------------------------------------------------------------------------------
36
37 // types of pre processing modules
38 enum PreProcId {
39 PREPROC_AGC, // Automatic Gain Control
40 PREPROC_AGC2, // Automatic Gain Control 2
41 PREPROC_AEC, // Acoustic Echo Canceler
42 PREPROC_NS, // Noise Suppressor
43 PREPROC_NUM_EFFECTS
44 };
45
46 enum PreProcParams {
47 ARG_HELP = 1,
48 ARG_INPUT,
49 ARG_OUTPUT,
50 ARG_FAR,
51 ARG_FS,
52 ARG_CH_MASK,
53 ARG_AGC_TGT_LVL,
54 ARG_AGC_COMP_LVL,
55 ARG_AEC_DELAY,
56 ARG_NS_LVL,
57 ARG_AGC2_GAIN,
58 ARG_AGC2_LVL,
59 ARG_AGC2_SAT_MGN,
60 ARG_FILE_CHANNELS,
61 ARG_MONO_MODE
62 };
63
64 struct preProcConfigParams_t {
65 int samplingFreq = 16000;
66 audio_channel_mask_t chMask = AUDIO_CHANNEL_IN_MONO;
67 int nsLevel = 0; // a value between 0-3
68 int agcTargetLevel = 3; // in dB
69 int agcCompLevel = 9; // in dB
70 float agc2Gain = 0.f; // in dB
71 float agc2SaturationMargin = 2.f; // in dB
72 int agc2Level = 0; // either kRms(0) or kPeak(1)
73 int aecDelay = 0; // in ms
74 int fileChannels = 1;
75 int monoMode = 0;
76 };
77
78 const effect_uuid_t kPreProcUuids[PREPROC_NUM_EFFECTS] = {
79 {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // agc uuid
80 {0x89f38e65, 0xd4d2, 0x4d64, 0xad0e, {0x2b, 0x3e, 0x79, 0x9e, 0xa8, 0x86}}, // agc2 uuid
81 {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // aec uuid
82 {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // ns uuid
83 };
84
85 constexpr audio_channel_mask_t kPreProcConfigChMask[] = {
86 AUDIO_CHANNEL_IN_MONO,
87 AUDIO_CHANNEL_IN_STEREO,
88 AUDIO_CHANNEL_IN_FRONT_BACK,
89 AUDIO_CHANNEL_IN_6,
90 AUDIO_CHANNEL_IN_2POINT0POINT2,
91 AUDIO_CHANNEL_IN_2POINT1POINT2,
92 AUDIO_CHANNEL_IN_3POINT0POINT2,
93 AUDIO_CHANNEL_IN_3POINT1POINT2,
94 AUDIO_CHANNEL_IN_5POINT1,
95 AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO,
96 AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO,
97 AUDIO_CHANNEL_IN_VOICE_CALL_MONO,
98 };
99
100 constexpr int kPreProcConfigChMaskCount = std::size(kPreProcConfigChMask);
101
printUsage()102 void printUsage() {
103 printf("\nUsage: ");
104 printf("\n <executable> [options]\n");
105 printf("\nwhere options are, ");
106 printf("\n --input <inputfile>");
107 printf("\n path to the input file");
108 printf("\n --output <outputfile>");
109 printf("\n path to the output file");
110 printf("\n --help");
111 printf("\n Prints this usage information");
112 printf("\n --fs <sampling_freq>");
113 printf("\n Sampling frequency in Hz, default 16000.");
114 printf("\n --ch_mask <channel_mask>\n");
115 printf("\n 0 - AUDIO_CHANNEL_IN_MONO");
116 printf("\n 1 - AUDIO_CHANNEL_IN_STEREO");
117 printf("\n 2 - AUDIO_CHANNEL_IN_FRONT_BACK");
118 printf("\n 3 - AUDIO_CHANNEL_IN_6");
119 printf("\n 4 - AUDIO_CHANNEL_IN_2POINT0POINT2");
120 printf("\n 5 - AUDIO_CHANNEL_IN_2POINT1POINT2");
121 printf("\n 6 - AUDIO_CHANNEL_IN_3POINT0POINT2");
122 printf("\n 7 - AUDIO_CHANNEL_IN_3POINT1POINT2");
123 printf("\n 8 - AUDIO_CHANNEL_IN_5POINT1");
124 printf("\n 9 - AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO");
125 printf("\n 10 - AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO ");
126 printf("\n 11 - AUDIO_CHANNEL_IN_VOICE_CALL_MONO ");
127 printf("\n default 0");
128 printf("\n --far <farend_file>");
129 printf("\n Path to far-end file needed for echo cancellation");
130 printf("\n --aec");
131 printf("\n Enable Echo Cancellation, default disabled");
132 printf("\n --ns");
133 printf("\n Enable Noise Suppression, default disabled");
134 printf("\n --agc");
135 printf("\n Enable Gain Control, default disabled");
136 printf("\n --agc2");
137 printf("\n Enable Gain Controller 2, default disabled");
138 printf("\n --ns_lvl <ns_level>");
139 printf("\n Noise Suppression level in dB, default value 0dB");
140 printf("\n --agc_tgt_lvl <target_level>");
141 printf("\n AGC Target Level in dB, default value 3dB");
142 printf("\n --agc_comp_lvl <comp_level>");
143 printf("\n AGC Comp Level in dB, default value 9dB");
144 printf("\n --agc2_gain <fixed_digital_gain>");
145 printf("\n AGC Fixed Digital Gain in dB, default value 0dB");
146 printf("\n --agc2_lvl <level_estimator>");
147 printf("\n AGC Adaptive Digital Level Estimator, default value kRms");
148 printf("\n --agc2_sat_mgn <saturation_margin>");
149 printf("\n AGC Adaptive Digital Saturation Margin in dB, default value 2dB");
150 printf("\n --aec_delay <delay>");
151 printf("\n AEC delay value in ms, default value 0ms");
152 printf("\n --fch <fileChannels>");
153 printf("\n number of channels in the input file");
154 printf("\n --mono <Mono Mode>");
155 printf("\n Mode to make data of all channels the same as first channel");
156 printf("\n");
157 }
158
159 constexpr float kTenMilliSecVal = 0.01;
160
preProcCreateEffect(effect_handle_t * pEffectHandle,uint32_t effectType,effect_config_t * pConfig,int sessionId,int ioId)161 int preProcCreateEffect(effect_handle_t* pEffectHandle, uint32_t effectType,
162 effect_config_t* pConfig, int sessionId, int ioId) {
163 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kPreProcUuids[effectType],
164 sessionId, ioId, pEffectHandle);
165 status != 0) {
166 ALOGE("Audio Preprocessing create returned an error = %d\n", status);
167 return EXIT_FAILURE;
168 }
169 int reply = 0;
170 uint32_t replySize = sizeof(reply);
171 if (effectType == PREPROC_AEC) {
172 (**pEffectHandle)
173 ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG_REVERSE, sizeof(effect_config_t),
174 pConfig, &replySize, &reply);
175 }
176 (**pEffectHandle)
177 ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), pConfig,
178 &replySize, &reply);
179 return reply;
180 }
181
preProcSetConfigParam(uint32_t paramType,uint32_t paramValue,effect_handle_t effectHandle)182 int preProcSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
183 int reply = 0;
184 uint32_t replySize = sizeof(reply);
185 uint32_t paramData[2] = {paramType, paramValue};
186 effect_param_t* effectParam = (effect_param_t*)malloc(sizeof(*effectParam) + sizeof(paramData));
187 memcpy(&effectParam->data[0], ¶mData[0], sizeof(paramData));
188 effectParam->psize = sizeof(paramData[0]);
189 (*effectHandle)
190 ->command(effectHandle, EFFECT_CMD_SET_PARAM, sizeof(effect_param_t), effectParam,
191 &replySize, &reply);
192 free(effectParam);
193 return reply;
194 }
195
main(int argc,const char * argv[])196 int main(int argc, const char* argv[]) {
197 if (argc == 1) {
198 printUsage();
199 return EXIT_FAILURE;
200 }
201
202 // Print the arguments passed
203 for (int i = 1; i < argc; i++) {
204 printf("%s ", argv[i]);
205 }
206
207 const char* inputFile = nullptr;
208 const char* outputFile = nullptr;
209 const char* farFile = nullptr;
210 int effectEn[PREPROC_NUM_EFFECTS] = {0};
211 struct preProcConfigParams_t preProcCfgParams {};
212
213 const option long_opts[] = {
214 {"help", no_argument, nullptr, ARG_HELP},
215 {"input", required_argument, nullptr, ARG_INPUT},
216 {"output", required_argument, nullptr, ARG_OUTPUT},
217 {"far", required_argument, nullptr, ARG_FAR},
218 {"fs", required_argument, nullptr, ARG_FS},
219 {"ch_mask", required_argument, nullptr, ARG_CH_MASK},
220 {"agc_tgt_lvl", required_argument, nullptr, ARG_AGC_TGT_LVL},
221 {"agc_comp_lvl", required_argument, nullptr, ARG_AGC_COMP_LVL},
222 {"agc2_gain", required_argument, nullptr, ARG_AGC2_GAIN},
223 {"agc2_lvl", required_argument, nullptr, ARG_AGC2_LVL},
224 {"agc2_sat_mgn", required_argument, nullptr, ARG_AGC2_SAT_MGN},
225 {"aec_delay", required_argument, nullptr, ARG_AEC_DELAY},
226 {"ns_lvl", required_argument, nullptr, ARG_NS_LVL},
227 {"aec", no_argument, &effectEn[PREPROC_AEC], 1},
228 {"agc", no_argument, &effectEn[PREPROC_AGC], 1},
229 {"agc2", no_argument, &effectEn[PREPROC_AGC2], 1},
230 {"ns", no_argument, &effectEn[PREPROC_NS], 1},
231 {"fch", required_argument, nullptr, ARG_FILE_CHANNELS},
232 {"mono", no_argument, &preProcCfgParams.monoMode, 1},
233 {nullptr, 0, nullptr, 0},
234 };
235
236 while (true) {
237 const int opt = getopt_long(argc, (char* const*)argv, "i:o:", long_opts, nullptr);
238 if (opt == -1) {
239 break;
240 }
241 switch (opt) {
242 case ARG_HELP:
243 printUsage();
244 return 0;
245 case ARG_INPUT: {
246 inputFile = (char*)optarg;
247 break;
248 }
249 case ARG_OUTPUT: {
250 outputFile = (char*)optarg;
251 break;
252 }
253 case ARG_FAR: {
254 farFile = (char*)optarg;
255 break;
256 }
257 case ARG_FS: {
258 preProcCfgParams.samplingFreq = atoi(optarg);
259 break;
260 }
261 case ARG_CH_MASK: {
262 int chMaskIdx = atoi(optarg);
263 if (chMaskIdx < 0 or chMaskIdx > kPreProcConfigChMaskCount) {
264 ALOGE("Channel Mask index not in correct range\n");
265 printUsage();
266 return EXIT_FAILURE;
267 }
268 preProcCfgParams.chMask = kPreProcConfigChMask[chMaskIdx];
269 break;
270 }
271 case ARG_AGC_TGT_LVL: {
272 preProcCfgParams.agcTargetLevel = atoi(optarg);
273 break;
274 }
275 case ARG_AGC_COMP_LVL: {
276 preProcCfgParams.agcCompLevel = atoi(optarg);
277 break;
278 }
279 case ARG_AGC2_GAIN: {
280 preProcCfgParams.agc2Gain = atof(optarg);
281 break;
282 }
283 case ARG_AGC2_LVL: {
284 preProcCfgParams.agc2Level = atoi(optarg);
285 break;
286 }
287 case ARG_AGC2_SAT_MGN: {
288 preProcCfgParams.agc2SaturationMargin = atof(optarg);
289 break;
290 }
291 case ARG_AEC_DELAY: {
292 preProcCfgParams.aecDelay = atoi(optarg);
293 break;
294 }
295 case ARG_NS_LVL: {
296 preProcCfgParams.nsLevel = atoi(optarg);
297 break;
298 }
299 case ARG_FILE_CHANNELS: {
300 preProcCfgParams.fileChannels = atoi(optarg);
301 break;
302 }
303 case ARG_MONO_MODE: {
304 preProcCfgParams.monoMode = 1;
305 break;
306 }
307 default:
308 break;
309 }
310 }
311
312 if (inputFile == nullptr) {
313 ALOGE("Error: missing input file\n");
314 printUsage();
315 return EXIT_FAILURE;
316 }
317
318 std::unique_ptr<FILE, decltype(&fclose)> inputFp(fopen(inputFile, "rb"), &fclose);
319 if (inputFp == nullptr) {
320 ALOGE("Cannot open input file %s\n", inputFile);
321 return EXIT_FAILURE;
322 }
323
324 std::unique_ptr<FILE, decltype(&fclose)> farFp(fopen(farFile, "rb"), &fclose);
325 std::unique_ptr<FILE, decltype(&fclose)> outputFp(fopen(outputFile, "wb"), &fclose);
326 if (effectEn[PREPROC_AEC]) {
327 if (farFile == nullptr) {
328 ALOGE("Far end signal file required for echo cancellation \n");
329 return EXIT_FAILURE;
330 }
331 if (farFp == nullptr) {
332 ALOGE("Cannot open far end stream file %s\n", farFile);
333 return EXIT_FAILURE;
334 }
335 struct stat statInput, statFar;
336 (void)fstat(fileno(inputFp.get()), &statInput);
337 (void)fstat(fileno(farFp.get()), &statFar);
338 if (statInput.st_size != statFar.st_size) {
339 ALOGE("Near and far end signals are of different sizes");
340 return EXIT_FAILURE;
341 }
342 }
343 if (outputFile != nullptr && outputFp == nullptr) {
344 ALOGE("Cannot open output file %s\n", outputFile);
345 return EXIT_FAILURE;
346 }
347
348 int32_t sessionId = 1;
349 int32_t ioId = 1;
350 effect_handle_t effectHandle[PREPROC_NUM_EFFECTS] = {nullptr};
351 effect_config_t config;
352 config.inputCfg.samplingRate = config.outputCfg.samplingRate = preProcCfgParams.samplingFreq;
353 config.inputCfg.channels = config.outputCfg.channels = preProcCfgParams.chMask;
354 config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
355
356 // Create all the effect handles
357 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
358 if (int status = preProcCreateEffect(&effectHandle[i], i, &config, sessionId, ioId);
359 status != 0) {
360 ALOGE("Create effect call returned error %i", status);
361 return EXIT_FAILURE;
362 }
363 }
364
365 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
366 if (effectEn[i] == 1) {
367 int reply = 0;
368 uint32_t replySize = sizeof(reply);
369 (*effectHandle[i])
370 ->command(effectHandle[i], EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
371 if (reply != 0) {
372 ALOGE("Command enable call returned error %d\n", reply);
373 return EXIT_FAILURE;
374 }
375 }
376 }
377
378 // Set Config Params of the effects
379 if (effectEn[PREPROC_AGC]) {
380 if (int status = preProcSetConfigParam(AGC_PARAM_TARGET_LEVEL,
381 (uint32_t)preProcCfgParams.agcTargetLevel,
382 effectHandle[PREPROC_AGC]);
383 status != 0) {
384 ALOGE("Invalid AGC Target Level. Error %d\n", status);
385 return EXIT_FAILURE;
386 }
387 if (int status = preProcSetConfigParam(AGC_PARAM_COMP_GAIN,
388 (uint32_t)preProcCfgParams.agcCompLevel,
389 effectHandle[PREPROC_AGC]);
390 status != 0) {
391 ALOGE("Invalid AGC Comp Gain. Error %d\n", status);
392 return EXIT_FAILURE;
393 }
394 }
395 if (effectEn[PREPROC_AGC2]) {
396 if (int status = preProcSetConfigParam(AGC2_PARAM_FIXED_DIGITAL_GAIN,
397 (float)preProcCfgParams.agc2Gain,
398 effectHandle[PREPROC_AGC2]);
399 status != 0) {
400 ALOGE("Invalid AGC2 Fixed Digital Gain. Error %d\n", status);
401 return EXIT_FAILURE;
402 }
403 if (int status = preProcSetConfigParam(AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR,
404 (uint32_t)preProcCfgParams.agc2Level,
405 effectHandle[PREPROC_AGC2]);
406 status != 0) {
407 ALOGE("Invalid AGC2 Level Estimator. Error %d\n", status);
408 return EXIT_FAILURE;
409 }
410 if (int status = preProcSetConfigParam(AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN,
411 (float)preProcCfgParams.agc2SaturationMargin,
412 effectHandle[PREPROC_AGC2]);
413 status != 0) {
414 ALOGE("Invalid AGC2 Saturation Margin. Error %d\n", status);
415 return EXIT_FAILURE;
416 }
417 }
418 if (effectEn[PREPROC_NS]) {
419 if (int status = preProcSetConfigParam(NS_PARAM_LEVEL, (uint32_t)preProcCfgParams.nsLevel,
420 effectHandle[PREPROC_NS]);
421 status != 0) {
422 ALOGE("Invalid Noise Suppression level Error %d\n", status);
423 return EXIT_FAILURE;
424 }
425 }
426
427 // Process Call
428 const int frameLength = (int)(preProcCfgParams.samplingFreq * kTenMilliSecVal);
429 const int ioChannelCount = audio_channel_count_from_in_mask(preProcCfgParams.chMask);
430 const int fileChannelCount = preProcCfgParams.fileChannels;
431 const int ioFrameSize = ioChannelCount * sizeof(short);
432 const int inFrameSize = fileChannelCount * sizeof(short);
433 int frameCounter = 0;
434 while (true) {
435 std::vector<short> in(frameLength * ioChannelCount);
436 std::vector<short> out(frameLength * ioChannelCount);
437 std::vector<short> farIn(frameLength * ioChannelCount);
438 size_t samplesRead = fread(in.data(), inFrameSize, frameLength, inputFp.get());
439 if (samplesRead == 0) {
440 break;
441 }
442 if (fileChannelCount != ioChannelCount) {
443 adjust_channels(in.data(), fileChannelCount, in.data(), ioChannelCount, sizeof(short),
444 frameLength * inFrameSize);
445 if (preProcCfgParams.monoMode == 1) {
446 for (int i = 0; i < frameLength; ++i) {
447 auto* fp = &in[i * ioChannelCount];
448 std::fill(fp + 1, fp + ioChannelCount, *fp); // replicate ch 0
449 }
450 }
451 }
452 audio_buffer_t inputBuffer, outputBuffer;
453 audio_buffer_t farInBuffer{};
454 inputBuffer.frameCount = frameLength;
455 outputBuffer.frameCount = frameLength;
456 inputBuffer.s16 = in.data();
457 outputBuffer.s16 = out.data();
458
459 if (farFp != nullptr) {
460 samplesRead = fread(farIn.data(), inFrameSize, frameLength, farFp.get());
461 if (samplesRead == 0) {
462 break;
463 }
464 if (fileChannelCount != ioChannelCount) {
465 adjust_channels(farIn.data(), fileChannelCount, farIn.data(), ioChannelCount,
466 sizeof(short), frameLength * inFrameSize);
467 if (preProcCfgParams.monoMode == 1) {
468 for (int i = 0; i < frameLength; ++i) {
469 auto* fp = &farIn[i * ioChannelCount];
470 std::fill(fp + 1, fp + ioChannelCount, *fp); // replicate ch 0
471 }
472 }
473 }
474
475 farInBuffer.frameCount = frameLength;
476 farInBuffer.s16 = farIn.data();
477 }
478
479 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
480 if (effectEn[i] == 1) {
481 if (i == PREPROC_AEC) {
482 if (int status = preProcSetConfigParam(AEC_PARAM_ECHO_DELAY,
483 (uint32_t)preProcCfgParams.aecDelay,
484 effectHandle[PREPROC_AEC]);
485 status != 0) {
486 ALOGE("preProcSetConfigParam returned Error %d\n", status);
487 return EXIT_FAILURE;
488 }
489 }
490 if (int status = (*effectHandle[i])
491 ->process(effectHandle[i], &inputBuffer, &outputBuffer);
492 status != 0) {
493 ALOGE("\nError: Process i = %d returned with error %d\n", i, status);
494 return EXIT_FAILURE;
495 }
496 if (i == PREPROC_AEC) {
497 if (int status = (*effectHandle[i])
498 ->process_reverse(effectHandle[i], &farInBuffer,
499 &outputBuffer);
500 status != 0) {
501 ALOGE("\nError: Process reverse i = %d returned with error %d\n", i,
502 status);
503 return EXIT_FAILURE;
504 }
505 }
506 }
507 }
508 if (outputFp != nullptr) {
509 if (fileChannelCount != ioChannelCount) {
510 adjust_channels(out.data(), ioChannelCount, out.data(), fileChannelCount,
511 sizeof(short), frameLength * ioFrameSize);
512 }
513 size_t samplesWritten =
514 fwrite(out.data(), inFrameSize, outputBuffer.frameCount, outputFp.get());
515 if (samplesWritten != outputBuffer.frameCount) {
516 ALOGE("\nError: Output file writing failed");
517 break;
518 }
519 }
520 frameCounter += frameLength;
521 }
522 printf("frameCounter: [%d]\n", frameCounter);
523 // Release all the effect handles created
524 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
525 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle[i]);
526 status != 0) {
527 ALOGE("Audio Preprocessing release returned an error = %d\n", status);
528 return EXIT_FAILURE;
529 }
530 }
531 return EXIT_SUCCESS;
532 }
533