OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
meters.hpp
1#pragma once
2#include <ossia/network/dataspace/gain.hpp>
3
4#include <algorithm>
5#include <array>
6#include <cmath>
7#include <numbers>
8
19{
21inline double fall_coefficient(double db_per_s, double rate) noexcept
22{
23 return std::pow(10., -db_per_s / (20. * rate));
24}
25
31{
32 double level{};
33 double fall{};
34
35 void prepare(double rate, double fall_db_per_s = 20.) noexcept
36 {
37 fall = fall_coefficient(fall_db_per_s, rate);
38 level = 0.;
39 }
40
41 void process(const auto* in, int n) noexcept
42 {
43 double l = level;
44 for(int i = 0; i < n; i++)
45 l = std::max(double(std::abs(in[i])), l * fall);
46 level = l;
47 }
48};
49
57struct rms
58{
59 double ms{};
60 double coef{};
61 bool sine_referenced{};
62
63 void prepare(double rate, double integration = 0.3, bool aes17 = false) noexcept
64 {
65 const double tau = integration / std::log(100.);
66 coef = 1. - std::exp(-1. / (tau * rate));
67 sine_referenced = aes17;
68 ms = 0.;
69 }
70
71 void process(const auto* in, int n) noexcept
72 {
73 double m = ms;
74 for(int i = 0; i < n; i++)
75 {
76 const double x = in[i];
77 m += coef * (x * x - m);
78 }
79 ms = m;
80 }
81
82 double level() const noexcept
83 {
84 const double r = std::sqrt(std::max(ms, 0.));
85 return sine_referenced ? r * std::numbers::sqrt2 : r;
86 }
87};
88
95struct vu
96{
97 static constexpr double zeta = 0.8127169864286775;
99 static constexpr double omega = 13.511933333385695;
101 static constexpr double sine_average_to_rms = 1.1107207345395915;
102
103 double y{}, v{};
104 double dt{};
105
106 void prepare(double rate) noexcept
107 {
108 dt = 1. / rate;
109 y = v = 0.;
110 }
111
112 void process(const auto* in, int n) noexcept
113 {
114 double yy = y, vv = v;
115 const double w2 = omega * omega, d = 2. * zeta * omega;
116 for(int i = 0; i < n; i++)
117 {
118 const double x = std::abs(double(in[i]));
119 vv += (w2 * (x - yy) - d * vv) * dt;
120 yy += vv * dt;
121 }
122 y = yy;
123 v = vv;
124 }
125
126 double level() const noexcept { return std::max(y, 0.) * sine_average_to_rms; }
127};
128
138struct ppm
139{
140 enum type
141 {
142 type_i,
143 type_ii
144 };
145
146 double env{};
147 double attack{};
148 double fall{};
149 double gain{1.};
150
151 static constexpr double integration_time(type t) noexcept
152 {
153 return t == type_i ? 0.005 : 0.010;
154 }
155 static constexpr double fall_db_per_s(type t) noexcept
156 {
157 return t == type_i ? 20. / 1.5 : 24. / 2.8;
158 }
159
163 type designed_type{};
164
165 void prepare(double rate, type t) noexcept
166 {
167 env = 0.;
168 if(rate == designed_rate && t == designed_type)
169 return;
170 designed_rate = rate;
171 designed_type = t;
172 fall = fall_coefficient(fall_db_per_s(t), rate);
173
174 // Steady reading for a given attack, to scale a steady sine to its peak
175 auto steady = [&](double a) {
176 ppm p{.attack = a, .fall = fall};
177 return p.tone_reading(rate, 0.1);
178 };
179 auto burst = [&](double a) {
180 ppm p{.attack = a, .fall = fall};
181 return p.tone_reading(rate, integration_time(t));
182 };
183
184 // The burst reading rises with the attack coefficient: bisect it
185 const double target = ossia::detail::DecibelsToLinearGain(-2.);
186 double lo = 1e-7, hi = 1.;
187 for(int i = 0; i < 60; i++)
188 {
189 const double mid = std::sqrt(lo * hi);
190 if(burst(mid) / steady(mid) < target)
191 lo = mid;
192 else
193 hi = mid;
194 }
195 attack = std::sqrt(lo * hi);
196 gain = 1. / steady(attack);
197 }
198
199 void process(const auto* in, int n) noexcept
200 {
201 double e = env;
202 for(int i = 0; i < n; i++)
203 {
204 const double x = std::abs(double(in[i]));
205 e *= fall;
206 if(x > e)
207 e += attack * (x - e);
208 }
209 env = e;
210 }
211
212 double level() const noexcept { return env * gain; }
213
215 double tone_reading(double rate, double duration) noexcept
216 {
217 env = 0.;
218 double peak = 0.;
219 const int n = std::max(1, int(duration * rate));
220 // The sine by rotation of a phasor, renormalized now and then
221 const double w = 2. * std::numbers::pi * 5000. / rate;
222 const double cw = std::cos(w), sw = std::sin(w);
223 double re = 1., im = 0.;
224 for(int i = 0; i < n; i++)
225 {
226 process(&im, 1);
227 peak = std::max(peak, env);
228 const double r = re * cw - im * sw;
229 im = re * sw + im * cw;
230 re = r;
231 if((i & 1023) == 1023)
232 {
233 const double norm = 1. / std::sqrt(re * re + im * im);
234 re *= norm;
235 im *= norm;
236 }
237 }
238 env = 0.;
239 return peak;
240 }
241};
242
250{
251 static constexpr int taps_per_phase = 12;
252 static constexpr int max_factor = 4;
253
254 int factor{1};
255 std::array<std::array<double, taps_per_phase>, max_factor> coefs{};
258 std::array<double, 2 * taps_per_phase> history{};
259 int pos{};
260 double block_peak{};
261
262 void prepare(double rate) noexcept
263 {
264 factor = rate < 96000. ? 4 : rate < 192000. ? 2 : 1;
265 history.fill(0.);
266 pos = 0;
267
268 // Kaiser-windowed sinc, cutoff at the original Nyquist frequency
269 const int len = taps_per_phase * factor;
270 const double beta = 7.865;
271 auto bessel_i0 = [](double x) {
272 double sum = 1., term = 1.;
273 for(int k = 1; k < 32; k++)
274 {
275 term *= (x / (2. * k)) * (x / (2. * k));
276 sum += term;
277 }
278 return sum;
279 };
280 const double i0b = bessel_i0(beta);
281 for(int t = 0; t < len; t++)
282 {
283 const double m = t - (len - 1) / 2.;
284 const double x = m / factor;
285 const double sinc
286 = std::abs(x) < 1e-12 ? 1. : std::sin(std::numbers::pi * x) / (std::numbers::pi * x);
287 const double r = 2. * t / (len - 1) - 1.;
288 const double win = bessel_i0(beta * std::sqrt(std::max(0., 1. - r * r))) / i0b;
289 coefs[t % factor][t / factor] = sinc * win;
290 }
291 }
292
293 void process(const auto* in, int n) noexcept
294 {
295 double peak = 0.;
296 if(factor == 1)
297 {
298 for(int i = 0; i < n; i++)
299 peak = std::max(peak, std::abs(double(in[i])));
300 block_peak = peak;
301 return;
302 }
303 for(int i = 0; i < n; i++)
304 {
305 pos = pos + 1 == taps_per_phase ? 0 : pos + 1;
306 history[pos] = history[pos + taps_per_phase] = in[i];
307 const double* h = history.data() + pos + 1;
308 for(int ph = 0; ph < factor; ph++)
309 {
310 double acc = 0.;
311 const auto& c = coefs[ph];
312 for(int k = 0; k < taps_per_phase; k++)
313 acc += c[k] * h[k];
314 peak = std::max(peak, std::abs(acc));
315 }
316 }
317 block_peak = peak;
318 }
319};
320
326{
327 double lr{}, ll{}, rr{};
328 double coef{};
329
330 void prepare(double rate, double integration = 0.3) noexcept
331 {
332 const double tau = integration / std::log(100.);
333 coef = 1. - std::exp(-1. / (tau * rate));
334 lr = ll = rr = 0.;
335 }
336
337 void process(const auto* l, const auto* r, int n) noexcept
338 {
339 double a = lr, b = ll, c = rr;
340 for(int i = 0; i < n; i++)
341 {
342 const double x = l[i], y = r[i];
343 a += coef * (x * y - a);
344 b += coef * (x * x - b);
345 c += coef * (y * y - c);
346 }
347 lr = a;
348 ll = b;
349 rr = c;
350 }
351
352 double value() const noexcept
353 {
354 const double d = std::sqrt(ll * rr);
355 return d > 1e-12 ? std::clamp(lr / d, -1., 1.) : 0.;
356 }
357};
358
364{
365 double value{};
366 double hold_frames{};
367 double since{};
368 double fall_per_frame{};
369
370 void prepare(double rate, double hold_s = 1.5, double fall_db_per_s = 20.) noexcept
371 {
372 hold_frames = hold_s * rate;
373 fall_per_frame = fall_coefficient(fall_db_per_s, rate);
374 value = 0.;
375 since = 0.;
376 }
377
378 void feed(double level, int frames) noexcept
379 {
380 if(level >= value)
381 {
382 value = level;
383 since = 0.;
384 return;
385 }
386 since += frames;
387 if(since > hold_frames)
388 value = std::max(level, value * std::pow(fall_per_frame, frames));
389 }
390};
391}
Definition meters.hpp:19
double fall_coefficient(double db_per_s, double rate) noexcept
Per-sample factor of an exponential fall of db_per_s decibels per second.
Definition meters.hpp:21
Definition meters.hpp:326
Definition meters.hpp:364
Definition meters.hpp:139
double designed_rate
The design the coefficients were computed for: preparing again for the same one only clears the envel...
Definition meters.hpp:162
double tone_reading(double rate, double duration) noexcept
Highest reading reached during a 5 kHz unit sine lasting duration.
Definition meters.hpp:215
Definition meters.hpp:58
Definition meters.hpp:31
Definition meters.hpp:250
std::array< double, 2 *taps_per_phase > history
The last taps_per_phase samples, stored twice so that the newest ones are always contiguous: history[...
Definition meters.hpp:258
Definition meters.hpp:96
static constexpr double omega
300 ms to 99% of a step, for that damping
Definition meters.hpp:99
static constexpr double sine_average_to_rms
Full-wave average of a sine to its RMS value: pi / (2 sqrt(2)).
Definition meters.hpp:101