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