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