Make a function static that's only used in one source file
[openal-soft.git] / Alc / mastering.c
blob91267d83709606e6fc28bf9994da3e30eb35cf5b
1 #include "config.h"
3 #include <math.h>
5 #include "mastering.h"
6 #include "alu.h"
7 #include "almalloc.h"
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])
27 ALsizei c, i;
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])
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] = 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;
67 ALsizei i;
69 for(i = 0;i < SamplesToDo;i++)
71 ALfloat sig = Comp->Envelope[i];
73 sum -= window[index];
74 window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
75 sum += window[index];
76 index = (index + 1) & RMS_WINDOW_MASK;
78 Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
81 Comp->RmsSum = sum;
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;
97 ALsizei i;
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);
104 if(env > last)
105 last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
106 else
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;
120 ALsizei i;
122 if(!(knee > 0.0f))
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));
130 else
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];
139 ALfloat gain;
141 if(env > lower && env < upper)
142 gain = m * (env - lower) * (lower - env);
143 else
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)
159 Compressor *Comp;
160 size_t size;
161 ALsizei i;
163 size = sizeof(*Comp);
164 if(RmsSensing)
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));
175 Comp->Ratio = Ratio;
176 Comp->Threshold = ThresholdDb / 20.0f;
177 Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
178 Comp->SampleRate = SampleRate;
180 Comp->RmsSum = 0;
181 if(RmsSensing)
182 Comp->RmsWindow = (ALuint*)(Comp+1);
183 else
184 Comp->RmsWindow = NULL;
185 Comp->RmsIndex = 0;
187 for(i = 0;i < BUFFERSIZE;i++)
188 Comp->Envelope[i] = 0.0f;
189 Comp->EnvLast = -6.0f;
191 return Comp;
194 void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
195 ALfloat (*restrict OutBuffer)[BUFFERSIZE])
197 ALsizei c, i;
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;
208 if(Comp->SummedLink)
209 SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
210 else
211 MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
213 if(Comp->RmsWindow)
214 RmsDetection(Comp, SamplesToDo);
215 FollowEnvelope(Comp, SamplesToDo);
217 if(Comp->Ratio > 0.0f)
218 EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
219 else
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];