forked from DirtyUnicorns/android_packages_apps_DSPManager
-
Notifications
You must be signed in to change notification settings - Fork 2
/
EffectCompression.cpp
211 lines (176 loc) · 6.95 KB
/
EffectCompression.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Effect-DRC"
#include <cutils/log.h>
#include "EffectCompression.h"
#include <math.h>
typedef struct {
effect_param_t ep;
uint32_t code;
uint16_t value;
} cmd1x4_1x2_t;
static int32_t max(int32_t a, int32_t b)
{
return a > b ? a : b;
}
EffectCompression::EffectCompression()
: mCompressionRatio(2.0), mFade(0)
{
for (int32_t i = 0; i < 2; i ++) {
mCurrentLevel[i] = 0;
mUserLevel[i] = 1 << 24;
}
}
int32_t EffectCompression::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData, uint32_t* replySize, void* pReplyData)
{
if (cmdCode == EFFECT_CMD_SET_CONFIG) {
int32_t *replyData = (int32_t *) pReplyData;
int32_t ret = Effect::configure(pCmdData);
if (ret != 0) {
*replyData = ret;
return 0;
}
/* This filter gives a reasonable approximation of A- and C-weighting
* which is close to correct for 100 - 10 kHz. 10 dB gain must be added to result. */
mWeigherBP[0].setBandPass(0, 2200, mSamplingRate, 0.33);
mWeigherBP[1].setBandPass(0, 2200, mSamplingRate, 0.33);
*replyData = 0;
return 0;
}
if (cmdCode == EFFECT_CMD_SET_PARAM) {
effect_param_t *cep = (effect_param_t *) pCmdData;
if (cep->psize == 4 && cep->vsize == 2) {
int32_t *replyData = (int32_t *) pReplyData;
cmd1x4_1x2_t *strength = (cmd1x4_1x2_t *) pCmdData;
if (strength->code == 0) {
/* 1.0 .. 11.0 */
mCompressionRatio = 1.f + strength->value / 100.f;
ALOGI("Compression factor set to: %f", mCompressionRatio);
*replyData = 0;
return 0;
}
}
ALOGE("Unknown SET_PARAM of %d, %d bytes", cep->psize, cep->vsize);
return -1;
}
if (cmdCode == EFFECT_CMD_SET_VOLUME && cmdSize == 8) {
ALOGI("Setting volumes");
if (pReplyData != NULL) {
int32_t *userVols = (int32_t *) pCmdData;
for (uint32_t i = 0; i < cmdSize / 4; i ++) {
ALOGI("user volume on channel %d: %d", i, userVols[i]);
mUserLevel[i] = userVols[i];
}
int32_t *myVols = (int32_t *) pReplyData;
for (uint32_t i = 0; i < *replySize / 4; i ++) {
ALOGI("Returning unity for our pre-requested volume on channel %d", i);
myVols[i] = 1 << 24; /* Unity gain */
}
} else {
/* We don't control volume. */
for (int32_t i = 0; i < 2; i ++) {
mUserLevel[i] = 1 << 24;
}
}
return 0;
}
/* Init to current volume level on enabling effect to prevent
* initial fade in / other shite */
if (cmdCode == EFFECT_CMD_ENABLE) {
ALOGI("Copying user levels as initial loudness.");
/* Unfortunately Android calls SET_VOLUME after ENABLE for us.
* so we can't really use those volumes. It's safest just to fade in
* each time. */
for (int32_t i = 0; i < 2; i ++) {
mCurrentLevel[i] = 0;
}
}
return Effect::command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
/* Return fixed point 16.48 */
uint64_t EffectCompression::estimateOneChannelLevel(audio_buffer_t *in, int32_t interleave, int32_t offset, Biquad& weigherBP)
{
uint64_t power = 0;
for (uint32_t i = 0; i < in->frameCount; i ++) {
int32_t tmp = read(in, offset);
tmp = weigherBP.process(tmp);
/* 2^24 * 2^24 = 48 */
power += int64_t(tmp) * int64_t(tmp);
offset += interleave;
}
return (power / in->frameCount);
}
int32_t EffectCompression::process(audio_buffer_t *in, audio_buffer_t *out)
{
/* Analyze both channels separately, pick the maximum power measured. */
uint64_t maximumPowerSquared = 0;
for (uint32_t i = 0; i < 2; i ++) {
uint64_t candidatePowerSquared = estimateOneChannelLevel(in, 2, i, mWeigherBP[i]);
if (candidatePowerSquared > maximumPowerSquared) {
maximumPowerSquared = candidatePowerSquared;
}
}
/* -100 .. 0 dB. */
float signalPowerDb = logf(maximumPowerSquared / float(int64_t(1) << 48) + 1e-10f) / logf(10.0f) * 10.0f;
/* Target 83 dB SPL */
signalPowerDb += 96.0f - 83.0f + 10.0f;
/* now we have an estimate of the signal power, with 0 level around 83 dB.
* we now select the level to boost to. */
float desiredLevelDb = signalPowerDb / mCompressionRatio;
/* turn back to multiplier */
float correctionDb = desiredLevelDb - signalPowerDb;
if (mEnable && mFade != 100) {
mFade += 1;
}
if (!mEnable && mFade != 0) {
mFade -= 1;
}
correctionDb *= mFade / 100.f;
/* Reduce extreme boost by a smooth ramp.
* New range -50 .. 0 dB */
correctionDb -= powf(correctionDb/100, 2.0f) * (100.0f / 2.0f);
/* 40.24 */
int64_t correctionFactor = (1 << 24) * powf(10.0f, correctionDb / 20.0f);
/* Now we have correction factor and user-desired sound level. */
for (uint32_t i = 0; i < 2; i ++) {
/* 8.24 */
int32_t desiredLevel = mUserLevel[i] * correctionFactor >> 24;
/* 8.24 */
int32_t volAdj = desiredLevel - mCurrentLevel[i];
/* I want volume adjustments to occur in about 0.025 seconds.
* However, if the input buffer would happen to be longer than
* this, I'll just make sure that I am done with the adjustment
* by the end of it. */
int32_t adjLen = mSamplingRate / 40; // in practice, about 1100 frames
/* This formulation results in piecewise linear approximation of
* exponential because the rate of adjustment decreases from granule
* to granule. */
volAdj /= max(adjLen, in->frameCount);
/* Additionally, I want volume to increase only very slowly.
* This biases us against pumping effects and also tends to spare
* our ears when some very loud sound begins suddenly. */
if (volAdj > 0) {
volAdj >>= 4;
}
for (uint32_t j = 0; j < in->frameCount; j ++) {
int32_t value = read(in, j * 2 + i);
value = int64_t(value) * mCurrentLevel[i] >> 24;
write(out, j * 2 + i, value);
mCurrentLevel[i] += volAdj;
}
}
return mEnable || mFade != 0 ? 0 : -ENODATA;
}