Add experimental support for 24-bit, dual-ear HRTFs
[openal-soft.git] / Alc / mastering.c
blob9de5fd5f0d014e0fae0a9eda01770e3a8fc92467
1 #include "config.h"
3 #include <math.h>
5 #include "alu.h"
6 #include "almalloc.h"
8 #define RMS_WINDOW_SIZE (1<<7)
9 #define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
10 #define RMS_VALUE_MAX (1<<24)
12 #define LOOKAHEAD_SIZE (1<<13)
13 #define LOOKAHEAD_MASK (LOOKAHEAD_SIZE-1)
15 static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
17 typedef struct Compressor {
18 ALfloat PreGain;
19 ALfloat PostGain;
20 ALboolean SummedLink;
21 ALfloat AttackMin;
22 ALfloat AttackMax;
23 ALfloat ReleaseMin;
24 ALfloat ReleaseMax;
25 ALfloat Ratio;
26 ALfloat Threshold;
27 ALfloat Knee;
28 ALuint SampleRate;
30 ALuint RmsSum;
31 ALuint *RmsWindow;
32 ALsizei RmsIndex;
33 ALfloat Envelope[BUFFERSIZE];
34 ALfloat EnvLast;
35 } Compressor;
37 /* Multichannel compression is linked via one of two modes:
39 * Summed - Absolute sum of all channels.
40 * Maxed - Absolute maximum of any channel.
42 static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
43 ALfloat (*restrict OutBuffer)[BUFFERSIZE])
45 ALsizei c, i;
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] += OutBuffer[c][i];
56 for(i = 0;i < SamplesToDo;i++)
57 Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
60 static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
61 ALfloat (*restrict OutBuffer)[BUFFERSIZE])
63 ALsizei c, i;
65 for(i = 0;i < SamplesToDo;i++)
66 Comp->Envelope[i] = 0.0f;
68 for(c = 0;c < NumChans;c++)
70 for(i = 0;i < SamplesToDo;i++)
71 Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
75 /* Envelope detection/sensing can be done via:
77 * RMS - Rectangular windowed root mean square of linking stage.
78 * Peak - Implicit output from linking stage.
80 static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
82 ALuint sum = Comp->RmsSum;
83 ALuint *window = Comp->RmsWindow;
84 ALsizei index = Comp->RmsIndex;
85 ALsizei i;
87 for(i = 0;i < SamplesToDo;i++)
89 ALfloat sig = Comp->Envelope[i];
91 sum -= window[index];
92 window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
93 sum += window[index];
94 index = (index + 1) & RMS_WINDOW_MASK;
96 Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
99 Comp->RmsSum = sum;
100 Comp->RmsIndex = index;
103 /* This isn't a very sophisticated envelope follower, but it gets the job
104 * done. First, it operates at logarithmic scales to keep transitions
105 * appropriate for human hearing. Second, it can apply adaptive (automated)
106 * attack/release adjustments based on the signal.
108 static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
110 ALfloat attackMin = Comp->AttackMin;
111 ALfloat attackMax = Comp->AttackMax;
112 ALfloat releaseMin = Comp->ReleaseMin;
113 ALfloat releaseMax = Comp->ReleaseMax;
114 ALfloat last = Comp->EnvLast;
115 ALsizei i;
117 for(i = 0;i < SamplesToDo;i++)
119 ALfloat env = maxf(-6.0f, log10f(Comp->Envelope[i]));
120 ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
122 if(env > last)
123 last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
124 else
125 last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
127 Comp->Envelope[i] = last;
130 Comp->EnvLast = last;
133 /* The envelope is converted to control gain with an optional soft knee. */
134 static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
136 const ALfloat threshold = Comp->Threshold;
137 const ALfloat knee = Comp->Knee;
138 ALsizei i;
140 if(!(knee > 0.0f))
142 for(i = 0;i < SamplesToDo;i++)
144 ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
145 Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
148 else
150 const ALfloat lower = threshold - (0.5f * knee);
151 const ALfloat upper = threshold + (0.5f * knee);
152 const ALfloat m = 0.5f * Slope / knee;
154 for(i = 0;i < SamplesToDo;i++)
156 ALfloat env = Comp->Envelope[i];
157 ALfloat gain;
159 if(env > lower && env < upper)
160 gain = m * (env - lower) * (lower - env);
161 else
162 gain = Slope * (threshold - env);
164 Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
170 Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
171 const ALboolean SummedLink, const ALboolean RmsSensing,
172 const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
173 const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
174 const ALfloat Ratio, const ALfloat ThresholdDb,
175 const ALfloat KneeDb, const ALuint SampleRate)
177 Compressor *Comp;
178 size_t size;
179 ALsizei i;
181 size = sizeof(*Comp);
182 if(RmsSensing)
183 size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
184 Comp = al_calloc(16, size);
186 Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
187 Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
188 Comp->SummedLink = SummedLink;
189 Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
190 Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
191 Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
192 Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
193 Comp->Ratio = Ratio;
194 Comp->Threshold = ThresholdDb / 20.0f;
195 Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
196 Comp->SampleRate = SampleRate;
198 Comp->RmsSum = 0;
199 if(RmsSensing)
200 Comp->RmsWindow = (ALuint*)(Comp+1);
201 else
202 Comp->RmsWindow = NULL;
203 Comp->RmsIndex = 0;
205 for(i = 0;i < BUFFERSIZE;i++)
206 Comp->Envelope[i] = 0.0f;
207 Comp->EnvLast = -6.0f;
209 return Comp;
212 ALuint GetCompressorSampleRate(const Compressor *Comp)
214 return Comp->SampleRate;
217 void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
218 ALfloat (*restrict OutBuffer)[BUFFERSIZE])
220 ALsizei c, i;
222 if(Comp->PreGain != 1.0f)
224 for(c = 0;c < NumChans;c++)
226 for(i = 0;i < SamplesToDo;i++)
227 OutBuffer[c][i] *= Comp->PreGain;
231 if(Comp->SummedLink)
232 SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
233 else
234 MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
236 if(Comp->RmsWindow)
237 RmsDetection(Comp, SamplesToDo);
238 FollowEnvelope(Comp, SamplesToDo);
240 if(Comp->Ratio > 0.0f)
241 EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
242 else
243 EnvelopeGain(Comp, SamplesToDo, 1.0f);
245 if(Comp->PostGain != 1.0f)
247 for(i = 0;i < SamplesToDo;i++)
248 Comp->Envelope[i] *= Comp->PostGain;
250 for(c = 0;c < NumChans;c++)
252 for(i = 0;i < SamplesToDo;i++)
253 OutBuffer[c][i] *= Comp->Envelope[i];