1 /*
2 * Copyright (C) 2011 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 <stdlib.h>
18 #include <string.h>
19 #define LOG_TAG "PreProcessing"
20 //#define LOG_NDEBUG 0
21 #include <audio_effects/effect_aec.h>
22 #include <audio_effects/effect_agc.h>
23 #include <hardware/audio_effect.h>
24 #include <utils/Log.h>
25 #include <utils/Timers.h>
26 #include <audio_effects/effect_agc2.h>
27 #include <audio_effects/effect_ns.h>
28 #include <audio_processing.h>
29 #include <module_common_types.h>
30
31 // undefine to perform multi channels API functional tests
32 //#define DUAL_MIC_TEST
33
34 //------------------------------------------------------------------------------
35 // local definitions
36 //------------------------------------------------------------------------------
37
38 // maximum number of sessions
39 #define PREPROC_NUM_SESSIONS 8
40
41 // types of pre processing modules
42 enum preproc_id {
43 PREPROC_AGC, // Automatic Gain Control
44 PREPROC_AGC2, // Automatic Gain Control 2
45 PREPROC_AEC, // Acoustic Echo Canceler
46 PREPROC_NS, // Noise Suppressor
47 PREPROC_NUM_EFFECTS
48 };
49
50 // Session state
51 enum preproc_session_state {
52 PREPROC_SESSION_STATE_INIT, // initialized
53 PREPROC_SESSION_STATE_CONFIG // configuration received
54 };
55
56 // Effect/Preprocessor state
57 enum preproc_effect_state {
58 PREPROC_EFFECT_STATE_INIT, // initialized
59 PREPROC_EFFECT_STATE_CREATED, // webRTC engine created
60 PREPROC_EFFECT_STATE_CONFIG, // configuration received/disabled
61 PREPROC_EFFECT_STATE_ACTIVE // active/enabled
62 };
63
64 // handle on webRTC engine
65 typedef void* preproc_fx_handle_t;
66
67 typedef struct preproc_session_s preproc_session_t;
68 typedef struct preproc_effect_s preproc_effect_t;
69 typedef struct preproc_ops_s preproc_ops_t;
70
71 // Effect operation table. Functions for all pre processors are declared in sPreProcOps[] table.
72 // Function pointer can be null if no action required.
73 struct preproc_ops_s {
74 int (*create)(preproc_effect_t* fx);
75 int (*init)(preproc_effect_t* fx);
76 int (*reset)(preproc_effect_t* fx);
77 void (*enable)(preproc_effect_t* fx);
78 void (*disable)(preproc_effect_t* fx);
79 int (*set_parameter)(preproc_effect_t* fx, void* param, void* value);
80 int (*get_parameter)(preproc_effect_t* fx, void* param, uint32_t* size, void* value);
81 int (*set_device)(preproc_effect_t* fx, uint32_t device);
82 };
83
84 // Effect context
85 struct preproc_effect_s {
86 const struct effect_interface_s* itfe;
87 uint32_t procId; // type of pre processor (enum preproc_id)
88 uint32_t state; // current state (enum preproc_effect_state)
89 preproc_session_t* session; // session the effect is on
90 const preproc_ops_t* ops; // effect ops table
91 preproc_fx_handle_t engine; // handle on webRTC engine
92 uint32_t type; // subtype of effect
93 #ifdef DUAL_MIC_TEST
94 bool aux_channels_on; // support auxiliary channels
95 size_t cur_channel_config; // current auciliary channel configuration
96 #endif
97 };
98
99 // Session context
100 struct preproc_session_s {
101 struct preproc_effect_s effects[PREPROC_NUM_EFFECTS]; // effects in this session
102 uint32_t state; // current state (enum preproc_session_state)
103 int id; // audio session ID
104 int io; // handle of input stream this session is on
105 webrtc::AudioProcessing* apm; // handle on webRTC audio processing module (APM)
106 // Audio Processing module builder
107 webrtc::AudioProcessingBuilder ap_builder;
108 // frameCount represents the size of the buffers used for processing, and must represent 10ms.
109 size_t frameCount;
110 uint32_t samplingRate; // sampling rate at effect process interface
111 uint32_t inChannelCount; // input channel count
112 uint32_t outChannelCount; // output channel count
113 uint32_t createdMsk; // bit field containing IDs of crested pre processors
114 uint32_t enabledMsk; // bit field containing IDs of enabled pre processors
115 uint32_t processedMsk; // bit field containing IDs of pre processors already
116 // processed in current round
117 // audio config strucutre
118 webrtc::AudioProcessing::Config config;
119 webrtc::StreamConfig inputConfig; // input stream configuration
120 webrtc::StreamConfig outputConfig; // output stream configuration
121 uint32_t revChannelCount; // number of channels on reverse stream
122 uint32_t revEnabledMsk; // bit field containing IDs of enabled pre processors
123 // with reverse channel
124 uint32_t revProcessedMsk; // bit field containing IDs of pre processors with reverse
125 // channel already processed in current round
126 webrtc::StreamConfig revConfig; // reverse stream configuration.
127 };
128
129 #ifdef DUAL_MIC_TEST
130 enum {
131 PREPROC_CMD_DUAL_MIC_ENABLE = EFFECT_CMD_FIRST_PROPRIETARY, // enable dual mic mode
132 PREPROC_CMD_DUAL_MIC_PCM_DUMP_START, // start pcm capture
133 PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP // stop pcm capture
134 };
135
136 enum {
137 CHANNEL_CFG_MONO,
138 CHANNEL_CFG_STEREO,
139 CHANNEL_CFG_MONO_AUX,
140 CHANNEL_CFG_STEREO_AUX,
141 CHANNEL_CFG_CNT,
142 CHANNEL_CFG_FIRST_AUX = CHANNEL_CFG_MONO_AUX,
143 };
144
145 const channel_config_t sDualMicConfigs[CHANNEL_CFG_CNT] = {
146 {AUDIO_CHANNEL_IN_MONO, 0},
147 {AUDIO_CHANNEL_IN_STEREO, 0},
148 {AUDIO_CHANNEL_IN_FRONT, AUDIO_CHANNEL_IN_BACK},
149 {AUDIO_CHANNEL_IN_STEREO, AUDIO_CHANNEL_IN_RIGHT}};
150
151 bool sHasAuxChannels[PREPROC_NUM_EFFECTS] = {
152 false, // PREPROC_AGC
153 true, // PREPROC_AEC
154 true, // PREPROC_NS
155 };
156
157 bool gDualMicEnabled;
158 FILE* gPcmDumpFh;
159 static pthread_mutex_t gPcmDumpLock = PTHREAD_MUTEX_INITIALIZER;
160 #endif
161
162 //------------------------------------------------------------------------------
163 // Effect descriptors
164 //------------------------------------------------------------------------------
165
166 // UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html
167 // as the pre processing effects are not defined by OpenSL ES
168
169 // Automatic Gain Control
170 static const effect_descriptor_t sAgcDescriptor = {
171 {0x0a8abfe0, 0x654c, 0x11e0, 0xba26, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
172 {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
173 EFFECT_CONTROL_API_VERSION,
174 (EFFECT_FLAG_TYPE_PRE_PROC | EFFECT_FLAG_DEVICE_IND),
175 0, // FIXME indicate CPU load
176 0, // FIXME indicate memory usage
177 "Automatic Gain Control",
178 "The Android Open Source Project"};
179
180 // Automatic Gain Control 2
181 static const effect_descriptor_t sAgc2Descriptor = {
182 {0xae3c653b, 0xbe18, 0x4ab8, 0x8938, {0x41, 0x8f, 0x0a, 0x7f, 0x06, 0xac}}, // type
183 {0x89f38e65, 0xd4d2, 0x4d64, 0xad0e, {0x2b, 0x3e, 0x79, 0x9e, 0xa8, 0x86}}, // uuid
184 EFFECT_CONTROL_API_VERSION,
185 (EFFECT_FLAG_TYPE_PRE_PROC | EFFECT_FLAG_DEVICE_IND),
186 0, // FIXME indicate CPU load
187 0, // FIXME indicate memory usage
188 "Automatic Gain Control 2",
189 "The Android Open Source Project"};
190
191 // Acoustic Echo Cancellation
192 static const effect_descriptor_t sAecDescriptor = {
193 {0x7b491460, 0x8d4d, 0x11e0, 0xbd61, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
194 {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
195 EFFECT_CONTROL_API_VERSION,
196 (EFFECT_FLAG_TYPE_PRE_PROC | EFFECT_FLAG_DEVICE_IND),
197 0, // FIXME indicate CPU load
198 0, // FIXME indicate memory usage
199 "Acoustic Echo Canceler",
200 "The Android Open Source Project"};
201
202 // Noise suppression
203 static const effect_descriptor_t sNsDescriptor = {
204 {0x58b4b260, 0x8e06, 0x11e0, 0xaa8e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
205 {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
206 EFFECT_CONTROL_API_VERSION,
207 (EFFECT_FLAG_TYPE_PRE_PROC | EFFECT_FLAG_DEVICE_IND),
208 0, // FIXME indicate CPU load
209 0, // FIXME indicate memory usage
210 "Noise Suppression",
211 "The Android Open Source Project"};
212
213 static const effect_descriptor_t* sDescriptors[PREPROC_NUM_EFFECTS] = {&sAgcDescriptor,
214 &sAgc2Descriptor,
215 &sAecDescriptor,
216 &sNsDescriptor};
217
218 //------------------------------------------------------------------------------
219 // Helper functions
220 //------------------------------------------------------------------------------
221
222 const effect_uuid_t* const sUuidToPreProcTable[PREPROC_NUM_EFFECTS] = {FX_IID_AGC,
223 FX_IID_AGC2,
224 FX_IID_AEC, FX_IID_NS};
225
ProcIdToUuid(int procId)226 const effect_uuid_t* ProcIdToUuid(int procId) {
227 if (procId >= PREPROC_NUM_EFFECTS) {
228 return EFFECT_UUID_NULL;
229 }
230 return sUuidToPreProcTable[procId];
231 }
232
UuidToProcId(const effect_uuid_t * uuid)233 uint32_t UuidToProcId(const effect_uuid_t* uuid) {
234 size_t i;
235 for (i = 0; i < PREPROC_NUM_EFFECTS; i++) {
236 if (memcmp(uuid, sUuidToPreProcTable[i], sizeof(*uuid)) == 0) {
237 break;
238 }
239 }
240 return i;
241 }
242
HasReverseStream(uint32_t procId)243 bool HasReverseStream(uint32_t procId) {
244 if (procId == PREPROC_AEC) {
245 return true;
246 }
247 return false;
248 }
249
250 //------------------------------------------------------------------------------
251 // Automatic Gain Control (AGC)
252 //------------------------------------------------------------------------------
253
254 static const int kAgcDefaultTargetLevel = 3;
255 static const int kAgcDefaultCompGain = 9;
256 static const bool kAgcDefaultLimiter = true;
257
Agc2Init(preproc_effect_t * effect)258 int Agc2Init(preproc_effect_t* effect) {
259 ALOGV("Agc2Init");
260 effect->session->config = effect->session->apm->GetConfig();
261 effect->session->config.gain_controller2.fixed_digital.gain_db = 0.f;
262 effect->session->config.gain_controller2.adaptive_digital.level_estimator =
263 effect->session->config.gain_controller2.kRms;
264 effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db = 2.f;
265 effect->session->apm->ApplyConfig(effect->session->config);
266 return 0;
267 }
268
AgcInit(preproc_effect_t * effect)269 int AgcInit(preproc_effect_t* effect) {
270 ALOGV("AgcInit");
271 effect->session->config = effect->session->apm->GetConfig();
272 effect->session->config.gain_controller1.target_level_dbfs = kAgcDefaultTargetLevel;
273 effect->session->config.gain_controller1.compression_gain_db = kAgcDefaultCompGain;
274 effect->session->config.gain_controller1.enable_limiter = kAgcDefaultLimiter;
275 effect->session->apm->ApplyConfig(effect->session->config);
276 return 0;
277 }
278
Agc2Create(preproc_effect_t * effect)279 int Agc2Create(preproc_effect_t* effect) {
280 Agc2Init(effect);
281 return 0;
282 }
283
AgcCreate(preproc_effect_t * effect)284 int AgcCreate(preproc_effect_t* effect) {
285 AgcInit(effect);
286 return 0;
287 }
288
Agc2GetParameter(preproc_effect_t * effect,void * pParam,uint32_t * pValueSize,void * pValue)289 int Agc2GetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) {
290 int status = 0;
291 uint32_t param = *(uint32_t*)pParam;
292 agc2_settings_t* pProperties = (agc2_settings_t*)pValue;
293
294 switch (param) {
295 case AGC2_PARAM_FIXED_DIGITAL_GAIN:
296 if (*pValueSize < sizeof(float)) {
297 *pValueSize = 0.f;
298 return -EINVAL;
299 }
300 break;
301 case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR:
302 if (*pValueSize < sizeof(int32_t)) {
303 *pValueSize = 0;
304 return -EINVAL;
305 }
306 break;
307 case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN:
308 if (*pValueSize < sizeof(float)) {
309 *pValueSize = 0.f;
310 return -EINVAL;
311 }
312 break;
313 case AGC2_PARAM_PROPERTIES:
314 if (*pValueSize < sizeof(agc2_settings_t)) {
315 *pValueSize = 0;
316 return -EINVAL;
317 }
318 break;
319
320 default:
321 ALOGW("Agc2GetParameter() unknown param %08x", param);
322 status = -EINVAL;
323 break;
324 }
325
326 effect->session->config = effect->session->apm->GetConfig();
327 switch (param) {
328 case AGC2_PARAM_FIXED_DIGITAL_GAIN:
329 *(float*)pValue =
330 (float)(effect->session->config.gain_controller2.fixed_digital.gain_db);
331 ALOGV("Agc2GetParameter() target level %f dB", *(float*)pValue);
332 break;
333 case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR:
334 *(uint32_t*)pValue = (uint32_t)(
335 effect->session->config.gain_controller2.adaptive_digital.level_estimator);
336 ALOGV("Agc2GetParameter() level estimator %d",
337 *(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
338 break;
339 case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN:
340 *(float*)pValue = (float)(effect->session->config.gain_controller2.adaptive_digital
341 .extra_saturation_margin_db);
342 ALOGV("Agc2GetParameter() extra saturation margin %f dB", *(float*)pValue);
343 break;
344 case AGC2_PARAM_PROPERTIES:
345 pProperties->fixedDigitalGain =
346 (float)(effect->session->config.gain_controller2.fixed_digital.gain_db);
347 pProperties->level_estimator = (uint32_t)(
348 effect->session->config.gain_controller2.adaptive_digital.level_estimator);
349 pProperties->extraSaturationMargin =
350 (float)(effect->session->config.gain_controller2.adaptive_digital
351 .extra_saturation_margin_db);
352 break;
353 default:
354 ALOGW("Agc2GetParameter() unknown param %d", param);
355 status = -EINVAL;
356 break;
357 }
358
359 return status;
360 }
361
AgcGetParameter(preproc_effect_t * effect,void * pParam,uint32_t * pValueSize,void * pValue)362 int AgcGetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) {
363 int status = 0;
364 uint32_t param = *(uint32_t*)pParam;
365 t_agc_settings* pProperties = (t_agc_settings*)pValue;
366
367 switch (param) {
368 case AGC_PARAM_TARGET_LEVEL:
369 case AGC_PARAM_COMP_GAIN:
370 if (*pValueSize < sizeof(int16_t)) {
371 *pValueSize = 0;
372 return -EINVAL;
373 }
374 break;
375 case AGC_PARAM_LIMITER_ENA:
376 if (*pValueSize < sizeof(bool)) {
377 *pValueSize = 0;
378 return -EINVAL;
379 }
380 break;
381 case AGC_PARAM_PROPERTIES:
382 if (*pValueSize < sizeof(t_agc_settings)) {
383 *pValueSize = 0;
384 return -EINVAL;
385 }
386 break;
387
388 default:
389 ALOGW("AgcGetParameter() unknown param %08x", param);
390 status = -EINVAL;
391 break;
392 }
393
394 effect->session->config = effect->session->apm->GetConfig();
395 switch (param) {
396 case AGC_PARAM_TARGET_LEVEL:
397 *(int16_t*)pValue =
398 (int16_t)(effect->session->config.gain_controller1.target_level_dbfs * -100);
399 ALOGV("AgcGetParameter() target level %d milliBels", *(int16_t*)pValue);
400 break;
401 case AGC_PARAM_COMP_GAIN:
402 *(int16_t*)pValue =
403 (int16_t)(effect->session->config.gain_controller1.compression_gain_db * -100);
404 ALOGV("AgcGetParameter() comp gain %d milliBels", *(int16_t*)pValue);
405 break;
406 case AGC_PARAM_LIMITER_ENA:
407 *(bool*)pValue = (bool)(effect->session->config.gain_controller1.enable_limiter);
408 ALOGV("AgcGetParameter() limiter enabled %s",
409 (*(int16_t*)pValue != 0) ? "true" : "false");
410 break;
411 case AGC_PARAM_PROPERTIES:
412 pProperties->targetLevel =
413 (int16_t)(effect->session->config.gain_controller1.target_level_dbfs * -100);
414 pProperties->compGain =
415 (int16_t)(effect->session->config.gain_controller1.compression_gain_db * -100);
416 pProperties->limiterEnabled =
417 (bool)(effect->session->config.gain_controller1.enable_limiter);
418 break;
419 default:
420 ALOGW("AgcGetParameter() unknown param %d", param);
421 status = -EINVAL;
422 break;
423 }
424 return status;
425 }
426
Agc2SetParameter(preproc_effect_t * effect,void * pParam,void * pValue)427 int Agc2SetParameter(preproc_effect_t* effect, void* pParam, void* pValue) {
428 int status = 0;
429 uint32_t param = *(uint32_t*)pParam;
430 float valueFloat = 0.f;
431 agc2_settings_t* pProperties = (agc2_settings_t*)pValue;
432 effect->session->config = effect->session->apm->GetConfig();
433 switch (param) {
434 case AGC2_PARAM_FIXED_DIGITAL_GAIN:
435 valueFloat = (float)(*(int32_t*)pValue);
436 ALOGV("Agc2SetParameter() fixed digital gain %f dB", valueFloat);
437 effect->session->config.gain_controller2.fixed_digital.gain_db = valueFloat;
438 break;
439 case AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR:
440 ALOGV("Agc2SetParameter() level estimator %d",
441 *(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
442 effect->session->config.gain_controller2.adaptive_digital.level_estimator =
443 (*(webrtc::AudioProcessing::Config::GainController2::LevelEstimator*)pValue);
444 break;
445 case AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN:
446 valueFloat = (float)(*(int32_t*)pValue);
447 ALOGV("Agc2SetParameter() extra saturation margin %f dB", valueFloat);
448 effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
449 valueFloat;
450 break;
451 case AGC2_PARAM_PROPERTIES:
452 ALOGV("Agc2SetParameter() properties gain %f, level %d margin %f",
453 pProperties->fixedDigitalGain, pProperties->level_estimator,
454 pProperties->extraSaturationMargin);
455 effect->session->config.gain_controller2.fixed_digital.gain_db =
456 pProperties->fixedDigitalGain;
457 effect->session->config.gain_controller2.adaptive_digital.level_estimator =
458 (webrtc::AudioProcessing::Config::GainController2::LevelEstimator)
459 pProperties->level_estimator;
460 effect->session->config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
461 pProperties->extraSaturationMargin;
462 break;
463 default:
464 ALOGW("Agc2SetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue);
465 status = -EINVAL;
466 break;
467 }
468 effect->session->apm->ApplyConfig(effect->session->config);
469
470 ALOGV("Agc2SetParameter() done status %d", status);
471
472 return status;
473 }
474
AgcSetParameter(preproc_effect_t * effect,void * pParam,void * pValue)475 int AgcSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) {
476 int status = 0;
477 uint32_t param = *(uint32_t*)pParam;
478 t_agc_settings* pProperties = (t_agc_settings*)pValue;
479 effect->session->config = effect->session->apm->GetConfig();
480 switch (param) {
481 case AGC_PARAM_TARGET_LEVEL:
482 ALOGV("AgcSetParameter() target level %d milliBels", *(int16_t*)pValue);
483 effect->session->config.gain_controller1.target_level_dbfs =
484 (-(*(int16_t*)pValue / 100));
485 break;
486 case AGC_PARAM_COMP_GAIN:
487 ALOGV("AgcSetParameter() comp gain %d milliBels", *(int16_t*)pValue);
488 effect->session->config.gain_controller1.compression_gain_db =
489 (*(int16_t*)pValue / 100);
490 break;
491 case AGC_PARAM_LIMITER_ENA:
492 ALOGV("AgcSetParameter() limiter enabled %s", *(bool*)pValue ? "true" : "false");
493 effect->session->config.gain_controller1.enable_limiter = (*(bool*)pValue);
494 break;
495 case AGC_PARAM_PROPERTIES:
496 ALOGV("AgcSetParameter() properties level %d, gain %d limiter %d",
497 pProperties->targetLevel, pProperties->compGain, pProperties->limiterEnabled);
498 effect->session->config.gain_controller1.target_level_dbfs =
499 -(pProperties->targetLevel / 100);
500 effect->session->config.gain_controller1.compression_gain_db =
501 pProperties->compGain / 100;
502 effect->session->config.gain_controller1.enable_limiter = pProperties->limiterEnabled;
503 break;
504 default:
505 ALOGW("AgcSetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue);
506 status = -EINVAL;
507 break;
508 }
509 effect->session->apm->ApplyConfig(effect->session->config);
510
511 ALOGV("AgcSetParameter() done status %d", status);
512
513 return status;
514 }
515
Agc2Enable(preproc_effect_t * effect)516 void Agc2Enable(preproc_effect_t* effect) {
517 effect->session->config = effect->session->apm->GetConfig();
518 effect->session->config.gain_controller2.enabled = true;
519 effect->session->apm->ApplyConfig(effect->session->config);
520 }
521
AgcEnable(preproc_effect_t * effect)522 void AgcEnable(preproc_effect_t* effect) {
523 effect->session->config = effect->session->apm->GetConfig();
524 effect->session->config.gain_controller1.enabled = true;
525 effect->session->apm->ApplyConfig(effect->session->config);
526 }
527
Agc2Disable(preproc_effect_t * effect)528 void Agc2Disable(preproc_effect_t* effect) {
529 effect->session->config = effect->session->apm->GetConfig();
530 effect->session->config.gain_controller2.enabled = false;
531 effect->session->apm->ApplyConfig(effect->session->config);
532 }
533
AgcDisable(preproc_effect_t * effect)534 void AgcDisable(preproc_effect_t* effect) {
535 effect->session->config = effect->session->apm->GetConfig();
536 effect->session->config.gain_controller1.enabled = false;
537 effect->session->apm->ApplyConfig(effect->session->config);
538 }
539
540 static const preproc_ops_t sAgcOps = {AgcCreate, AgcInit, NULL, AgcEnable, AgcDisable,
541 AgcSetParameter, AgcGetParameter, NULL};
542
543 static const preproc_ops_t sAgc2Ops = {Agc2Create, Agc2Init, NULL,
544 Agc2Enable, Agc2Disable, Agc2SetParameter,
545 Agc2GetParameter, NULL};
546
547 //------------------------------------------------------------------------------
548 // Acoustic Echo Canceler (AEC)
549 //------------------------------------------------------------------------------
550
551
AecInit(preproc_effect_t * effect)552 int AecInit(preproc_effect_t* effect) {
553 ALOGV("AecInit");
554 effect->session->config = effect->session->apm->GetConfig();
555 effect->session->config.echo_canceller.mobile_mode = true;
556 effect->session->apm->ApplyConfig(effect->session->config);
557 return 0;
558 }
559
AecCreate(preproc_effect_t * effect)560 int AecCreate(preproc_effect_t* effect) {
561 AecInit(effect);
562 return 0;
563 }
564
AecGetParameter(preproc_effect_t * effect,void * pParam,uint32_t * pValueSize,void * pValue)565 int AecGetParameter(preproc_effect_t* effect, void* pParam, uint32_t* pValueSize, void* pValue) {
566 int status = 0;
567 uint32_t param = *(uint32_t*)pParam;
568
569 if (*pValueSize < sizeof(uint32_t)) {
570 return -EINVAL;
571 }
572 switch (param) {
573 case AEC_PARAM_ECHO_DELAY:
574 case AEC_PARAM_PROPERTIES:
575 *(uint32_t*)pValue = 1000 * effect->session->apm->stream_delay_ms();
576 ALOGV("AecGetParameter() echo delay %d us", *(uint32_t*)pValue);
577 break;
578 case AEC_PARAM_MOBILE_MODE:
579 effect->session->config = effect->session->apm->GetConfig();
580 *(uint32_t*)pValue = effect->session->config.echo_canceller.mobile_mode;
581 ALOGV("AecGetParameter() mobile mode %d us", *(uint32_t*)pValue);
582 break;
583 default:
584 ALOGW("AecGetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue);
585 status = -EINVAL;
586 break;
587 }
588 return status;
589 }
590
AecSetParameter(preproc_effect_t * effect,void * pParam,void * pValue)591 int AecSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) {
592 int status = 0;
593 uint32_t param = *(uint32_t*)pParam;
594 uint32_t value = *(uint32_t*)pValue;
595
596 switch (param) {
597 case AEC_PARAM_ECHO_DELAY:
598 case AEC_PARAM_PROPERTIES:
599 status = effect->session->apm->set_stream_delay_ms(value / 1000);
600 ALOGV("AecSetParameter() echo delay %d us, status %d", value, status);
601 break;
602 case AEC_PARAM_MOBILE_MODE:
603 effect->session->config = effect->session->apm->GetConfig();
604 effect->session->config.echo_canceller.mobile_mode = value;
605 ALOGV("AecSetParameter() mobile mode %d us", value);
606 effect->session->apm->ApplyConfig(effect->session->config);
607 break;
608 default:
609 ALOGW("AecSetParameter() unknown param %08x value %08x", param, *(uint32_t*)pValue);
610 status = -EINVAL;
611 break;
612 }
613 return status;
614 }
615
AecEnable(preproc_effect_t * effect)616 void AecEnable(preproc_effect_t* effect) {
617 effect->session->config = effect->session->apm->GetConfig();
618 effect->session->config.echo_canceller.enabled = true;
619 effect->session->apm->ApplyConfig(effect->session->config);
620 }
621
AecDisable(preproc_effect_t * effect)622 void AecDisable(preproc_effect_t* effect) {
623 effect->session->config = effect->session->apm->GetConfig();
624 effect->session->config.echo_canceller.enabled = false;
625 effect->session->apm->ApplyConfig(effect->session->config);
626 }
627
AecSetDevice(preproc_effect_t * effect,uint32_t device)628 int AecSetDevice(preproc_effect_t* effect, uint32_t device) {
629 ALOGV("AecSetDevice %08x", device);
630
631 if (audio_is_input_device(device)) {
632 return 0;
633 }
634
635 return 0;
636 }
637
638 static const preproc_ops_t sAecOps = {AecCreate, AecInit, NULL,
639 AecEnable, AecDisable, AecSetParameter,
640 AecGetParameter, AecSetDevice};
641
642 //------------------------------------------------------------------------------
643 // Noise Suppression (NS)
644 //------------------------------------------------------------------------------
645
646 static const webrtc::AudioProcessing::Config::NoiseSuppression::Level kNsDefaultLevel =
647 webrtc::AudioProcessing::Config::NoiseSuppression::kModerate;
648
NsInit(preproc_effect_t * effect)649 int NsInit(preproc_effect_t* effect) {
650 ALOGV("NsInit");
651 effect->session->config = effect->session->apm->GetConfig();
652 effect->session->config.noise_suppression.level = kNsDefaultLevel;
653 effect->session->apm->ApplyConfig(effect->session->config);
654 effect->type = NS_TYPE_SINGLE_CHANNEL;
655 return 0;
656 }
657
NsCreate(preproc_effect_t * effect)658 int NsCreate(preproc_effect_t* effect) {
659 NsInit(effect);
660 return 0;
661 }
662
NsGetParameter(preproc_effect_t *,void *,uint32_t *,void *)663 int NsGetParameter(preproc_effect_t* /*effect __unused*/, void* /*pParam __unused*/,
664 uint32_t* /*pValueSize __unused*/, void* /*pValue __unused*/) {
665 int status = 0;
666 return status;
667 }
668
NsSetParameter(preproc_effect_t * effect,void * pParam,void * pValue)669 int NsSetParameter(preproc_effect_t* effect, void* pParam, void* pValue) {
670 int status = 0;
671 uint32_t param = *(uint32_t*)pParam;
672 uint32_t value = *(uint32_t*)pValue;
673 effect->session->config = effect->session->apm->GetConfig();
674 switch (param) {
675 case NS_PARAM_LEVEL:
676 effect->session->config.noise_suppression.level =
677 (webrtc::AudioProcessing::Config::NoiseSuppression::Level)value;
678 ALOGV("NsSetParameter() level %d", value);
679 break;
680 default:
681 ALOGW("NsSetParameter() unknown param %08x value %08x", param, value);
682 status = -EINVAL;
683 }
684 effect->session->apm->ApplyConfig(effect->session->config);
685
686 return status;
687 }
688
NsEnable(preproc_effect_t * effect)689 void NsEnable(preproc_effect_t* effect) {
690 effect->session->config = effect->session->apm->GetConfig();
691 effect->session->config.noise_suppression.enabled = true;
692 effect->session->apm->ApplyConfig(effect->session->config);
693 }
694
NsDisable(preproc_effect_t * effect)695 void NsDisable(preproc_effect_t* effect) {
696 ALOGV("NsDisable");
697 effect->session->config = effect->session->apm->GetConfig();
698 effect->session->config.noise_suppression.enabled = false;
699 effect->session->apm->ApplyConfig(effect->session->config);
700 }
701
702 static const preproc_ops_t sNsOps = {NsCreate, NsInit, NULL, NsEnable,
703 NsDisable, NsSetParameter, NsGetParameter, NULL};
704
705 static const preproc_ops_t* sPreProcOps[PREPROC_NUM_EFFECTS] = {&sAgcOps,
706 &sAgc2Ops,
707 &sAecOps, &sNsOps};
708
709 //------------------------------------------------------------------------------
710 // Effect functions
711 //------------------------------------------------------------------------------
712
713 void Session_SetProcEnabled(preproc_session_t* session, uint32_t procId, bool enabled);
714
715 extern "C" const struct effect_interface_s sEffectInterface;
716 extern "C" const struct effect_interface_s sEffectInterfaceReverse;
717
718 #define BAD_STATE_ABORT(from, to) LOG_ALWAYS_FATAL("Bad state transition from %d to %d", from, to);
719
Effect_SetState(preproc_effect_t * effect,uint32_t state)720 int Effect_SetState(preproc_effect_t* effect, uint32_t state) {
721 int status = 0;
722 ALOGV("Effect_SetState proc %d, new %d old %d", effect->procId, state, effect->state);
723 switch (state) {
724 case PREPROC_EFFECT_STATE_INIT:
725 switch (effect->state) {
726 case PREPROC_EFFECT_STATE_ACTIVE:
727 effect->ops->disable(effect);
728 Session_SetProcEnabled(effect->session, effect->procId, false);
729 break;
730 case PREPROC_EFFECT_STATE_CONFIG:
731 case PREPROC_EFFECT_STATE_CREATED:
732 case PREPROC_EFFECT_STATE_INIT:
733 break;
734 default:
735 BAD_STATE_ABORT(effect->state, state);
736 }
737 break;
738 case PREPROC_EFFECT_STATE_CREATED:
739 switch (effect->state) {
740 case PREPROC_EFFECT_STATE_INIT:
741 status = effect->ops->create(effect);
742 break;
743 case PREPROC_EFFECT_STATE_CREATED:
744 case PREPROC_EFFECT_STATE_ACTIVE:
745 case PREPROC_EFFECT_STATE_CONFIG:
746 ALOGE("Effect_SetState invalid transition");
747 status = -ENOSYS;
748 break;
749 default:
750 BAD_STATE_ABORT(effect->state, state);
751 }
752 break;
753 case PREPROC_EFFECT_STATE_CONFIG:
754 switch (effect->state) {
755 case PREPROC_EFFECT_STATE_INIT:
756 ALOGE("Effect_SetState invalid transition");
757 status = -ENOSYS;
758 break;
759 case PREPROC_EFFECT_STATE_ACTIVE:
760 effect->ops->disable(effect);
761 Session_SetProcEnabled(effect->session, effect->procId, false);
762 break;
763 case PREPROC_EFFECT_STATE_CREATED:
764 case PREPROC_EFFECT_STATE_CONFIG:
765 break;
766 default:
767 BAD_STATE_ABORT(effect->state, state);
768 }
769 break;
770 case PREPROC_EFFECT_STATE_ACTIVE:
771 switch (effect->state) {
772 case PREPROC_EFFECT_STATE_INIT:
773 case PREPROC_EFFECT_STATE_CREATED:
774 ALOGE("Effect_SetState invalid transition");
775 status = -ENOSYS;
776 break;
777 case PREPROC_EFFECT_STATE_ACTIVE:
778 // enabling an already enabled effect is just ignored
779 break;
780 case PREPROC_EFFECT_STATE_CONFIG:
781 effect->ops->enable(effect);
782 Session_SetProcEnabled(effect->session, effect->procId, true);
783 break;
784 default:
785 BAD_STATE_ABORT(effect->state, state);
786 }
787 break;
788 default:
789 BAD_STATE_ABORT(effect->state, state);
790 }
791 if (status == 0) {
792 effect->state = state;
793 }
794 return status;
795 }
796
Effect_Init(preproc_effect_t * effect,uint32_t procId)797 int Effect_Init(preproc_effect_t* effect, uint32_t procId) {
798 if (HasReverseStream(procId)) {
799 effect->itfe = &sEffectInterfaceReverse;
800 } else {
801 effect->itfe = &sEffectInterface;
802 }
803 effect->ops = sPreProcOps[procId];
804 effect->procId = procId;
805 effect->state = PREPROC_EFFECT_STATE_INIT;
806 return 0;
807 }
808
Effect_Create(preproc_effect_t * effect,preproc_session_t * session,effect_handle_t * interface)809 int Effect_Create(preproc_effect_t* effect, preproc_session_t* session,
810 effect_handle_t* interface) {
811 effect->session = session;
812 *interface = (effect_handle_t)&effect->itfe;
813 return Effect_SetState(effect, PREPROC_EFFECT_STATE_CREATED);
814 }
815
Effect_Release(preproc_effect_t * effect)816 int Effect_Release(preproc_effect_t* effect) {
817 return Effect_SetState(effect, PREPROC_EFFECT_STATE_INIT);
818 }
819
820 //------------------------------------------------------------------------------
821 // Session functions
822 //------------------------------------------------------------------------------
823
824 #define RESAMPLER_QUALITY SPEEX_RESAMPLER_QUALITY_VOIP
825
826 static const int kPreprocDefaultSr = 16000;
827 static const int kPreProcDefaultCnl = 1;
828
Session_Init(preproc_session_t * session)829 int Session_Init(preproc_session_t* session) {
830 size_t i;
831 int status = 0;
832
833 session->state = PREPROC_SESSION_STATE_INIT;
834 session->id = 0;
835 session->io = 0;
836 session->createdMsk = 0;
837 for (i = 0; i < PREPROC_NUM_EFFECTS && status == 0; i++) {
838 status = Effect_Init(&session->effects[i], i);
839 }
840 return status;
841 }
842
Session_CreateEffect(preproc_session_t * session,int32_t procId,effect_handle_t * interface)843 extern "C" int Session_CreateEffect(preproc_session_t* session, int32_t procId,
844 effect_handle_t* interface) {
845 int status = -ENOMEM;
846
847 ALOGV("Session_CreateEffect procId %d, createdMsk %08x", procId, session->createdMsk);
848
849 if (session->createdMsk == 0) {
850 session->apm = session->ap_builder.Create();
851 if (session->apm == NULL) {
852 ALOGW("Session_CreateEffect could not get apm engine");
853 goto error;
854 }
855 session->frameCount = kPreprocDefaultSr / 100;
856 session->samplingRate = kPreprocDefaultSr;
857 session->inChannelCount = kPreProcDefaultCnl;
858 session->outChannelCount = kPreProcDefaultCnl;
859 session->inputConfig.set_sample_rate_hz(kPreprocDefaultSr);
860 session->inputConfig.set_num_channels(kPreProcDefaultCnl);
861 session->outputConfig.set_sample_rate_hz(kPreprocDefaultSr);
862 session->outputConfig.set_num_channels(kPreProcDefaultCnl);
863 session->revChannelCount = kPreProcDefaultCnl;
864 session->revConfig.set_sample_rate_hz(kPreprocDefaultSr);
865 session->revConfig.set_num_channels(kPreProcDefaultCnl);
866 session->enabledMsk = 0;
867 session->processedMsk = 0;
868 session->revEnabledMsk = 0;
869 session->revProcessedMsk = 0;
870 }
871 status = Effect_Create(&session->effects[procId], session, interface);
872 if (status < 0) {
873 goto error;
874 }
875 ALOGV("Session_CreateEffect OK");
876 session->createdMsk |= (1 << procId);
877 return status;
878
879 error:
880 if (session->createdMsk == 0) {
881 delete session->apm;
882 session->apm = NULL;
883 }
884 return status;
885 }
886
Session_ReleaseEffect(preproc_session_t * session,preproc_effect_t * fx)887 int Session_ReleaseEffect(preproc_session_t* session, preproc_effect_t* fx) {
888 ALOGW_IF(Effect_Release(fx) != 0, " Effect_Release() failed for proc ID %d", fx->procId);
889 session->createdMsk &= ~(1 << fx->procId);
890 if (session->createdMsk == 0) {
891 delete session->apm;
892 session->apm = NULL;
893 session->id = 0;
894 }
895
896 return 0;
897 }
898
Session_SetConfig(preproc_session_t * session,effect_config_t * config)899 int Session_SetConfig(preproc_session_t* session, effect_config_t* config) {
900 uint32_t inCnl = audio_channel_count_from_in_mask(config->inputCfg.channels);
901 uint32_t outCnl = audio_channel_count_from_in_mask(config->outputCfg.channels);
902
903 if (config->inputCfg.samplingRate != config->outputCfg.samplingRate ||
904 config->inputCfg.format != config->outputCfg.format ||
905 config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
906 return -EINVAL;
907 }
908
909 ALOGV("Session_SetConfig sr %d cnl %08x", config->inputCfg.samplingRate,
910 config->inputCfg.channels);
911
912 session->samplingRate = config->inputCfg.samplingRate;
913 session->frameCount = session->samplingRate / 100;
914 session->inChannelCount = inCnl;
915 session->outChannelCount = outCnl;
916 session->inputConfig.set_sample_rate_hz(session->samplingRate);
917 session->inputConfig.set_num_channels(inCnl);
918 session->outputConfig.set_sample_rate_hz(session->samplingRate);
919 session->outputConfig.set_num_channels(inCnl);
920
921 session->revChannelCount = inCnl;
922 session->revConfig.set_sample_rate_hz(session->samplingRate);
923 session->revConfig.set_num_channels(inCnl);
924
925 session->state = PREPROC_SESSION_STATE_CONFIG;
926 return 0;
927 }
928
Session_GetConfig(preproc_session_t * session,effect_config_t * config)929 void Session_GetConfig(preproc_session_t* session, effect_config_t* config) {
930 memset(config, 0, sizeof(effect_config_t));
931 config->inputCfg.samplingRate = config->outputCfg.samplingRate = session->samplingRate;
932 config->inputCfg.format = config->outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
933 config->inputCfg.channels = audio_channel_in_mask_from_count(session->inChannelCount);
934 // "out" doesn't mean output device, so this is the correct API to convert channel count to mask
935 config->outputCfg.channels = audio_channel_in_mask_from_count(session->outChannelCount);
936 config->inputCfg.mask = config->outputCfg.mask =
937 (EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT);
938 }
939
Session_SetReverseConfig(preproc_session_t * session,effect_config_t * config)940 int Session_SetReverseConfig(preproc_session_t* session, effect_config_t* config) {
941 if (config->inputCfg.samplingRate != config->outputCfg.samplingRate ||
942 config->inputCfg.format != config->outputCfg.format ||
943 config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
944 return -EINVAL;
945 }
946
947 ALOGV("Session_SetReverseConfig sr %d cnl %08x", config->inputCfg.samplingRate,
948 config->inputCfg.channels);
949
950 if (session->state < PREPROC_SESSION_STATE_CONFIG) {
951 return -ENOSYS;
952 }
953 if (config->inputCfg.samplingRate != session->samplingRate ||
954 config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
955 return -EINVAL;
956 }
957 uint32_t inCnl = audio_channel_count_from_out_mask(config->inputCfg.channels);
958 session->revChannelCount = inCnl;
959
960 return 0;
961 }
962
Session_GetReverseConfig(preproc_session_t * session,effect_config_t * config)963 void Session_GetReverseConfig(preproc_session_t* session, effect_config_t* config) {
964 memset(config, 0, sizeof(effect_config_t));
965 config->inputCfg.samplingRate = config->outputCfg.samplingRate = session->samplingRate;
966 config->inputCfg.format = config->outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
967 config->inputCfg.channels = config->outputCfg.channels =
968 audio_channel_in_mask_from_count(session->revChannelCount);
969 config->inputCfg.mask = config->outputCfg.mask =
970 (EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT);
971 }
972
Session_SetProcEnabled(preproc_session_t * session,uint32_t procId,bool enabled)973 void Session_SetProcEnabled(preproc_session_t* session, uint32_t procId, bool enabled) {
974 if (enabled) {
975 session->enabledMsk |= (1 << procId);
976 if (HasReverseStream(procId)) {
977 session->revEnabledMsk |= (1 << procId);
978 }
979 } else {
980 session->enabledMsk &= ~(1 << procId);
981 if (HasReverseStream(procId)) {
982 session->revEnabledMsk &= ~(1 << procId);
983 }
984 }
985 ALOGV("Session_SetProcEnabled proc %d, enabled %d enabledMsk %08x revEnabledMsk %08x", procId,
986 enabled, session->enabledMsk, session->revEnabledMsk);
987 session->processedMsk = 0;
988 if (HasReverseStream(procId)) {
989 session->revProcessedMsk = 0;
990 }
991 }
992
993 //------------------------------------------------------------------------------
994 // Bundle functions
995 //------------------------------------------------------------------------------
996
997 static int sInitStatus = 1;
998 static preproc_session_t sSessions[PREPROC_NUM_SESSIONS];
999
PreProc_GetSession(int32_t procId,int32_t sessionId,int32_t ioId)1000 preproc_session_t* PreProc_GetSession(int32_t procId, int32_t sessionId, int32_t ioId) {
1001 size_t i;
1002 for (i = 0; i < PREPROC_NUM_SESSIONS; i++) {
1003 if (sSessions[i].id == sessionId) {
1004 if (sSessions[i].createdMsk & (1 << procId)) {
1005 return NULL;
1006 }
1007 return &sSessions[i];
1008 }
1009 }
1010 for (i = 0; i < PREPROC_NUM_SESSIONS; i++) {
1011 if (sSessions[i].id == 0) {
1012 sSessions[i].id = sessionId;
1013 sSessions[i].io = ioId;
1014 return &sSessions[i];
1015 }
1016 }
1017 return NULL;
1018 }
1019
PreProc_Init()1020 int PreProc_Init() {
1021 size_t i;
1022 int status = 0;
1023
1024 if (sInitStatus <= 0) {
1025 return sInitStatus;
1026 }
1027 for (i = 0; i < PREPROC_NUM_SESSIONS && status == 0; i++) {
1028 status = Session_Init(&sSessions[i]);
1029 }
1030 sInitStatus = status;
1031 return sInitStatus;
1032 }
1033
PreProc_GetDescriptor(const effect_uuid_t * uuid)1034 const effect_descriptor_t* PreProc_GetDescriptor(const effect_uuid_t* uuid) {
1035 size_t i;
1036 for (i = 0; i < PREPROC_NUM_EFFECTS; i++) {
1037 if (memcmp(&sDescriptors[i]->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
1038 return sDescriptors[i];
1039 }
1040 }
1041 return NULL;
1042 }
1043
1044 extern "C" {
1045
1046 //------------------------------------------------------------------------------
1047 // Effect Control Interface Implementation
1048 //------------------------------------------------------------------------------
1049
PreProcessingFx_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)1050 int PreProcessingFx_Process(effect_handle_t self, audio_buffer_t* inBuffer,
1051 audio_buffer_t* outBuffer) {
1052 preproc_effect_t* effect = (preproc_effect_t*)self;
1053
1054 if (effect == NULL) {
1055 ALOGV("PreProcessingFx_Process() ERROR effect == NULL");
1056 return -EINVAL;
1057 }
1058 preproc_session_t* session = (preproc_session_t*)effect->session;
1059
1060 if (inBuffer == NULL || inBuffer->raw == NULL || outBuffer == NULL || outBuffer->raw == NULL) {
1061 ALOGW("PreProcessingFx_Process() ERROR bad pointer");
1062 return -EINVAL;
1063 }
1064
1065 if (inBuffer->frameCount != outBuffer->frameCount) {
1066 ALOGW("inBuffer->frameCount %zu is not equal to outBuffer->frameCount %zu",
1067 inBuffer->frameCount, outBuffer->frameCount);
1068 return -EINVAL;
1069 }
1070
1071 if (inBuffer->frameCount != session->frameCount) {
1072 ALOGW("inBuffer->frameCount %zu != %zu representing 10ms at sampling rate %d",
1073 inBuffer->frameCount, session->frameCount, session->samplingRate);
1074 return -EINVAL;
1075 }
1076
1077 session->processedMsk |= (1 << effect->procId);
1078
1079 // ALOGV("PreProcessingFx_Process In %d frames enabledMsk %08x processedMsk %08x",
1080 // inBuffer->frameCount, session->enabledMsk, session->processedMsk);
1081 if ((session->processedMsk & session->enabledMsk) == session->enabledMsk) {
1082 effect->session->processedMsk = 0;
1083 if (int status = effect->session->apm->ProcessStream(
1084 (const int16_t* const)inBuffer->s16,
1085 (const webrtc::StreamConfig)effect->session->inputConfig,
1086 (const webrtc::StreamConfig)effect->session->outputConfig,
1087 (int16_t* const)outBuffer->s16);
1088 status != 0) {
1089 ALOGE("Process Stream failed with error %d\n", status);
1090 return status;
1091 }
1092 return 0;
1093 } else {
1094 return -ENODATA;
1095 }
1096 }
1097
PreProcessingFx_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)1098 int PreProcessingFx_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
1099 void* pCmdData, uint32_t* replySize, void* pReplyData) {
1100 preproc_effect_t* effect = (preproc_effect_t*)self;
1101
1102 if (effect == NULL) {
1103 return -EINVAL;
1104 }
1105
1106 // ALOGV("PreProcessingFx_Command: command %d cmdSize %d",cmdCode, cmdSize);
1107
1108 switch (cmdCode) {
1109 case EFFECT_CMD_INIT:
1110 if (pReplyData == NULL || *replySize != sizeof(int)) {
1111 return -EINVAL;
1112 }
1113 if (effect->ops->init) {
1114 effect->ops->init(effect);
1115 }
1116 *(int*)pReplyData = 0;
1117 break;
1118
1119 case EFFECT_CMD_SET_CONFIG: {
1120 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) || pReplyData == NULL ||
1121 *replySize != sizeof(int)) {
1122 ALOGV("PreProcessingFx_Command cmdCode Case: "
1123 "EFFECT_CMD_SET_CONFIG: ERROR");
1124 return -EINVAL;
1125 }
1126 #ifdef DUAL_MIC_TEST
1127 // make sure that the config command is accepted by making as if all effects were
1128 // disabled: this is OK for functional tests
1129 uint32_t enabledMsk = effect->session->enabledMsk;
1130 if (gDualMicEnabled) {
1131 effect->session->enabledMsk = 0;
1132 }
1133 #endif
1134 *(int*)pReplyData = Session_SetConfig(effect->session, (effect_config_t*)pCmdData);
1135 #ifdef DUAL_MIC_TEST
1136 if (gDualMicEnabled) {
1137 effect->session->enabledMsk = enabledMsk;
1138 }
1139 #endif
1140 if (*(int*)pReplyData != 0) {
1141 break;
1142 }
1143 if (effect->state != PREPROC_EFFECT_STATE_ACTIVE) {
1144 *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG);
1145 }
1146 } break;
1147
1148 case EFFECT_CMD_GET_CONFIG:
1149 if (pReplyData == NULL || *replySize != sizeof(effect_config_t)) {
1150 ALOGV("\tLVM_ERROR : PreProcessingFx_Command cmdCode Case: "
1151 "EFFECT_CMD_GET_CONFIG: ERROR");
1152 return -EINVAL;
1153 }
1154
1155 Session_GetConfig(effect->session, (effect_config_t*)pReplyData);
1156 break;
1157
1158 case EFFECT_CMD_SET_CONFIG_REVERSE:
1159 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) || pReplyData == NULL ||
1160 *replySize != sizeof(int)) {
1161 ALOGV("PreProcessingFx_Command cmdCode Case: "
1162 "EFFECT_CMD_SET_CONFIG_REVERSE: ERROR");
1163 return -EINVAL;
1164 }
1165 *(int*)pReplyData =
1166 Session_SetReverseConfig(effect->session, (effect_config_t*)pCmdData);
1167 if (*(int*)pReplyData != 0) {
1168 break;
1169 }
1170 break;
1171
1172 case EFFECT_CMD_GET_CONFIG_REVERSE:
1173 if (pReplyData == NULL || *replySize != sizeof(effect_config_t)) {
1174 ALOGV("PreProcessingFx_Command cmdCode Case: "
1175 "EFFECT_CMD_GET_CONFIG_REVERSE: ERROR");
1176 return -EINVAL;
1177 }
1178 Session_GetReverseConfig(effect->session, (effect_config_t*)pCmdData);
1179 break;
1180
1181 case EFFECT_CMD_RESET:
1182 if (effect->ops->reset) {
1183 effect->ops->reset(effect);
1184 }
1185 break;
1186
1187 case EFFECT_CMD_GET_PARAM: {
1188 effect_param_t* p = (effect_param_t*)pCmdData;
1189
1190 if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
1191 cmdSize < (sizeof(effect_param_t) + p->psize) || pReplyData == NULL ||
1192 replySize == NULL || *replySize < (sizeof(effect_param_t) + p->psize)) {
1193 ALOGV("PreProcessingFx_Command cmdCode Case: "
1194 "EFFECT_CMD_GET_PARAM: ERROR");
1195 return -EINVAL;
1196 }
1197
1198 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize);
1199
1200 p = (effect_param_t*)pReplyData;
1201
1202 int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
1203
1204 if (effect->ops->get_parameter) {
1205 p->status =
1206 effect->ops->get_parameter(effect, p->data, &p->vsize, p->data + voffset);
1207 *replySize = sizeof(effect_param_t) + voffset + p->vsize;
1208 }
1209 } break;
1210
1211 case EFFECT_CMD_SET_PARAM: {
1212 if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) || pReplyData == NULL ||
1213 replySize == NULL || *replySize != sizeof(int32_t)) {
1214 ALOGV("PreProcessingFx_Command cmdCode Case: "
1215 "EFFECT_CMD_SET_PARAM: ERROR");
1216 return -EINVAL;
1217 }
1218 effect_param_t* p = (effect_param_t*)pCmdData;
1219
1220 if (p->psize != sizeof(int32_t)) {
1221 ALOGV("PreProcessingFx_Command cmdCode Case: "
1222 "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
1223 return -EINVAL;
1224 }
1225 if (effect->ops->set_parameter) {
1226 *(int*)pReplyData =
1227 effect->ops->set_parameter(effect, (void*)p->data, p->data + p->psize);
1228 }
1229 } break;
1230
1231 case EFFECT_CMD_ENABLE:
1232 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
1233 ALOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_ENABLE: ERROR");
1234 return -EINVAL;
1235 }
1236 *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_ACTIVE);
1237 break;
1238
1239 case EFFECT_CMD_DISABLE:
1240 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
1241 ALOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_DISABLE: ERROR");
1242 return -EINVAL;
1243 }
1244 *(int*)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG);
1245 break;
1246
1247 case EFFECT_CMD_SET_DEVICE:
1248 case EFFECT_CMD_SET_INPUT_DEVICE:
1249 if (pCmdData == NULL || cmdSize != sizeof(uint32_t)) {
1250 ALOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_SET_DEVICE: ERROR");
1251 return -EINVAL;
1252 }
1253
1254 if (effect->ops->set_device) {
1255 effect->ops->set_device(effect, *(uint32_t*)pCmdData);
1256 }
1257 break;
1258
1259 case EFFECT_CMD_SET_VOLUME:
1260 case EFFECT_CMD_SET_AUDIO_MODE:
1261 break;
1262
1263 #ifdef DUAL_MIC_TEST
1264 ///// test commands start
1265 case PREPROC_CMD_DUAL_MIC_ENABLE: {
1266 if (pCmdData == NULL || cmdSize != sizeof(uint32_t) || pReplyData == NULL ||
1267 replySize == NULL) {
1268 ALOGE("PreProcessingFx_Command cmdCode Case: "
1269 "PREPROC_CMD_DUAL_MIC_ENABLE: ERROR");
1270 *replySize = 0;
1271 return -EINVAL;
1272 }
1273 gDualMicEnabled = *(bool*)pCmdData;
1274 if (gDualMicEnabled) {
1275 effect->aux_channels_on = sHasAuxChannels[effect->procId];
1276 } else {
1277 effect->aux_channels_on = false;
1278 }
1279 effect->cur_channel_config =
1280 (effect->session->inChannelCount == 1) ? CHANNEL_CFG_MONO : CHANNEL_CFG_STEREO;
1281
1282 ALOGV("PREPROC_CMD_DUAL_MIC_ENABLE: %s", gDualMicEnabled ? "enabled" : "disabled");
1283 *replySize = sizeof(int);
1284 *(int*)pReplyData = 0;
1285 } break;
1286 case PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: {
1287 if (pCmdData == NULL || pReplyData == NULL || replySize == NULL) {
1288 ALOGE("PreProcessingFx_Command cmdCode Case: "
1289 "PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: ERROR");
1290 *replySize = 0;
1291 return -EINVAL;
1292 }
1293 pthread_mutex_lock(&gPcmDumpLock);
1294 if (gPcmDumpFh != NULL) {
1295 fclose(gPcmDumpFh);
1296 gPcmDumpFh = NULL;
1297 }
1298 char* path = strndup((char*)pCmdData, cmdSize);
1299 gPcmDumpFh = fopen((char*)path, "wb");
1300 pthread_mutex_unlock(&gPcmDumpLock);
1301 ALOGV("PREPROC_CMD_DUAL_MIC_PCM_DUMP_START: path %s gPcmDumpFh %p", path, gPcmDumpFh);
1302 ALOGE_IF(gPcmDumpFh <= 0, "gPcmDumpFh open error %d %s", errno, strerror(errno));
1303 free(path);
1304 *replySize = sizeof(int);
1305 *(int*)pReplyData = 0;
1306 } break;
1307 case PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP: {
1308 if (pReplyData == NULL || replySize == NULL) {
1309 ALOGE("PreProcessingFx_Command cmdCode Case: "
1310 "PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP: ERROR");
1311 *replySize = 0;
1312 return -EINVAL;
1313 }
1314 pthread_mutex_lock(&gPcmDumpLock);
1315 if (gPcmDumpFh != NULL) {
1316 fclose(gPcmDumpFh);
1317 gPcmDumpFh = NULL;
1318 }
1319 pthread_mutex_unlock(&gPcmDumpLock);
1320 ALOGV("PREPROC_CMD_DUAL_MIC_PCM_DUMP_STOP");
1321 *replySize = sizeof(int);
1322 *(int*)pReplyData = 0;
1323 } break;
1324 ///// test commands end
1325
1326 case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: {
1327 if (!gDualMicEnabled) {
1328 return -EINVAL;
1329 }
1330 if (pCmdData == NULL || cmdSize != 2 * sizeof(uint32_t) || pReplyData == NULL ||
1331 replySize == NULL) {
1332 ALOGE("PreProcessingFx_Command cmdCode Case: "
1333 "EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS: ERROR");
1334 *replySize = 0;
1335 return -EINVAL;
1336 }
1337 if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) {
1338 ALOGV("PreProcessingFx_Command feature EFFECT_FEATURE_AUX_CHANNELS not supported by"
1339 " fx %d",
1340 effect->procId);
1341 *(uint32_t*)pReplyData = -ENOSYS;
1342 *replySize = sizeof(uint32_t);
1343 break;
1344 }
1345 size_t num_configs = *((uint32_t*)pCmdData + 1);
1346 if (*replySize < (2 * sizeof(uint32_t) + num_configs * sizeof(channel_config_t))) {
1347 *replySize = 0;
1348 return -EINVAL;
1349 }
1350
1351 *((uint32_t*)pReplyData + 1) = CHANNEL_CFG_CNT;
1352 if (num_configs < CHANNEL_CFG_CNT ||
1353 *replySize < (2 * sizeof(uint32_t) + CHANNEL_CFG_CNT * sizeof(channel_config_t))) {
1354 *(uint32_t*)pReplyData = -ENOMEM;
1355 } else {
1356 num_configs = CHANNEL_CFG_CNT;
1357 *(uint32_t*)pReplyData = 0;
1358 }
1359 ALOGV("PreProcessingFx_Command EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS num config %d",
1360 num_configs);
1361
1362 *replySize = 2 * sizeof(uint32_t) + num_configs * sizeof(channel_config_t);
1363 *((uint32_t*)pReplyData + 1) = num_configs;
1364 memcpy((uint32_t*)pReplyData + 2, &sDualMicConfigs,
1365 num_configs * sizeof(channel_config_t));
1366 } break;
1367 case EFFECT_CMD_GET_FEATURE_CONFIG:
1368 if (!gDualMicEnabled) {
1369 return -EINVAL;
1370 }
1371 if (pCmdData == NULL || cmdSize != sizeof(uint32_t) || pReplyData == NULL ||
1372 replySize == NULL || *replySize < sizeof(uint32_t) + sizeof(channel_config_t)) {
1373 ALOGE("PreProcessingFx_Command cmdCode Case: "
1374 "EFFECT_CMD_GET_FEATURE_CONFIG: ERROR");
1375 return -EINVAL;
1376 }
1377 if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) {
1378 *(uint32_t*)pReplyData = -ENOSYS;
1379 *replySize = sizeof(uint32_t);
1380 break;
1381 }
1382 ALOGV("PreProcessingFx_Command EFFECT_CMD_GET_FEATURE_CONFIG");
1383 *(uint32_t*)pReplyData = 0;
1384 *replySize = sizeof(uint32_t) + sizeof(channel_config_t);
1385 memcpy((uint32_t*)pReplyData + 1, &sDualMicConfigs[effect->cur_channel_config],
1386 sizeof(channel_config_t));
1387 break;
1388 case EFFECT_CMD_SET_FEATURE_CONFIG: {
1389 ALOGV("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG: "
1390 "gDualMicEnabled %d effect->aux_channels_on %d",
1391 gDualMicEnabled, effect->aux_channels_on);
1392 if (!gDualMicEnabled) {
1393 return -EINVAL;
1394 }
1395 if (pCmdData == NULL || cmdSize != (sizeof(uint32_t) + sizeof(channel_config_t)) ||
1396 pReplyData == NULL || replySize == NULL || *replySize < sizeof(uint32_t)) {
1397 ALOGE("PreProcessingFx_Command cmdCode Case: "
1398 "EFFECT_CMD_SET_FEATURE_CONFIG: ERROR\n"
1399 "pCmdData %p cmdSize %d pReplyData %p replySize %p *replySize %d",
1400 pCmdData, cmdSize, pReplyData, replySize, replySize ? *replySize : -1);
1401 return -EINVAL;
1402 }
1403 *replySize = sizeof(uint32_t);
1404 if (*(uint32_t*)pCmdData != EFFECT_FEATURE_AUX_CHANNELS || !effect->aux_channels_on) {
1405 *(uint32_t*)pReplyData = -ENOSYS;
1406 ALOGV("PreProcessingFx_Command cmdCode Case: "
1407 "EFFECT_CMD_SET_FEATURE_CONFIG: ERROR\n"
1408 "CmdData %d effect->aux_channels_on %d",
1409 *(uint32_t*)pCmdData, effect->aux_channels_on);
1410 break;
1411 }
1412 size_t i;
1413 for (i = 0; i < CHANNEL_CFG_CNT; i++) {
1414 if (memcmp((uint32_t*)pCmdData + 1, &sDualMicConfigs[i],
1415 sizeof(channel_config_t)) == 0) {
1416 break;
1417 }
1418 }
1419 if (i == CHANNEL_CFG_CNT) {
1420 *(uint32_t*)pReplyData = -EINVAL;
1421 ALOGW("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG invalid config"
1422 "[%08x].[%08x]",
1423 *((uint32_t*)pCmdData + 1), *((uint32_t*)pCmdData + 2));
1424 } else {
1425 effect->cur_channel_config = i;
1426 *(uint32_t*)pReplyData = 0;
1427 ALOGV("PreProcessingFx_Command EFFECT_CMD_SET_FEATURE_CONFIG New config"
1428 "[%08x].[%08x]",
1429 sDualMicConfigs[i].main_channels, sDualMicConfigs[i].aux_channels);
1430 }
1431 } break;
1432 #endif
1433 default:
1434 return -EINVAL;
1435 }
1436 return 0;
1437 }
1438
PreProcessingFx_GetDescriptor(effect_handle_t self,effect_descriptor_t * pDescriptor)1439 int PreProcessingFx_GetDescriptor(effect_handle_t self, effect_descriptor_t* pDescriptor) {
1440 preproc_effect_t* effect = (preproc_effect_t*)self;
1441
1442 if (effect == NULL || pDescriptor == NULL) {
1443 return -EINVAL;
1444 }
1445
1446 *pDescriptor = *sDescriptors[effect->procId];
1447
1448 return 0;
1449 }
1450
PreProcessingFx_ProcessReverse(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)1451 int PreProcessingFx_ProcessReverse(effect_handle_t self, audio_buffer_t* inBuffer,
1452 audio_buffer_t* outBuffer) {
1453 preproc_effect_t* effect = (preproc_effect_t*)self;
1454
1455 if (effect == NULL) {
1456 ALOGW("PreProcessingFx_ProcessReverse() ERROR effect == NULL");
1457 return -EINVAL;
1458 }
1459 preproc_session_t* session = (preproc_session_t*)effect->session;
1460
1461 if (inBuffer == NULL || inBuffer->raw == NULL) {
1462 ALOGW("PreProcessingFx_ProcessReverse() ERROR bad pointer");
1463 return -EINVAL;
1464 }
1465
1466 if (inBuffer->frameCount != outBuffer->frameCount) {
1467 ALOGW("inBuffer->frameCount %zu is not equal to outBuffer->frameCount %zu",
1468 inBuffer->frameCount, outBuffer->frameCount);
1469 return -EINVAL;
1470 }
1471
1472 if (inBuffer->frameCount != session->frameCount) {
1473 ALOGW("inBuffer->frameCount %zu != %zu representing 10ms at sampling rate %d",
1474 inBuffer->frameCount, session->frameCount, session->samplingRate);
1475 return -EINVAL;
1476 }
1477
1478 session->revProcessedMsk |= (1 << effect->procId);
1479
1480 // ALOGV("PreProcessingFx_ProcessReverse In %d frames revEnabledMsk %08x revProcessedMsk
1481 // %08x",
1482 // inBuffer->frameCount, session->revEnabledMsk, session->revProcessedMsk);
1483
1484 if ((session->revProcessedMsk & session->revEnabledMsk) == session->revEnabledMsk) {
1485 effect->session->revProcessedMsk = 0;
1486 if (int status = effect->session->apm->ProcessReverseStream(
1487 (const int16_t* const)inBuffer->s16,
1488 (const webrtc::StreamConfig)effect->session->revConfig,
1489 (const webrtc::StreamConfig)effect->session->revConfig,
1490 (int16_t* const)outBuffer->s16);
1491 status != 0) {
1492 ALOGE("Process Reverse Stream failed with error %d\n", status);
1493 return status;
1494 }
1495 return 0;
1496 } else {
1497 return -ENODATA;
1498 }
1499 }
1500
1501 // effect_handle_t interface implementation for effect
1502 const struct effect_interface_s sEffectInterface = {
1503 PreProcessingFx_Process, PreProcessingFx_Command, PreProcessingFx_GetDescriptor, NULL};
1504
1505 const struct effect_interface_s sEffectInterfaceReverse = {
1506 PreProcessingFx_Process, PreProcessingFx_Command, PreProcessingFx_GetDescriptor,
1507 PreProcessingFx_ProcessReverse};
1508
1509 //------------------------------------------------------------------------------
1510 // Effect Library Interface Implementation
1511 //------------------------------------------------------------------------------
1512
PreProcessingLib_Create(const effect_uuid_t * uuid,int32_t sessionId,int32_t ioId,effect_handle_t * pInterface)1513 int PreProcessingLib_Create(const effect_uuid_t* uuid, int32_t sessionId, int32_t ioId,
1514 effect_handle_t* pInterface) {
1515 ALOGV("EffectCreate: uuid: %08x session %d IO: %d", uuid->timeLow, sessionId, ioId);
1516
1517 int status;
1518 const effect_descriptor_t* desc;
1519 preproc_session_t* session;
1520 uint32_t procId;
1521
1522 if (PreProc_Init() != 0) {
1523 return sInitStatus;
1524 }
1525 desc = PreProc_GetDescriptor(uuid);
1526 if (desc == NULL) {
1527 ALOGW("EffectCreate: fx not found uuid: %08x", uuid->timeLow);
1528 return -EINVAL;
1529 }
1530 procId = UuidToProcId(&desc->type);
1531
1532 session = PreProc_GetSession(procId, sessionId, ioId);
1533 if (session == NULL) {
1534 ALOGW("EffectCreate: no more session available");
1535 return -EINVAL;
1536 }
1537
1538 status = Session_CreateEffect(session, procId, pInterface);
1539
1540 if (status < 0 && session->createdMsk == 0) {
1541 session->id = 0;
1542 }
1543 return status;
1544 }
1545
PreProcessingLib_Release(effect_handle_t interface)1546 int PreProcessingLib_Release(effect_handle_t interface) {
1547 ALOGV("EffectRelease start %p", interface);
1548 if (PreProc_Init() != 0) {
1549 return sInitStatus;
1550 }
1551
1552 preproc_effect_t* fx = (preproc_effect_t*)interface;
1553
1554 if (fx->session->id == 0) {
1555 return -EINVAL;
1556 }
1557 return Session_ReleaseEffect(fx->session, fx);
1558 }
1559
PreProcessingLib_GetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)1560 int PreProcessingLib_GetDescriptor(const effect_uuid_t* uuid, effect_descriptor_t* pDescriptor) {
1561 if (pDescriptor == NULL || uuid == NULL) {
1562 return -EINVAL;
1563 }
1564
1565 const effect_descriptor_t* desc = PreProc_GetDescriptor(uuid);
1566 if (desc == NULL) {
1567 ALOGV("PreProcessingLib_GetDescriptor() not found");
1568 return -EINVAL;
1569 }
1570
1571 ALOGV("PreProcessingLib_GetDescriptor() got fx %s", desc->name);
1572
1573 *pDescriptor = *desc;
1574 return 0;
1575 }
1576
1577 // This is the only symbol that needs to be exported
1578 __attribute__((visibility("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
1579 .tag = AUDIO_EFFECT_LIBRARY_TAG,
1580 .version = EFFECT_LIBRARY_API_VERSION,
1581 .name = "Audio Preprocessing Library",
1582 .implementor = "The Android Open Source Project",
1583 .create_effect = PreProcessingLib_Create,
1584 .release_effect = PreProcessingLib_Release,
1585 .get_descriptor = PreProcessingLib_GetDescriptor};
1586
1587 }; // extern "C"
1588