blob: dfc25db70ea8cd4141d719254a0fe3947531a946 [file] [log] [blame]
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -07001/*
2 * Copyright (C) 2013 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#define LOG_TAG "EffectLE"
18//#define LOG_NDEBUG 0
19#include <cutils/log.h>
20#include <assert.h>
21#include <stdlib.h>
22#include <string.h>
23#include <new>
24#include <time.h>
25#include <math.h>
26#include <audio_effects/effect_loudnessenhancer.h>
27#include "dsp/core/dynamic_range_compression.h"
28
29extern "C" {
30
31// effect_handle_t interface implementation for LE effect
32extern const struct effect_interface_s gLEInterface;
33
34// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c
35const effect_descriptor_t gLEDescriptor = {
36 {0xfe3199be, 0xaed0, 0x413f, 0x87bb, {0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}, // type
37 {0xfa415329, 0x2034, 0x4bea, 0xb5dc, {0x5b, 0x38, 0x1c, 0x8d, 0x1e, 0x2c}}, // uuid
38 EFFECT_CONTROL_API_VERSION,
39 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
40 0, // TODO
41 1,
42 "Loudness Enhancer",
43 "The Android Open Source Project",
44};
45
46enum le_state_e {
47 LOUDNESS_ENHANCER_STATE_UNINITIALIZED,
48 LOUDNESS_ENHANCER_STATE_INITIALIZED,
49 LOUDNESS_ENHANCER_STATE_ACTIVE,
50};
51
52struct LoudnessEnhancerContext {
53 const struct effect_interface_s *mItfe;
54 effect_config_t mConfig;
55 uint8_t mState;
56 int32_t mTargetGainmB;// target gain in mB
57 // in this implementation, there is no coupling between the compression on the left and right
58 // channels
59 le_fx::AdaptiveDynamicRangeCompression* mCompressorL;
60 le_fx::AdaptiveDynamicRangeCompression* mCompressorR;
61};
62
63//
64//--- Local functions (not directly used by effect interface)
65//
66
67void LE_reset(LoudnessEnhancerContext *pContext)
68{
69 ALOGV(" > LE_reset(%p)", pContext);
70
71 if ((pContext->mCompressorL != NULL) && (pContext->mCompressorR != NULL)) {
72 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
73 ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
74 pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
75 pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
76 } else {
77 ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext);
78 }
79}
80
81static inline int16_t clamp16(int32_t sample)
82{
83 if ((sample>>15) ^ (sample>>31))
84 sample = 0x7FFF ^ (sample>>31);
85 return sample;
86}
87
88//----------------------------------------------------------------------------
89// LE_setConfig()
90//----------------------------------------------------------------------------
91// Purpose: Set input and output audio configuration.
92//
93// Inputs:
94// pContext: effect engine context
95// pConfig: pointer to effect_config_t structure holding input and output
96// configuration parameters
97//
98// Outputs:
99//
100//----------------------------------------------------------------------------
101
102int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig)
103{
104 ALOGV("LE_setConfig(%p)", pContext);
105
106 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
107 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
108 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
109 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
110 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
111 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
112 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
113
114 pContext->mConfig = *pConfig;
115
116 LE_reset(pContext);
117
118 return 0;
119}
120
121
122//----------------------------------------------------------------------------
123// LE_getConfig()
124//----------------------------------------------------------------------------
125// Purpose: Get input and output audio configuration.
126//
127// Inputs:
128// pContext: effect engine context
129// pConfig: pointer to effect_config_t structure holding input and output
130// configuration parameters
131//
132// Outputs:
133//
134//----------------------------------------------------------------------------
135
136void LE_getConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig)
137{
138 *pConfig = pContext->mConfig;
139}
140
141
142//----------------------------------------------------------------------------
143// LE_init()
144//----------------------------------------------------------------------------
145// Purpose: Initialize engine with default configuration.
146//
147// Inputs:
148// pContext: effect engine context
149//
150// Outputs:
151//
152//----------------------------------------------------------------------------
153
154int LE_init(LoudnessEnhancerContext *pContext)
155{
156 ALOGV("LE_init(%p)", pContext);
157
158 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
159 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
160 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
161 pContext->mConfig.inputCfg.samplingRate = 44100;
162 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
163 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
164 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
165 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
166 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
167 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
168 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
169 pContext->mConfig.outputCfg.samplingRate = 44100;
170 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
171 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
172 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
173 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
174
175 pContext->mTargetGainmB = LOUDNESS_ENHANCER_DEFAULT_TARGET_GAIN_MB;
176 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
177 ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
178
179 if (pContext->mCompressorL == NULL) {
180 pContext->mCompressorL = new le_fx::AdaptiveDynamicRangeCompression();
181 pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
182 }
183 if (pContext->mCompressorR == NULL) {
184 pContext->mCompressorR = new le_fx::AdaptiveDynamicRangeCompression();
185 pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
186 }
187
188 LE_setConfig(pContext, &pContext->mConfig);
189
190 return 0;
191}
192
193//
194//--- Effect Library Interface Implementation
195//
196
197int LELib_Create(const effect_uuid_t *uuid,
198 int32_t sessionId,
199 int32_t ioId,
200 effect_handle_t *pHandle) {
201 ALOGV("LELib_Create()");
202 int ret;
203 int i;
204
205 if (pHandle == NULL || uuid == NULL) {
206 return -EINVAL;
207 }
208
209 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
210 return -EINVAL;
211 }
212
213 LoudnessEnhancerContext *pContext = new LoudnessEnhancerContext;
214
215 pContext->mItfe = &gLEInterface;
216 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
217
218 pContext->mCompressorL = NULL;
219 pContext->mCompressorR = NULL;
220 ret = LE_init(pContext);
221 if (ret < 0) {
222 ALOGW("LELib_Create() init failed");
223 delete pContext;
224 return ret;
225 }
226
227 *pHandle = (effect_handle_t)pContext;
228
229 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED;
230
231 ALOGV(" LELib_Create context is %p", pContext);
232
233 return 0;
234
235}
236
237int LELib_Release(effect_handle_t handle) {
238 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)handle;
239
240 ALOGV("LELib_Release %p", handle);
241 if (pContext == NULL) {
242 return -EINVAL;
243 }
244 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
245 if (pContext->mCompressorL != NULL) {
246 delete pContext->mCompressorL;
247 pContext->mCompressorL = NULL;
248 }
249 if (pContext->mCompressorR != NULL) {
250 delete pContext->mCompressorR;
251 pContext->mCompressorR = NULL;
252 }
253 delete pContext;
254
255 return 0;
256}
257
258int LELib_GetDescriptor(const effect_uuid_t *uuid,
259 effect_descriptor_t *pDescriptor) {
260
261 if (pDescriptor == NULL || uuid == NULL){
262 ALOGV("LELib_GetDescriptor() called with NULL pointer");
263 return -EINVAL;
264 }
265
266 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
267 *pDescriptor = gLEDescriptor;
268 return 0;
269 }
270
271 return -EINVAL;
272} /* end LELib_GetDescriptor */
273
274//
275//--- Effect Control Interface Implementation
276//
277int LE_process(
278 effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
279{
280 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self;
281
282 if (pContext == NULL) {
283 return -EINVAL;
284 }
285
286 if (inBuffer == NULL || inBuffer->raw == NULL ||
287 outBuffer == NULL || outBuffer->raw == NULL ||
288 inBuffer->frameCount != outBuffer->frameCount ||
289 inBuffer->frameCount == 0) {
290 return -EINVAL;
291 }
292
293 //ALOGV("LE about to process %d samples", inBuffer->frameCount);
294 uint16_t inIdx;
295 float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f);
296 for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
297 inBuffer->s16[2*inIdx] = pContext->mCompressorL->Compress(
298 inputAmp * (float)inBuffer->s16[2*inIdx]);
299 inBuffer->s16[2*inIdx +1] = pContext->mCompressorR->Compress(
300 inputAmp * (float)inBuffer->s16[2*inIdx +1]);
301 }
302
303 if (inBuffer->raw != outBuffer->raw) {
304 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
305 for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
306 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
307 }
308 } else {
309 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
310 }
311 }
312 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
313 return -ENODATA;
314 }
315 return 0;
316}
317
318int LE_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
319 void *pCmdData, uint32_t *replySize, void *pReplyData) {
320
321 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self;
322 int retsize;
323
324 if (pContext == NULL || pContext->mState == LOUDNESS_ENHANCER_STATE_UNINITIALIZED) {
325 return -EINVAL;
326 }
327
328// ALOGV("LE_command command %d cmdSize %d",cmdCode, cmdSize);
329 switch (cmdCode) {
330 case EFFECT_CMD_INIT:
331 if (pReplyData == NULL || *replySize != sizeof(int)) {
332 return -EINVAL;
333 }
334 *(int *) pReplyData = LE_init(pContext);
335 break;
336 case EFFECT_CMD_SET_CONFIG:
337 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
338 || pReplyData == NULL || *replySize != sizeof(int)) {
339 return -EINVAL;
340 }
341 *(int *) pReplyData = LE_setConfig(pContext,
342 (effect_config_t *) pCmdData);
343 break;
344 case EFFECT_CMD_GET_CONFIG:
345 if (pReplyData == NULL ||
346 *replySize != sizeof(effect_config_t)) {
347 return -EINVAL;
348 }
349 LE_getConfig(pContext, (effect_config_t *)pReplyData);
350 break;
351 case EFFECT_CMD_RESET:
352 LE_reset(pContext);
353 break;
354 case EFFECT_CMD_ENABLE:
355 if (pReplyData == NULL || *replySize != sizeof(int)) {
356 return -EINVAL;
357 }
358 if (pContext->mState != LOUDNESS_ENHANCER_STATE_INITIALIZED) {
359 return -ENOSYS;
360 }
361 pContext->mState = LOUDNESS_ENHANCER_STATE_ACTIVE;
362 ALOGV("EFFECT_CMD_ENABLE() OK");
363 *(int *)pReplyData = 0;
364 break;
365 case EFFECT_CMD_DISABLE:
366 if (pReplyData == NULL || *replySize != sizeof(int)) {
367 return -EINVAL;
368 }
369 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
370 return -ENOSYS;
371 }
372 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED;
373 ALOGV("EFFECT_CMD_DISABLE() OK");
374 *(int *)pReplyData = 0;
375 break;
376 case EFFECT_CMD_GET_PARAM: {
377 if (pCmdData == NULL ||
378 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
379 pReplyData == NULL ||
380 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
381 return -EINVAL;
382 }
383 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
384 effect_param_t *p = (effect_param_t *)pReplyData;
385 p->status = 0;
386 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
387 if (p->psize != sizeof(uint32_t)) {
388 p->status = -EINVAL;
389 break;
390 }
391 switch (*(uint32_t *)p->data) {
392 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
393 ALOGV("get target gain(mB) = %d", pContext->mTargetGainmB);
394 *((int32_t *)p->data + 1) = pContext->mTargetGainmB;
395 p->vsize = sizeof(int32_t);
396 *replySize += sizeof(int32_t);
397 break;
398 default:
399 p->status = -EINVAL;
400 }
401 } break;
402 case EFFECT_CMD_SET_PARAM: {
403 if (pCmdData == NULL ||
404 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
405 pReplyData == NULL || *replySize != sizeof(int32_t)) {
406 return -EINVAL;
407 }
408 *(int32_t *)pReplyData = 0;
409 effect_param_t *p = (effect_param_t *)pCmdData;
410 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
411 *(int32_t *)pReplyData = -EINVAL;
412 break;
413 }
414 switch (*(uint32_t *)p->data) {
415 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
416 pContext->mTargetGainmB = *((int32_t *)p->data + 1);
417 ALOGV("set target gain(mB) = %d", pContext->mTargetGainmB);
418 LE_reset(pContext); // apply parameter update
419 break;
420 default:
421 *(int32_t *)pReplyData = -EINVAL;
422 }
423 } break;
424 case EFFECT_CMD_SET_DEVICE:
425 case EFFECT_CMD_SET_VOLUME:
426 case EFFECT_CMD_SET_AUDIO_MODE:
427 break;
428
429 default:
430 ALOGW("LE_command invalid command %d",cmdCode);
431 return -EINVAL;
432 }
433
434 return 0;
435}
436
437/* Effect Control Interface Implementation: get_descriptor */
438int LE_getDescriptor(effect_handle_t self,
439 effect_descriptor_t *pDescriptor)
440{
441 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *) self;
442
443 if (pContext == NULL || pDescriptor == NULL) {
444 ALOGV("LE_getDescriptor() invalid param");
445 return -EINVAL;
446 }
447
448 *pDescriptor = gLEDescriptor;
449
450 return 0;
451} /* end LE_getDescriptor */
452
453// effect_handle_t interface implementation for DRC effect
454const struct effect_interface_s gLEInterface = {
455 LE_process,
456 LE_command,
457 LE_getDescriptor,
458 NULL,
459};
460
461// This is the only symbol that needs to be exported
462__attribute__ ((visibility ("default")))
463audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
464 tag : AUDIO_EFFECT_LIBRARY_TAG,
465 version : EFFECT_LIBRARY_API_VERSION,
466 name : "Loudness Enhancer Library",
467 implementor : "The Android Open Source Project",
468 create_effect : LELib_Create,
469 release_effect : LELib_Release,
470 get_descriptor : LELib_GetDescriptor,
471};
472
473}; // extern "C"
474