Use < instead of != for some loop checks
[openal-soft.git] / Alc / mastering.c
blobe1bc4ae75b633915452f8ece45682dbfeb58ce7a
1 #include "config.h"
3 #include <math.h>
5 #include "mastering.h"
6 #include "alu.h"
7 #include "almalloc.h"
8 #include "static_assert.h"
11 /* These structures assume BUFFERSIZE is a power of 2. */
12 static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
14 typedef struct SlidingHold {
15 ALfloat Values[BUFFERSIZE];
16 ALsizei Expiries[BUFFERSIZE];
17 ALsizei LowerIndex;
18 ALsizei UpperIndex;
19 ALsizei Length;
20 } SlidingHold;
22 /* General topology and basic automation was based on the following paper:
24 * D. Giannoulis, M. Massberg and J. D. Reiss,
25 * "Parameter Automation in a Dynamic Range Compressor,"
26 * Journal of the Audio Engineering Society, v61 (10), Oct. 2013
28 * Available (along with supplemental reading) at:
30 * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
32 typedef struct Compressor {
33 ALsizei NumChans;
34 ALuint SampleRate;
36 struct {
37 ALuint Knee : 1;
38 ALuint Attack : 1;
39 ALuint Release : 1;
40 ALuint PostGain : 1;
41 ALuint Declip : 1;
42 } Auto;
44 ALsizei LookAhead;
46 ALfloat PreGain;
47 ALfloat PostGain;
49 ALfloat Threshold;
50 ALfloat Slope;
51 ALfloat Knee;
53 ALfloat Attack;
54 ALfloat Release;
56 alignas(16) ALfloat SideChain[2*BUFFERSIZE];
57 alignas(16) ALfloat CrestFactor[BUFFERSIZE];
59 SlidingHold *Hold;
60 ALfloat (*Delay)[BUFFERSIZE];
61 ALsizei DelayIndex;
63 ALfloat CrestCoeff;
64 ALfloat GainEstimate;
65 ALfloat AdaptCoeff;
67 ALfloat LastPeakSq;
68 ALfloat LastRmsSq;
69 ALfloat LastRelease;
70 ALfloat LastAttack;
71 ALfloat LastGainDev;
72 } Compressor;
75 /* This sliding hold follows the input level with an instant attack and a
76 * fixed duration hold before an instant release to the next highest level.
77 * It is a sliding window maximum (descending maxima) implementation based on
78 * Richard Harter's ascending minima algorithm available at:
80 * http://www.richardhartersworld.com/cri/2001/slidingmin.html
82 ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
84 const ALsizei mask = BUFFERSIZE - 1;
85 const ALsizei length = Hold->Length;
86 ALfloat *restrict values = Hold->Values;
87 ALsizei *restrict expiries = Hold->Expiries;
88 ALsizei lowerIndex = Hold->LowerIndex;
89 ALsizei upperIndex = Hold->UpperIndex;
91 if(i >= expiries[upperIndex])
92 upperIndex = (upperIndex + 1) & mask;
94 if(in >= values[upperIndex])
96 values[upperIndex] = in;
97 expiries[upperIndex] = i + length;
98 lowerIndex = upperIndex;
100 else
102 do {
103 do {
104 if(!(in >= values[lowerIndex]))
105 goto found_place;
106 } while(lowerIndex--);
107 lowerIndex = mask;
108 } while(1);
109 found_place:
111 lowerIndex = (lowerIndex + 1) & mask;
112 values[lowerIndex] = in;
113 expiries[lowerIndex] = i + length;
116 Hold->LowerIndex = lowerIndex;
117 Hold->UpperIndex = upperIndex;
119 return values[upperIndex];
122 static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
124 const ALsizei lowerIndex = Hold->LowerIndex;
125 ALsizei *restrict expiries = Hold->Expiries;
126 ALsizei i = Hold->UpperIndex;
128 if(lowerIndex < i)
130 for(;i < BUFFERSIZE;i++)
131 expiries[i] -= n;
132 i = 0;
134 for(;i < lowerIndex;i++)
135 expiries[i] -= n;
137 expiries[i] -= n;
140 /* Multichannel compression is linked via the absolute maximum of all
141 * channels.
143 static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
145 const ALsizei index = Comp->LookAhead;
146 const ALsizei numChans = Comp->NumChans;
147 ALfloat *restrict sideChain = Comp->SideChain;
148 ALsizei c, i;
150 ASSUME(SamplesToDo > 0);
151 ASSUME(numChans > 0);
153 for(i = 0;i < SamplesToDo;i++)
154 sideChain[index + i] = 0.0f;
156 for(c = 0;c < numChans;c++)
158 ALsizei offset = index;
159 for(i = 0;i < SamplesToDo;i++)
161 sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
162 ++offset;
167 /* This calculates the squared crest factor of the control signal for the
168 * basic automation of the attack/release times. As suggested by the paper,
169 * it uses an instantaneous squared peak detector and a squared RMS detector
170 * both with 200ms release times.
172 static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
174 const ALfloat a_crest = Comp->CrestCoeff;
175 const ALsizei index = Comp->LookAhead;
176 const ALfloat *restrict sideChain = Comp->SideChain;
177 ALfloat *restrict crestFactor = Comp->CrestFactor;
178 ALfloat y2_peak = Comp->LastPeakSq;
179 ALfloat y2_rms = Comp->LastRmsSq;
180 ALsizei i;
182 ASSUME(SamplesToDo > 0);
184 for(i = 0;i < SamplesToDo;i++)
186 ALfloat x_abs = sideChain[index + i];
187 ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
189 y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
190 y2_rms = lerp(x2, y2_rms, a_crest);
191 crestFactor[i] = y2_peak / y2_rms;
194 Comp->LastPeakSq = y2_peak;
195 Comp->LastRmsSq = y2_rms;
198 /* The side-chain starts with a simple peak detector (based on the absolute
199 * value of the incoming signal) and performs most of its operations in the
200 * log domain.
202 static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
204 const ALsizei index = Comp->LookAhead;
205 ALfloat *restrict sideChain = Comp->SideChain;
206 ALsizei i;
208 ASSUME(SamplesToDo > 0);
210 for(i = 0;i < SamplesToDo;i++)
212 const ALuint offset = index + i;
213 const ALfloat x_abs = sideChain[offset];
215 sideChain[offset] = logf(maxf(0.000001f, x_abs));
219 /* An optional hold can be used to extend the peak detector so it can more
220 * solidly detect fast transients. This is best used when operating as a
221 * limiter.
223 static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
225 const ALsizei index = Comp->LookAhead;
226 ALfloat *restrict sideChain = Comp->SideChain;
227 SlidingHold *hold = Comp->Hold;
228 ALsizei i;
230 ASSUME(SamplesToDo > 0);
232 for(i = 0;i < SamplesToDo;i++)
234 const ALsizei offset = index + i;
235 const ALfloat x_abs = sideChain[offset];
236 const ALfloat x_G = logf(maxf(0.000001f, x_abs));
238 sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
241 ShiftSlidingHold(hold, SamplesToDo);
244 /* This is the heart of the feed-forward compressor. It operates in the log
245 * domain (to better match human hearing) and can apply some basic automation
246 * to knee width, attack/release times, make-up/post gain, and clipping
247 * reduction.
249 static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
251 const bool autoKnee = Comp->Auto.Knee;
252 const bool autoAttack = Comp->Auto.Attack;
253 const bool autoRelease = Comp->Auto.Release;
254 const bool autoPostGain = Comp->Auto.PostGain;
255 const bool autoDeclip = Comp->Auto.Declip;
256 const ALsizei lookAhead = Comp->LookAhead;
257 const ALfloat threshold = Comp->Threshold;
258 const ALfloat slope = Comp->Slope;
259 const ALfloat attack = Comp->Attack;
260 const ALfloat release = Comp->Release;
261 const ALfloat c_est = Comp->GainEstimate;
262 const ALfloat a_adp = Comp->AdaptCoeff;
263 const ALfloat *restrict crestFactor = Comp->CrestFactor;
264 ALfloat *restrict sideChain = Comp->SideChain;
265 ALfloat postGain = Comp->PostGain;
266 ALfloat knee = Comp->Knee;
267 ALfloat t_att = attack;
268 ALfloat t_rel = release - attack;
269 ALfloat a_att = expf(-1.0f / t_att);
270 ALfloat a_rel = expf(-1.0f / t_rel);
271 ALfloat y_1 = Comp->LastRelease;
272 ALfloat y_L = Comp->LastAttack;
273 ALfloat c_dev = Comp->LastGainDev;
274 ALsizei i;
276 ASSUME(SamplesToDo > 0);
278 for(i = 0;i < SamplesToDo;i++)
280 const ALfloat y2_crest = crestFactor[i];
281 const ALfloat x_G = sideChain[lookAhead + i];
282 const ALfloat x_over = x_G - threshold;
283 ALfloat knee_h;
284 ALfloat y_G;
285 ALfloat x_L;
287 if(autoKnee)
288 knee = maxf(0.0f, 2.5f * (c_dev + c_est));
289 knee_h = 0.5f * knee;
291 /* This is the gain computer. It applies a static compression curve
292 * to the control signal.
294 if(x_over <= -knee_h)
295 y_G = 0.0f;
296 else if(fabsf(x_over) < knee_h)
297 y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
298 else
299 y_G = x_over;
301 x_L = -slope * y_G;
303 if(autoAttack)
305 t_att = 2.0f * attack / y2_crest;
306 a_att = expf(-1.0f / t_att);
309 if(autoRelease)
311 t_rel = 2.0f * release / y2_crest - t_att;
312 a_rel = expf(-1.0f / t_rel);
315 /* Gain smoothing (ballistics) is done via a smooth decoupled peak
316 * detector. The attack time is subtracted from the release time
317 * above to compensate for the chained operating mode.
319 y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
320 y_L = lerp(y_1, y_L, a_att);
322 /* Knee width and make-up gain automation make use of a smoothed
323 * measurement of deviation between the control signal and estimate.
324 * The estimate is also used to bias the measurement to hot-start its
325 * average.
327 c_dev = lerp(-y_L - c_est, c_dev, a_adp);
329 if(autoPostGain)
331 /* Clipping reduction is only viable when make-up gain is being
332 * automated. It modifies the deviation to further attenuate the
333 * control signal when clipping is detected. The adaptation
334 * time is sufficiently long enough to suppress further clipping
335 * at the same output level.
337 if(autoDeclip)
338 c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
340 postGain = -(c_dev + c_est);
343 sideChain[i] = expf(postGain - y_L);
346 Comp->LastRelease = y_1;
347 Comp->LastAttack = y_L;
348 Comp->LastGainDev = c_dev;
351 /* Combined with the hold time, a look-ahead delay can improve handling of
352 * fast transients by allowing the envelope time to converge prior to
353 * reaching the offending impulse. This is best used when operating as a
354 * limiter.
356 static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
358 const ALsizei mask = BUFFERSIZE - 1;
359 const ALsizei numChans = Comp->NumChans;
360 const ALsizei indexIn = Comp->DelayIndex;
361 const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
362 ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
363 ALsizei c, i;
365 ASSUME(SamplesToDo > 0);
366 ASSUME(numChans > 0);
368 for(c = 0;c < numChans;c++)
370 for(i = 0;i < SamplesToDo;i++)
372 ALfloat sig = OutBuffer[c][i];
374 OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
375 delay[c][(indexIn + i) & mask] = sig;
379 Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
382 /* The compressor is initialized with the following settings:
384 * NumChans - Number of channels to process.
385 * SampleRate - Sample rate to process.
386 * AutoKnee - Whether to automate the knee width parameter.
387 * AutoAttack - Whether to automate the attack time parameter.
388 * AutoRelease - Whether to automate the release time parameter.
389 * AutoPostGain - Whether to automate the make-up (post) gain parameter.
390 * AutoDeclip - Whether to automate clipping reduction. Ignored when
391 * not automating make-up gain.
392 * LookAheadTime - Look-ahead time (in seconds).
393 * HoldTime - Peak hold-time (in seconds).
394 * PreGainDb - Gain applied before detection (in dB).
395 * PostGainDb - Make-up gain applied after compression (in dB).
396 * ThresholdDb - Triggering threshold (in dB).
397 * Ratio - Compression ratio (x:1). Set to INFINITY for true
398 * limiting. Ignored when automating knee width.
399 * KneeDb - Knee width (in dB). Ignored when automating knee
400 * width.
401 * AttackTimeMin - Attack time (in seconds). Acts as a maximum when
402 * automating attack time.
403 * ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
404 * automating release time.
406 Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
407 const ALboolean AutoKnee, const ALboolean AutoAttack,
408 const ALboolean AutoRelease, const ALboolean AutoPostGain,
409 const ALboolean AutoDeclip, const ALfloat LookAheadTime,
410 const ALfloat HoldTime, const ALfloat PreGainDb,
411 const ALfloat PostGainDb, const ALfloat ThresholdDb,
412 const ALfloat Ratio, const ALfloat KneeDb,
413 const ALfloat AttackTime, const ALfloat ReleaseTime)
415 Compressor *Comp;
416 ALsizei lookAhead;
417 ALsizei hold;
418 size_t size;
420 lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
421 hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
422 /* The sliding hold implementation doesn't handle a length of 1. A 1-sample
423 * hold is useless anyway, it would only ever give back what was just given
424 * to it.
426 if(hold == 1)
427 hold = 0;
429 size = sizeof(*Comp);
430 if(lookAhead > 0)
432 size += sizeof(*Comp->Delay) * NumChans;
433 if(hold > 0)
434 size += sizeof(*Comp->Hold);
437 Comp = al_calloc(16, size);
438 Comp->NumChans = NumChans;
439 Comp->SampleRate = SampleRate;
440 Comp->Auto.Knee = AutoKnee;
441 Comp->Auto.Attack = AutoAttack;
442 Comp->Auto.Release = AutoRelease;
443 Comp->Auto.PostGain = AutoPostGain;
444 Comp->Auto.Declip = AutoPostGain && AutoDeclip;
445 Comp->LookAhead = lookAhead;
446 Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
447 Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
448 Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
449 Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
450 Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
451 Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
452 Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
454 /* Knee width automation actually treats the compressor as a limiter. By
455 * varying the knee width, it can effectively be seen as applying
456 * compression over a wide range of ratios.
458 if(AutoKnee)
459 Comp->Slope = -1.0f;
461 if(lookAhead > 0)
463 if(hold > 0)
465 Comp->Hold = (SlidingHold*)(Comp + 1);
466 Comp->Hold->Values[0] = -INFINITY;
467 Comp->Hold->Expiries[0] = hold;
468 Comp->Hold->Length = hold;
469 Comp->Delay = (ALfloat(*)[])(Comp->Hold + 1);
471 else
473 Comp->Delay = (ALfloat(*)[])(Comp + 1);
477 Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
478 Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
479 Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
481 return Comp;
484 void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
486 const ALsizei numChans = Comp->NumChans;
487 const ALfloat preGain = Comp->PreGain;
488 ALfloat *restrict sideChain;
489 ALsizei c, i;
491 ASSUME(SamplesToDo > 0);
492 ASSUME(numChans > 0);
494 if(preGain != 1.0f)
496 for(c = 0;c < numChans;c++)
498 for(i = 0;i < SamplesToDo;i++)
499 OutBuffer[c][i] *= preGain;
503 LinkChannels(Comp, SamplesToDo, OutBuffer);
505 if(Comp->Auto.Attack || Comp->Auto.Release)
506 CrestDetector(Comp, SamplesToDo);
508 if(Comp->Hold)
509 PeakHoldDetector(Comp, SamplesToDo);
510 else
511 PeakDetector(Comp, SamplesToDo);
513 GainCompressor(Comp, SamplesToDo);
515 if(Comp->Delay)
516 SignalDelay(Comp, SamplesToDo, OutBuffer);
518 sideChain = Comp->SideChain;
519 for(c = 0;c < numChans;c++)
521 for(i = 0;i < SamplesToDo;i++)
522 OutBuffer[c][i] *= sideChain[i];
525 memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
529 ALsizei GetCompressorLookAhead(const Compressor *Comp)
530 { return Comp->LookAhead; }