10 extern inline ALuint
GetCompressorSampleRate(const Compressor
*Comp
);
12 #define RMS_WINDOW_SIZE (1<<7)
13 #define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
14 #define RMS_VALUE_MAX (1<<24)
16 static_assert(RMS_VALUE_MAX
< (UINT_MAX
/ RMS_WINDOW_SIZE
), "RMS_VALUE_MAX is too big");
19 /* Multichannel compression is linked via one of two modes:
21 * Summed - Absolute sum of all channels.
22 * Maxed - Absolute maximum of any channel.
24 static void SumChannels(Compressor
*Comp
, const ALsizei NumChans
, const ALsizei SamplesToDo
,
25 ALfloat (*restrict OutBuffer
)[BUFFERSIZE
])
29 for(i
= 0;i
< SamplesToDo
;i
++)
30 Comp
->Envelope
[i
] = 0.0f
;
32 for(c
= 0;c
< NumChans
;c
++)
34 for(i
= 0;i
< SamplesToDo
;i
++)
35 Comp
->Envelope
[i
] += OutBuffer
[c
][i
];
38 for(i
= 0;i
< SamplesToDo
;i
++)
39 Comp
->Envelope
[i
] = fabsf(Comp
->Envelope
[i
]);
42 static void MaxChannels(Compressor
*Comp
, const ALsizei NumChans
, const ALsizei SamplesToDo
,
43 ALfloat (*restrict OutBuffer
)[BUFFERSIZE
])
47 for(i
= 0;i
< SamplesToDo
;i
++)
48 Comp
->Envelope
[i
] = 0.0f
;
50 for(c
= 0;c
< NumChans
;c
++)
52 for(i
= 0;i
< SamplesToDo
;i
++)
53 Comp
->Envelope
[i
] = maxf(Comp
->Envelope
[i
], fabsf(OutBuffer
[c
][i
]));
57 /* Envelope detection/sensing can be done via:
59 * RMS - Rectangular windowed root mean square of linking stage.
60 * Peak - Implicit output from linking stage.
62 static void RmsDetection(Compressor
*Comp
, const ALsizei SamplesToDo
)
64 ALuint sum
= Comp
->RmsSum
;
65 ALuint
*window
= Comp
->RmsWindow
;
66 ALsizei index
= Comp
->RmsIndex
;
69 for(i
= 0;i
< SamplesToDo
;i
++)
71 ALfloat sig
= Comp
->Envelope
[i
];
74 window
[index
] = fastf2i(minf(sig
* sig
* 65536.0f
, RMS_VALUE_MAX
));
76 index
= (index
+ 1) & RMS_WINDOW_MASK
;
78 Comp
->Envelope
[i
] = sqrtf(sum
/ 65536.0f
/ RMS_WINDOW_SIZE
);
82 Comp
->RmsIndex
= index
;
85 /* This isn't a very sophisticated envelope follower, but it gets the job
86 * done. First, it operates at logarithmic scales to keep transitions
87 * appropriate for human hearing. Second, it can apply adaptive (automated)
88 * attack/release adjustments based on the signal.
90 static void FollowEnvelope(Compressor
*Comp
, const ALsizei SamplesToDo
)
92 ALfloat attackMin
= Comp
->AttackMin
;
93 ALfloat attackMax
= Comp
->AttackMax
;
94 ALfloat releaseMin
= Comp
->ReleaseMin
;
95 ALfloat releaseMax
= Comp
->ReleaseMax
;
96 ALfloat last
= Comp
->EnvLast
;
99 for(i
= 0;i
< SamplesToDo
;i
++)
101 ALfloat env
= maxf(-6.0f
, log10f(Comp
->Envelope
[i
]));
102 ALfloat slope
= minf(1.0f
, fabsf(env
- last
) / 4.5f
);
105 last
= minf(env
, last
+ lerp(attackMin
, attackMax
, 1.0f
- (slope
* slope
)));
107 last
= maxf(env
, last
+ lerp(releaseMin
, releaseMax
, 1.0f
- (slope
* slope
)));
109 Comp
->Envelope
[i
] = last
;
112 Comp
->EnvLast
= last
;
115 /* The envelope is converted to control gain with an optional soft knee. */
116 static void EnvelopeGain(Compressor
*Comp
, const ALsizei SamplesToDo
, const ALfloat Slope
)
118 const ALfloat threshold
= Comp
->Threshold
;
119 const ALfloat knee
= Comp
->Knee
;
124 for(i
= 0;i
< SamplesToDo
;i
++)
126 ALfloat gain
= Slope
* (threshold
- Comp
->Envelope
[i
]);
127 Comp
->Envelope
[i
] = powf(10.0f
, minf(0.0f
, gain
));
132 const ALfloat lower
= threshold
- (0.5f
* knee
);
133 const ALfloat upper
= threshold
+ (0.5f
* knee
);
134 const ALfloat m
= 0.5f
* Slope
/ knee
;
136 for(i
= 0;i
< SamplesToDo
;i
++)
138 ALfloat env
= Comp
->Envelope
[i
];
141 if(env
> lower
&& env
< upper
)
142 gain
= m
* (env
- lower
) * (lower
- env
);
144 gain
= Slope
* (threshold
- env
);
146 Comp
->Envelope
[i
] = powf(10.0f
, minf(0.0f
, gain
));
152 Compressor
*CompressorInit(const ALfloat PreGainDb
, const ALfloat PostGainDb
,
153 const ALboolean SummedLink
, const ALboolean RmsSensing
,
154 const ALfloat AttackTimeMin
, const ALfloat AttackTimeMax
,
155 const ALfloat ReleaseTimeMin
, const ALfloat ReleaseTimeMax
,
156 const ALfloat Ratio
, const ALfloat ThresholdDb
,
157 const ALfloat KneeDb
, const ALuint SampleRate
)
163 size
= sizeof(*Comp
);
165 size
+= sizeof(Comp
->RmsWindow
[0]) * RMS_WINDOW_SIZE
;
166 Comp
= al_calloc(16, size
);
168 Comp
->PreGain
= powf(10.0f
, PreGainDb
/ 20.0f
);
169 Comp
->PostGain
= powf(10.0f
, PostGainDb
/ 20.0f
);
170 Comp
->SummedLink
= SummedLink
;
171 Comp
->AttackMin
= 1.0f
/ maxf(0.000001f
, AttackTimeMin
* SampleRate
* logf(10.0f
));
172 Comp
->AttackMax
= 1.0f
/ maxf(0.000001f
, AttackTimeMax
* SampleRate
* logf(10.0f
));
173 Comp
->ReleaseMin
= -1.0f
/ maxf(0.000001f
, ReleaseTimeMin
* SampleRate
* logf(10.0f
));
174 Comp
->ReleaseMax
= -1.0f
/ maxf(0.000001f
, ReleaseTimeMax
* SampleRate
* logf(10.0f
));
176 Comp
->Threshold
= ThresholdDb
/ 20.0f
;
177 Comp
->Knee
= maxf(0.0f
, KneeDb
/ 20.0f
);
178 Comp
->SampleRate
= SampleRate
;
182 Comp
->RmsWindow
= (ALuint
*)(Comp
+1);
184 Comp
->RmsWindow
= NULL
;
187 for(i
= 0;i
< BUFFERSIZE
;i
++)
188 Comp
->Envelope
[i
] = 0.0f
;
189 Comp
->EnvLast
= -6.0f
;
194 void ApplyCompression(Compressor
*Comp
, const ALsizei NumChans
, const ALsizei SamplesToDo
,
195 ALfloat (*restrict OutBuffer
)[BUFFERSIZE
])
199 if(Comp
->PreGain
!= 1.0f
)
201 for(c
= 0;c
< NumChans
;c
++)
203 for(i
= 0;i
< SamplesToDo
;i
++)
204 OutBuffer
[c
][i
] *= Comp
->PreGain
;
209 SumChannels(Comp
, NumChans
, SamplesToDo
, OutBuffer
);
211 MaxChannels(Comp
, NumChans
, SamplesToDo
, OutBuffer
);
214 RmsDetection(Comp
, SamplesToDo
);
215 FollowEnvelope(Comp
, SamplesToDo
);
217 if(Comp
->Ratio
> 0.0f
)
218 EnvelopeGain(Comp
, SamplesToDo
, 1.0f
- (1.0f
/ Comp
->Ratio
));
220 EnvelopeGain(Comp
, SamplesToDo
, 1.0f
);
222 if(Comp
->PostGain
!= 1.0f
)
224 for(i
= 0;i
< SamplesToDo
;i
++)
225 Comp
->Envelope
[i
] *= Comp
->PostGain
;
227 for(c
= 0;c
< NumChans
;c
++)
229 for(i
= 0;i
< SamplesToDo
;i
++)
230 OutBuffer
[c
][i
] *= Comp
->Envelope
[i
];