blob: 5c81d78c29158d7ff718e89a11046022facf0630 [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
17#include <audio_effects/effect_aec.h>
18#include <audio_effects/effect_agc.h>
19#include <audio_effects/effect_ns.h>
20#include <audio_processing.h>
21#include <getopt.h>
22#include <hardware/audio_effect.h>
23#include <module_common_types.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <utils/Log.h>
28#include <utils/Timers.h>
29
30#include <audio_utils/channels.h>
31#include <audio_utils/primitives.h>
32#include <log/log.h>
33#include <system/audio.h>
34
35// This is the only symbol that needs to be imported
36extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
37
38//------------------------------------------------------------------------------
39// local definitions
40//------------------------------------------------------------------------------
41
42// types of pre processing modules
43enum PreProcId {
44 PREPROC_AGC, // Automatic Gain Control
45 PREPROC_AEC, // Acoustic Echo Canceler
46 PREPROC_NS, // Noise Suppressor
47 PREPROC_NUM_EFFECTS
48};
49
50enum PreProcParams {
51 ARG_HELP = 1,
52 ARG_INPUT,
53 ARG_OUTPUT,
54 ARG_FAR,
55 ARG_FS,
56 ARG_CH_MASK,
57 ARG_AGC_TGT_LVL,
58 ARG_AGC_COMP_LVL,
59 ARG_AEC_DELAY,
60 ARG_NS_LVL,
61};
62
63struct preProcConfigParams_t {
64 int samplingFreq = 16000;
65 audio_channel_mask_t chMask = AUDIO_CHANNEL_IN_MONO;
66 int nsLevel = 0; // a value between 0-3
67 int agcTargetLevel = 3; // in dB
68 int agcCompLevel = 9; // in dB
69 int aecDelay = 0; // in ms
70};
71
72const effect_uuid_t kPreProcUuids[PREPROC_NUM_EFFECTS] = {
73 {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // agc uuid
74 {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // aec uuid
75 {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // ns uuid
76};
77
78constexpr audio_channel_mask_t kPreProcConfigChMask[] = {
79 AUDIO_CHANNEL_IN_MONO,
80 AUDIO_CHANNEL_IN_STEREO,
81 AUDIO_CHANNEL_IN_FRONT_BACK,
82 AUDIO_CHANNEL_IN_6,
83 AUDIO_CHANNEL_IN_2POINT0POINT2,
84 AUDIO_CHANNEL_IN_2POINT1POINT2,
85 AUDIO_CHANNEL_IN_3POINT0POINT2,
86 AUDIO_CHANNEL_IN_3POINT1POINT2,
87 AUDIO_CHANNEL_IN_5POINT1,
88 AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO,
89 AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO,
90 AUDIO_CHANNEL_IN_VOICE_CALL_MONO,
91};
92
93constexpr int kPreProcConfigChMaskCount = std::size(kPreProcConfigChMask);
94
95void printUsage() {
96 printf("\nUsage: ");
97 printf("\n <executable> [options]\n");
98 printf("\nwhere options are, ");
99 printf("\n --input <inputfile>");
100 printf("\n path to the input file");
101 printf("\n --output <outputfile>");
102 printf("\n path to the output file");
103 printf("\n --help");
104 printf("\n Prints this usage information");
105 printf("\n --fs <sampling_freq>");
106 printf("\n Sampling frequency in Hz, default 16000.");
107 printf("\n -ch_mask <channel_mask>\n");
108 printf("\n 0 - AUDIO_CHANNEL_IN_MONO");
109 printf("\n 1 - AUDIO_CHANNEL_IN_STEREO");
110 printf("\n 2 - AUDIO_CHANNEL_IN_FRONT_BACK");
111 printf("\n 3 - AUDIO_CHANNEL_IN_6");
112 printf("\n 4 - AUDIO_CHANNEL_IN_2POINT0POINT2");
113 printf("\n 5 - AUDIO_CHANNEL_IN_2POINT1POINT2");
114 printf("\n 6 - AUDIO_CHANNEL_IN_3POINT0POINT2");
115 printf("\n 7 - AUDIO_CHANNEL_IN_3POINT1POINT2");
116 printf("\n 8 - AUDIO_CHANNEL_IN_5POINT1");
117 printf("\n 9 - AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO");
118 printf("\n 10 - AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO ");
119 printf("\n 11 - AUDIO_CHANNEL_IN_VOICE_CALL_MONO ");
120 printf("\n default 0");
121 printf("\n --far <farend_file>");
122 printf("\n Path to far-end file needed for echo cancellation");
123 printf("\n --aec");
124 printf("\n Enable Echo Cancellation, default disabled");
125 printf("\n --ns");
126 printf("\n Enable Noise Suppression, default disabled");
127 printf("\n --agc");
128 printf("\n Enable Gain Control, default disabled");
129 printf("\n --ns_lvl <ns_level>");
130 printf("\n Noise Suppression level in dB, default value 0dB");
131 printf("\n --agc_tgt_lvl <target_level>");
132 printf("\n AGC Target Level in dB, default value 3dB");
133 printf("\n --agc_comp_lvl <comp_level>");
134 printf("\n AGC Comp Level in dB, default value 9dB");
135 printf("\n --aec_delay <delay>");
136 printf("\n AEC delay value in ms, default value 0ms");
137 printf("\n");
138}
139
140constexpr float kTenMilliSecVal = 0.01;
141
142int preProcCreateEffect(effect_handle_t *pEffectHandle, uint32_t effectType,
143 effect_config_t *pConfig, int sessionId, int ioId) {
144 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kPreProcUuids[effectType],
145 sessionId, ioId, pEffectHandle);
146 status != 0) {
147 ALOGE("Audio Preprocessing create returned an error = %d\n", status);
148 return EXIT_FAILURE;
149 }
150 int reply = 0;
151 uint32_t replySize = sizeof(reply);
152 if (effectType == PREPROC_AEC) {
153 (**pEffectHandle)
154 ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG_REVERSE, sizeof(effect_config_t), pConfig,
155 &replySize, &reply);
156 }
157 (**pEffectHandle)
158 ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), pConfig,
159 &replySize, &reply);
160 return reply;
161}
162
163int preProcSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
164 int reply = 0;
165 uint32_t replySize = sizeof(reply);
166 uint32_t paramData[2] = {paramType, paramValue};
167 effect_param_t *effectParam =
168 (effect_param_t *)malloc(sizeof(*effectParam) + sizeof(paramData));
169 memcpy(&effectParam->data[0], &paramData[0], sizeof(paramData));
170 effectParam->psize = sizeof(paramData[0]);
171 (*effectHandle)
172 ->command(effectHandle, EFFECT_CMD_SET_PARAM, sizeof(effect_param_t), effectParam,
173 &replySize, &reply);
174 free(effectParam);
175 return reply;
176}
177
178int main(int argc, const char *argv[]) {
179 if (argc == 1) {
180 printUsage();
181 return EXIT_FAILURE;
182 }
183 const char *inputFile = nullptr;
184 const char *outputFile = nullptr;
185 const char *farFile = nullptr;
186 int effectEn[PREPROC_NUM_EFFECTS] = {0};
187
188 const option long_opts[] = {
189 {"help", no_argument, nullptr, ARG_HELP},
190 {"input", required_argument, nullptr, ARG_INPUT},
191 {"output", required_argument, nullptr, ARG_OUTPUT},
192 {"far", required_argument, nullptr, ARG_FAR},
193 {"fs", required_argument, nullptr, ARG_FS},
194 {"ch_mask", required_argument, nullptr, ARG_CH_MASK},
195 {"agc_tgt_lvl", required_argument, nullptr, ARG_AGC_TGT_LVL},
196 {"agc_comp_lvl", required_argument, nullptr, ARG_AGC_COMP_LVL},
197 {"aec_delay", required_argument, nullptr, ARG_AEC_DELAY},
198 {"ns_lvl", required_argument, nullptr, ARG_NS_LVL},
199 {"aec", no_argument, &effectEn[PREPROC_AEC], 1},
200 {"agc", no_argument, &effectEn[PREPROC_AGC], 1},
201 {"ns", no_argument, &effectEn[PREPROC_NS], 1},
202 {nullptr, 0, nullptr, 0},
203 };
204 struct preProcConfigParams_t preProcCfgParams {};
205
206 while (true) {
207 const int opt = getopt_long(argc, (char *const *)argv, "i:o:", long_opts, nullptr);
208 if (opt == -1) {
209 break;
210 }
211 switch (opt) {
212 case ARG_HELP:
213 printUsage();
214 return 0;
215 case ARG_INPUT: {
216 inputFile = (char *)optarg;
217 break;
218 }
219 case ARG_OUTPUT: {
220 outputFile = (char *)optarg;
221 break;
222 }
223 case ARG_FAR: {
224 farFile = (char *)optarg;
225 break;
226 }
227 case ARG_FS: {
228 preProcCfgParams.samplingFreq = atoi(optarg);
229 break;
230 }
231 case ARG_CH_MASK: {
232 int chMaskIdx = atoi(optarg);
233 if (chMaskIdx < 0 or chMaskIdx > kPreProcConfigChMaskCount) {
234 ALOGE("Channel Mask index not in correct range\n");
235 printUsage();
236 return EXIT_FAILURE;
237 }
238 preProcCfgParams.chMask = kPreProcConfigChMask[chMaskIdx];
239 break;
240 }
241 case ARG_AGC_TGT_LVL: {
242 preProcCfgParams.agcTargetLevel = atoi(optarg);
243 break;
244 }
245 case ARG_AGC_COMP_LVL: {
246 preProcCfgParams.agcCompLevel = atoi(optarg);
247 break;
248 }
249 case ARG_AEC_DELAY: {
250 preProcCfgParams.aecDelay = atoi(optarg);
251 break;
252 }
253 case ARG_NS_LVL: {
254 preProcCfgParams.nsLevel = atoi(optarg);
255 break;
256 }
257 default:
258 break;
259 }
260 }
261
262 if (inputFile == nullptr) {
263 ALOGE("Error: missing input file\n");
264 printUsage();
265 return EXIT_FAILURE;
266 }
267
268 std::unique_ptr<FILE, decltype(&fclose)> inputFp(fopen(inputFile, "rb"), &fclose);
269 if (inputFp == nullptr) {
270 ALOGE("Cannot open input file %s\n", inputFile);
271 return EXIT_FAILURE;
272 }
273
274 std::unique_ptr<FILE, decltype(&fclose)> farFp(fopen(farFile, "rb"), &fclose);
275 std::unique_ptr<FILE, decltype(&fclose)> outputFp(fopen(outputFile, "wb"), &fclose);
276 if (effectEn[PREPROC_AEC]) {
277 if (farFile == nullptr) {
278 ALOGE("Far end signal file required for echo cancellation \n");
279 return EXIT_FAILURE;
280 }
281 if (farFp == nullptr) {
282 ALOGE("Cannot open far end stream file %s\n", farFile);
283 return EXIT_FAILURE;
284 }
285 struct stat statInput, statFar;
286 (void)fstat(fileno(inputFp.get()), &statInput);
287 (void)fstat(fileno(farFp.get()), &statFar);
288 if (statInput.st_size != statFar.st_size) {
289 ALOGE("Near and far end signals are of different sizes");
290 return EXIT_FAILURE;
291 }
292 }
293 if (outputFile != nullptr && outputFp == nullptr) {
294 ALOGE("Cannot open output file %s\n", outputFile);
295 return EXIT_FAILURE;
296 }
297
298 int32_t sessionId = 1;
299 int32_t ioId = 1;
300 effect_handle_t effectHandle[PREPROC_NUM_EFFECTS] = {nullptr};
301 effect_config_t config;
302 config.inputCfg.samplingRate = config.outputCfg.samplingRate = preProcCfgParams.samplingFreq;
303 config.inputCfg.channels = config.outputCfg.channels = preProcCfgParams.chMask;
304 config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
305
306 // Create all the effect handles
307 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
308 if (int status = preProcCreateEffect(&effectHandle[i], i, &config, sessionId, ioId);
309 status != 0) {
310 ALOGE("Create effect call returned error %i", status);
311 return EXIT_FAILURE;
312 }
313 }
314
315 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
316 if (effectEn[i] == 1) {
317 int reply = 0;
318 uint32_t replySize = sizeof(reply);
319 (*effectHandle[i])
320 ->command(effectHandle[i], EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
321 if (reply != 0) {
322 ALOGE("Command enable call returned error %d\n", reply);
323 return EXIT_FAILURE;
324 }
325 }
326 }
327
328 // Set Config Params of the effects
329 if (effectEn[PREPROC_AGC]) {
330 if (int status = preProcSetConfigParam(AGC_PARAM_TARGET_LEVEL,
331 (uint32_t)preProcCfgParams.agcTargetLevel,
332 effectHandle[PREPROC_AGC]);
333 status != 0) {
334 ALOGE("Invalid AGC Target Level. Error %d\n", status);
335 return EXIT_FAILURE;
336 }
337 if (int status =
338 preProcSetConfigParam(AGC_PARAM_COMP_GAIN, (uint32_t)preProcCfgParams.agcCompLevel,
339 effectHandle[PREPROC_AGC]);
340 status != 0) {
341 ALOGE("Invalid AGC Comp Gain. Error %d\n", status);
342 return EXIT_FAILURE;
343 }
344 }
345 if (effectEn[PREPROC_NS]) {
346 if (int status = preProcSetConfigParam(NS_PARAM_LEVEL, (uint32_t)preProcCfgParams.nsLevel,
347 effectHandle[PREPROC_NS]);
348 status != 0) {
349 ALOGE("Invalid Noise Suppression level Error %d\n", status);
350 return EXIT_FAILURE;
351 }
352 }
353
354 // Process Call
355 const int frameLength = (int)(preProcCfgParams.samplingFreq * kTenMilliSecVal);
356 const int ioChannelCount = audio_channel_count_from_in_mask(preProcCfgParams.chMask);
357 const int ioFrameSize = ioChannelCount * sizeof(short);
358 int frameCounter = 0;
359 while (true) {
360 std::vector<short> in(frameLength * ioChannelCount);
361 std::vector<short> out(frameLength * ioChannelCount);
362 std::vector<short> farIn(frameLength * ioChannelCount);
363 size_t samplesRead = fread(in.data(), ioFrameSize, frameLength, inputFp.get());
364 if (samplesRead == 0) {
365 break;
366 }
367 audio_buffer_t inputBuffer, outputBuffer;
368 audio_buffer_t farInBuffer{};
369 inputBuffer.frameCount = samplesRead;
370 outputBuffer.frameCount = samplesRead;
371 inputBuffer.s16 = in.data();
372 outputBuffer.s16 = out.data();
373
374 if (farFp != nullptr) {
375 samplesRead = fread(farIn.data(), ioFrameSize, frameLength, farFp.get());
376 if (samplesRead == 0) {
377 break;
378 }
379 farInBuffer.frameCount = samplesRead;
380 farInBuffer.s16 = farIn.data();
381 }
382
383 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
384 if (effectEn[i] == 1) {
385 if (i == PREPROC_AEC) {
386 if (int status =
387 preProcSetConfigParam(AEC_PARAM_ECHO_DELAY, (uint32_t)preProcCfgParams.aecDelay,
388 effectHandle[PREPROC_AEC]);
389 status != 0) {
390 ALOGE("preProcSetConfigParam returned Error %d\n", status);
391 return EXIT_FAILURE;
392 }
393 }
394 if (int status =
395 (*effectHandle[i])->process(effectHandle[i], &inputBuffer, &outputBuffer);
396 status != 0) {
397 ALOGE("\nError: Process i = %d returned with error %d\n", i, status);
398 return EXIT_FAILURE;
399 }
400 if (i == PREPROC_AEC) {
401 if (int status = (*effectHandle[i])
402 ->process_reverse(effectHandle[i], &farInBuffer, &outputBuffer);
403 status != 0) {
404 ALOGE("\nError: Process reverse i = %d returned with error %d\n", i, status);
405 return EXIT_FAILURE;
406 }
407 }
408 }
409 }
410 if (outputFp != nullptr) {
411 size_t samplesWritten =
412 fwrite(out.data(), ioFrameSize, outputBuffer.frameCount, outputFp.get());
413 if (samplesWritten != outputBuffer.frameCount) {
414 ALOGE("\nError: Output file writing failed");
415 break;
416 }
417 }
418 frameCounter += frameLength;
419 }
420 // Release all the effect handles created
421 for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
422 if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle[i]);
423 status != 0) {
424 ALOGE("Audio Preprocessing release returned an error = %d\n", status);
425 return EXIT_FAILURE;
426 }
427 }
428 return EXIT_SUCCESS;
429}