1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci  st2095.c
3c72fcc34Sopenharmony_ci
4c72fcc34Sopenharmony_ci  Generate Bandlimited Pink Noise (-18.5dB AES FS)
5c72fcc34Sopenharmony_ci  Using the SMPTE ST 2095:1-2015 standard
6c72fcc34Sopenharmony_ci
7c72fcc34Sopenharmony_ci  Based on pseudo-code from the above SMPTE standard, which bore the credit
8c72fcc34Sopenharmony_ci  "Revised 2015-01-04 by Calvert Dayton"
9c72fcc34Sopenharmony_ci
10c72fcc34Sopenharmony_ci  Copyleft 2023 Rick Sayre - No rights reserved.
11c72fcc34Sopenharmony_ci*/
12c72fcc34Sopenharmony_ci
13c72fcc34Sopenharmony_ci#include "aconfig.h"
14c72fcc34Sopenharmony_ci#include <stdio.h>
15c72fcc34Sopenharmony_ci#include <math.h>
16c72fcc34Sopenharmony_ci#include "st2095.h"
17c72fcc34Sopenharmony_ci
18c72fcc34Sopenharmony_ci/************************************************************/
19c72fcc34Sopenharmony_ci
20c72fcc34Sopenharmony_ci
21c72fcc34Sopenharmony_civoid reset_st2095_noise_measurement( st2095_noise_t *st2095 ) {
22c72fcc34Sopenharmony_ci    st2095->accum = 0.;
23c72fcc34Sopenharmony_ci}
24c72fcc34Sopenharmony_ci
25c72fcc34Sopenharmony_cifloat compute_st2095_noise_measurement( st2095_noise_t *st2095, int period ) {
26c72fcc34Sopenharmony_ci    return(10. * log10f(st2095->accum / (float)period) + 3.01);
27c72fcc34Sopenharmony_ci}
28c72fcc34Sopenharmony_ci
29c72fcc34Sopenharmony_civoid initialize_st2095_noise( st2095_noise_t *st2095, int sample_rate) {
30c72fcc34Sopenharmony_ci    // Periodicity in samples must be a power of two, <= 2^31
31c72fcc34Sopenharmony_ci    // Typical values are 524288, 1048576, 2097152 or 4194304
32c72fcc34Sopenharmony_ci    if (sample_rate > 48000) {
33c72fcc34Sopenharmony_ci	// Special case LCG step for 1024K samples @ 88.2K or 96k
34c72fcc34Sopenharmony_ci	st2095->samplesPerPeriod = 1048576;
35c72fcc34Sopenharmony_ci	st2095->randStep = 163841;
36c72fcc34Sopenharmony_ci    } else {
37c72fcc34Sopenharmony_ci	st2095->samplesPerPeriod = 524288;
38c72fcc34Sopenharmony_ci	st2095->randStep = 52737;
39c72fcc34Sopenharmony_ci    }
40c72fcc34Sopenharmony_ci
41c72fcc34Sopenharmony_ci    // set up LCG PRNG
42c72fcc34Sopenharmony_ci    st2095->randMax = st2095->samplesPerPeriod - 1;
43c72fcc34Sopenharmony_ci    st2095->seed = 0;
44c72fcc34Sopenharmony_ci    st2095->scaleFactor = 2.0 / (float)st2095->randMax;
45c72fcc34Sopenharmony_ci
46c72fcc34Sopenharmony_ci    st2095->maxAmp = powf(10.0, ST2095_MAX_PEAK / 20.0);
47c72fcc34Sopenharmony_ci
48c72fcc34Sopenharmony_ci    // Calculate omegaT for matched Z transform highpass filters
49c72fcc34Sopenharmony_ci    st2095->w0t = 2.0 * M_PI * ST2095_HPFC / (float)sample_rate;
50c72fcc34Sopenharmony_ci
51c72fcc34Sopenharmony_ci    //  Limit LpFc <= Nyquist (actually lower, based on 48 vs 22.4 KHz spec cutoff)
52c72fcc34Sopenharmony_ci    //          The spec says the filter begins at 22.4KHz, if we ask for a Nyquist-impossible
53c72fcc34Sopenharmony_ci    //          sampling rate, compute something with the same relationship
54c72fcc34Sopenharmony_ci    st2095->LpFc = ST2095_LPFC;
55c72fcc34Sopenharmony_ci    float rateratio = 48000. / ST2095_LPFC;
56c72fcc34Sopenharmony_ci    if (st2095->LpFc > sample_rate/rateratio)
57c72fcc34Sopenharmony_ci	st2095->LpFc = sample_rate/rateratio;
58c72fcc34Sopenharmony_ci
59c72fcc34Sopenharmony_ci    // Calculate k and k^2 for bilinear transform lowpass filters
60c72fcc34Sopenharmony_ci    st2095->k = tanf(( 2.0 * M_PI * st2095->LpFc / (float)sample_rate ) / 2.0);
61c72fcc34Sopenharmony_ci    st2095->k2 = st2095->k * st2095->k;
62c72fcc34Sopenharmony_ci
63c72fcc34Sopenharmony_ci    // Calculate biquad coefficients for bandpass filter components
64c72fcc34Sopenharmony_ci    st2095->hp1_a1 = -2.0 * expf(-0.3826835 * st2095->w0t) * cosf(0.9238795 * st2095->w0t);
65c72fcc34Sopenharmony_ci    st2095->hp1_a2 = expf(2.0 * -0.3826835 * st2095->w0t);
66c72fcc34Sopenharmony_ci    st2095->hp1_b0 = (1.0 - st2095->hp1_a1 + st2095->hp1_a2) / 4.0;
67c72fcc34Sopenharmony_ci    st2095->hp1_b1 = -2.0 * st2095->hp1_b0;
68c72fcc34Sopenharmony_ci    st2095->hp1_b2 = st2095->hp1_b0;
69c72fcc34Sopenharmony_ci
70c72fcc34Sopenharmony_ci    st2095->hp2_a1 = -2.0 * expf(-0.9238795 * st2095->w0t) * cosf(0.3826835 * st2095->w0t);
71c72fcc34Sopenharmony_ci    st2095->hp2_a2 = expf(2.0 * -0.9238795 * st2095->w0t);
72c72fcc34Sopenharmony_ci    st2095->hp2_b0 = (1.0 - st2095->hp2_a1 + st2095->hp2_a2) / 4.0;
73c72fcc34Sopenharmony_ci    st2095->hp2_b1 = -2.0 * st2095->hp2_b0;
74c72fcc34Sopenharmony_ci    st2095->hp2_b2 = st2095->hp2_b0;
75c72fcc34Sopenharmony_ci
76c72fcc34Sopenharmony_ci    st2095->lp1_a1 = (2.0 * (st2095->k2 - 1.0)) / (st2095->k2 + (st2095->k / 1.306563) + 1.0);
77c72fcc34Sopenharmony_ci    st2095->lp1_a2 = (st2095->k2 - (st2095->k / 1.306563) + 1.0) / (st2095->k2 + (st2095->k / 1.306563) + 1.0);
78c72fcc34Sopenharmony_ci    st2095->lp1_b0 = st2095->k2 / (st2095->k2 + (st2095->k / 1.306563) + 1.0);
79c72fcc34Sopenharmony_ci    st2095->lp1_b1 = 2.0 * st2095->lp1_b0;
80c72fcc34Sopenharmony_ci    st2095->lp1_b2 = st2095->lp1_b0;
81c72fcc34Sopenharmony_ci
82c72fcc34Sopenharmony_ci    st2095->lp2_a1 = (2.0 * (st2095->k2 - 1.0)) / (st2095->k2 + (st2095->k / 0.541196) + 1.0);
83c72fcc34Sopenharmony_ci    st2095->lp2_a2 = (st2095->k2 - (st2095->k / 0.541196) + 1.0) / (st2095->k2 + (st2095->k / 0.541196) + 1.0);
84c72fcc34Sopenharmony_ci    st2095->lp2_b0 = st2095->k2 / (st2095->k2 + (st2095->k / 0.541196) + 1.0);
85c72fcc34Sopenharmony_ci    st2095->lp2_b1 = 2.0 * st2095->lp2_b0;
86c72fcc34Sopenharmony_ci    st2095->lp2_b2 = st2095->lp2_b0;
87c72fcc34Sopenharmony_ci
88c72fcc34Sopenharmony_ci    // initialize delay lines for bandpass filter
89c72fcc34Sopenharmony_ci    st2095->hp1w1 = 0.0;
90c72fcc34Sopenharmony_ci    st2095->hp1w2 = 0.0;
91c72fcc34Sopenharmony_ci    st2095->hp2w1 = 0.0;
92c72fcc34Sopenharmony_ci    st2095->hp2w2 = 0.0;
93c72fcc34Sopenharmony_ci    st2095->lp1w1 = 0.0;
94c72fcc34Sopenharmony_ci    st2095->lp1w2 = 0.0;
95c72fcc34Sopenharmony_ci    st2095->lp2w1 = 0.0;
96c72fcc34Sopenharmony_ci    st2095->lp2w2 = 0.0;
97c72fcc34Sopenharmony_ci
98c72fcc34Sopenharmony_ci    // initialize delay lines for pink filter network
99c72fcc34Sopenharmony_ci    st2095->lp1 = 0.0;
100c72fcc34Sopenharmony_ci    st2095->lp2 = 0.0;
101c72fcc34Sopenharmony_ci    st2095->lp3 = 0.0;
102c72fcc34Sopenharmony_ci    st2095->lp4 = 0.0;
103c72fcc34Sopenharmony_ci    st2095->lp5 = 0.0;
104c72fcc34Sopenharmony_ci    st2095->lp6 = 0.0;
105c72fcc34Sopenharmony_ci
106c72fcc34Sopenharmony_ci    // cycle the generator for one complete time series to populate filter-bank delay lines
107c72fcc34Sopenharmony_ci    for (int i=0; i<st2095->samplesPerPeriod; i++)
108c72fcc34Sopenharmony_ci	generate_st2095_noise_sample(st2095);
109c72fcc34Sopenharmony_ci    st2095->accum = 0.0;
110c72fcc34Sopenharmony_ci}
111c72fcc34Sopenharmony_ci
112c72fcc34Sopenharmony_cifloat generate_st2095_noise_sample( st2095_noise_t *st2095 ) {
113c72fcc34Sopenharmony_ci    float white, w, pink;
114c72fcc34Sopenharmony_ci
115c72fcc34Sopenharmony_ci    // Generate a pseudorandom integer in the range 0 <= seed <= randMax.
116c72fcc34Sopenharmony_ci    //# Bitwise AND with randMax zeroes out any unwanted high order bits.
117c72fcc34Sopenharmony_ci    st2095->seed = (1664525 * st2095->seed + st2095->randStep) & st2095->randMax;
118c72fcc34Sopenharmony_ci    // Scale to a real number in the range -1.0 <= white <= 1.0
119c72fcc34Sopenharmony_ci    white = (float)st2095->seed * st2095->scaleFactor - 1.0;
120c72fcc34Sopenharmony_ci
121c72fcc34Sopenharmony_ci    // Run pink filter; a parallel network of first-order LP filters, scaled to
122c72fcc34Sopenharmony_ci    // produce an output signal with target RMS = -21.5 dB FS (-18.5 dB AES FS)
123c72fcc34Sopenharmony_ci    // when bandpass filter cutoff frequencies are 10 Hz and 22.4 kHz.
124c72fcc34Sopenharmony_ci    st2095->lp1 = 0.9994551 * st2095->lp1 + 0.00198166688621989 * white;
125c72fcc34Sopenharmony_ci    st2095->lp2 = 0.9969859 * st2095->lp2 + 0.00263702334184061 * white;
126c72fcc34Sopenharmony_ci    st2095->lp3 = 0.9844470 * st2095->lp3 + 0.00643213710202331 * white;
127c72fcc34Sopenharmony_ci    st2095->lp4 = 0.9161757 * st2095->lp4 + 0.01438952538362820 * white;
128c72fcc34Sopenharmony_ci    st2095->lp5 = 0.6563399 * st2095->lp5 + 0.02698408541064610 * white;
129c72fcc34Sopenharmony_ci    pink = st2095->lp1 + st2095->lp2 + st2095->lp3 +
130c72fcc34Sopenharmony_ci	st2095->lp4 + st2095->lp5 + st2095->lp6 + white * 0.0342675832159306;
131c72fcc34Sopenharmony_ci    st2095->lp6 = white * 0.0088766118009356;
132c72fcc34Sopenharmony_ci
133c72fcc34Sopenharmony_ci    // Run bandpass filter; a series network of 4 biquad filters
134c72fcc34Sopenharmony_ci    // Biquad filters implemented in Direct Form II
135c72fcc34Sopenharmony_ci    w = pink - st2095->hp1_a1 * st2095->hp1w1 - st2095->hp1_a2 * st2095->hp1w2;
136c72fcc34Sopenharmony_ci    pink = st2095->hp1_b0 * w + st2095->hp1_b1 * st2095->hp1w1 + st2095->hp1_b2 * st2095->hp1w2;
137c72fcc34Sopenharmony_ci    st2095->hp1w2 = st2095->hp1w1;
138c72fcc34Sopenharmony_ci    st2095->hp1w1 = w;
139c72fcc34Sopenharmony_ci
140c72fcc34Sopenharmony_ci    w = pink - st2095->hp2_a1 * st2095->hp2w1 - st2095->hp2_a2 * st2095->hp2w2;
141c72fcc34Sopenharmony_ci    pink = st2095->hp2_b0 * w + st2095->hp2_b1 * st2095->hp2w1 + st2095->hp2_b2 * st2095->hp2w2;
142c72fcc34Sopenharmony_ci    st2095->hp2w2 = st2095->hp2w1;
143c72fcc34Sopenharmony_ci    st2095->hp2w1 = w;
144c72fcc34Sopenharmony_ci
145c72fcc34Sopenharmony_ci    w = pink - st2095->lp1_a1 * st2095->lp1w1 - st2095->lp1_a2 * st2095->lp1w2;
146c72fcc34Sopenharmony_ci    pink = st2095->lp1_b0 * w + st2095->lp1_b1 * st2095->lp1w1 + st2095->lp1_b2 * st2095->lp1w2;
147c72fcc34Sopenharmony_ci    st2095->lp1w2 = st2095->lp1w1;
148c72fcc34Sopenharmony_ci    st2095->lp1w1 = w;
149c72fcc34Sopenharmony_ci
150c72fcc34Sopenharmony_ci    w = pink - st2095->lp2_a1 * st2095->lp2w1 - st2095->lp2_a2 * st2095->lp2w2;
151c72fcc34Sopenharmony_ci    pink = st2095->lp2_b0 * w + st2095->lp2_b1 * st2095->lp2w1 + st2095->lp2_b2 * st2095->lp2w2;
152c72fcc34Sopenharmony_ci    st2095->lp2w2 = st2095->lp2w1;
153c72fcc34Sopenharmony_ci    st2095->lp2w1 = w;
154c72fcc34Sopenharmony_ci
155c72fcc34Sopenharmony_ci    // Limit peaks to +/-MaxAmp
156c72fcc34Sopenharmony_ci    if (pink > st2095->maxAmp)
157c72fcc34Sopenharmony_ci	pink = st2095->maxAmp;
158c72fcc34Sopenharmony_ci    else if (pink < -st2095->maxAmp)
159c72fcc34Sopenharmony_ci	pink = -st2095->maxAmp;
160c72fcc34Sopenharmony_ci
161c72fcc34Sopenharmony_ci    // accumulate squared amplitude for RMS computation
162c72fcc34Sopenharmony_ci    st2095->accum += (pink * pink);
163c72fcc34Sopenharmony_ci    return(pink);
164c72fcc34Sopenharmony_ci}
165