Skip to content

Commit 5466e69

Browse files
committed
Add files from Tympan_Library
1 parent 5a43e34 commit 5466e69

29 files changed

+2255
-257
lines changed

AudioCalcEnvelope_F32.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* AudioCalcEnvelope_F32
3+
*
4+
* Created: Chip Audette, Feb 2017
5+
* Purpose: This module extracts the envelope of the audio signal.
6+
* Derived From: Core envelope extraction algorithm is from "smooth_env"
7+
* WDRC_circuit from CHAPRO from BTNRC: https://github.com/BTNRH/chapro
8+
* As of Feb 2017, CHAPRO license is listed as "Creative Commons?"
9+
*
10+
* This processes a single stream fo audio data (ie, it is mono)
11+
*
12+
* MIT License. use at your own risk.
13+
*/
14+
15+
#ifndef _AudioCalcEnvelope_F32_h
16+
#define _AudioCalcEnvelope_F32_h
17+
18+
#include <arm_math.h> //ARM DSP extensions. for speed!
19+
#include <AudioStream_F32.h>
20+
21+
class AudioCalcEnvelope_F32 : public AudioStream_F32
22+
{
23+
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
24+
//GUI: shortName:calc_envelope
25+
public:
26+
//default constructor
27+
AudioCalcEnvelope_F32(void) : AudioStream_F32(1, inputQueueArray_f32),
28+
sample_rate_Hz(AUDIO_SAMPLE_RATE) { setDefaultValues(); };
29+
AudioCalcEnvelope_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32),
30+
sample_rate_Hz(settings.sample_rate_Hz) { setDefaultValues(); };
31+
32+
//here's the method that does all the work
33+
void update(void) {
34+
35+
//get the input audio data block
36+
audio_block_f32_t *in_block = AudioStream_F32::receiveReadOnly_f32();
37+
if (!in_block) return;
38+
39+
//check format
40+
if (in_block->fs_Hz != sample_rate_Hz) {
41+
Serial.println("AudioComputeEnvelope_F32: *** WARNING ***: Data sample rate does not match expected.");
42+
Serial.println("AudioComputeEnvelope_F32: Changing sample rate.");
43+
setSampleRate_Hz(in_block->fs_Hz);
44+
}
45+
46+
//prepare an output data block
47+
audio_block_f32_t *out_block = AudioStream_F32::allocate_f32();
48+
if (!out_block) return;
49+
50+
// //////////////////////add your processing here!
51+
smooth_env(in_block->data, out_block->data, in_block->length);
52+
out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz;
53+
54+
//transmit the block and be done
55+
AudioStream_F32::transmit(out_block);
56+
AudioStream_F32::release(out_block);
57+
AudioStream_F32::release(in_block);
58+
59+
}
60+
61+
//compute the smoothed signal envelope
62+
//compute the envelope of the signal, not of the signal power)
63+
void smooth_env(float x[], float y[], const int n) {
64+
float xab, xpk;
65+
int k;
66+
67+
// find envelope of x and return as y
68+
//xpk = *ppk; // start with previous xpk
69+
xpk = state_ppk;
70+
for (k = 0; k < n; k++) {
71+
xab = (x[k] >= 0.0f) ? x[k] : -x[k];
72+
if (xab >= xpk) {
73+
xpk = alfa * xpk + (1.f-alfa) * xab;
74+
} else {
75+
xpk = beta * xpk;
76+
}
77+
y[k] = xpk;
78+
}
79+
//*ppk = xpk; // save xpk for next time
80+
state_ppk = xpk;
81+
}
82+
83+
//convert time constants from seconds to unitless parameters, from CHAPRO, agc_prepare.c
84+
void setAttackRelease_msec(const float atk_msec, const float rel_msec) {
85+
given_attack_msec = atk_msec;
86+
given_release_msec = rel_msec;
87+
88+
// convert ANSI attack & release times to filter time constants
89+
float ansi_atk = 0.001f * atk_msec * sample_rate_Hz / 2.425f;
90+
float ansi_rel = 0.001f * rel_msec * sample_rate_Hz / 1.782f;
91+
alfa = (float) (ansi_atk / (1.0f + ansi_atk));
92+
beta = (float) (ansi_rel / (10.f + ansi_rel));
93+
}
94+
95+
void setDefaultValues(void) {
96+
float32_t attack_msec = 5.0f;
97+
float32_t release_msec = 50.0f;
98+
setAttackRelease_msec(attack_msec, release_msec);
99+
state_ppk = 0; //initialize
100+
}
101+
102+
void setSampleRate_Hz(const float &fs_Hz) {
103+
//change params that follow sample rate
104+
105+
sample_rate_Hz = fs_Hz;
106+
}
107+
108+
void resetStates(void) { state_ppk = 1.0; }
109+
float getCurrentLevel(void) { return state_ppk; }
110+
private:
111+
audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module
112+
float32_t sample_rate_Hz;
113+
float32_t given_attack_msec, given_release_msec;
114+
float32_t alfa, beta; //time constants, but in terms of samples, not seconds
115+
float32_t state_ppk = 1.0f;
116+
};
117+
118+
#endif

AudioCalcGainWDRC_F32.h

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* AudioCalcGainWDRC_F32
3+
*
4+
* Created: Chip Audette, Feb 2017
5+
* Purpose: This module calculates the gain needed for wide dynamic range compression.
6+
* Derived From: Core algorithm is from "WDRC_circuit"
7+
* WDRC_circuit from CHAPRO from BTNRC: https://github.com/BTNRH/chapro
8+
* As of Feb 2017, CHAPRO license is listed as "Creative Commons?"
9+
*
10+
* This processes a single stream fo audio data (ie, it is mono)
11+
*
12+
* MIT License. use at your own risk.
13+
*/
14+
15+
#ifndef _AudioCalcGainWDRC_F32_h
16+
#define _AudioCalcGainWDRC_F32_h
17+
18+
#include <arm_math.h> //ARM DSP extensions. for speed!
19+
#include <AudioStream_F32.h>
20+
21+
typedef struct {
22+
float attack; // attack time (ms), unused in this class
23+
float release; // release time (ms), unused in this class
24+
float fs; // sampling rate (Hz), set through other means in this class
25+
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
26+
float tkgain; // compression-start gain
27+
float tk; // compression-start kneepoint
28+
float cr; // compression ratio
29+
float bolt; // broadband output limiting threshold
30+
} CHA_WDRC;
31+
32+
33+
class AudioCalcGainWDRC_F32 : public AudioStream_F32
34+
{
35+
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
36+
//GUI: shortName:calc_WDRCGain
37+
public:
38+
//default constructor
39+
AudioCalcGainWDRC_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { setDefaultValues(); };
40+
41+
//here's the method that does all the work
42+
void update(void) {
43+
44+
//get the input audio data block
45+
audio_block_f32_t *in_block = AudioStream_F32::receiveReadOnly_f32(); // must be the envelope!
46+
if (!in_block) return;
47+
48+
//prepare an output data block
49+
audio_block_f32_t *out_block = AudioStream_F32::allocate_f32();
50+
if (!out_block) return;
51+
52+
// //////////////////////add your processing here!
53+
calcGainFromEnvelope(in_block->data, out_block->data, in_block->length);
54+
out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz;
55+
56+
//transmit the block and be done
57+
AudioStream_F32::transmit(out_block);
58+
AudioStream_F32::release(out_block);
59+
AudioStream_F32::release(in_block);
60+
61+
}
62+
63+
void calcGainFromEnvelope(float *env, float *gain_out, const int n) {
64+
//env = input, signal envelope (not the envelope of the power, but the envelope of the signal itslef)
65+
//gain = output, the gain in natural units (not power, not dB)
66+
//n = input, number of samples to process in each vector
67+
68+
//prepare intermediate data block
69+
audio_block_f32_t *env_dB_block = AudioStream_F32::allocate_f32();
70+
if (!env_dB_block) return;
71+
72+
//convert to dB
73+
for (int k=0; k < n; k++) env_dB_block->data[k] = maxdB + db2(env[k]); //maxdb in the private section
74+
75+
// apply wide-dynamic range compression
76+
WDRC_circuit_gain(env_dB_block->data, gain_out, n, tkgn, tk, cr, bolt);
77+
AudioStream_F32::release(env_dB_block);
78+
}
79+
80+
//original call to WDRC_circuit
81+
//void WDRC_circuit(float *x, float *y, float *pdb, int n, float tkgn, float tk, float cr, float bolt)
82+
//void WDRC_circuit(float *orig_signal, float *signal_out, float *env_dB, int n, float tkgn, float tk, float cr, float bolt)
83+
//modified to output the gain instead of the fully processed signal
84+
void WDRC_circuit_gain(float *env_dB, float *gain_out, const int n,
85+
const float tkgn, const float tk, const float cr, const float bolt) {
86+
87+
float gdb, tkgo, pblt;
88+
int k;
89+
float *pdb = env_dB; //just rename it to keep the code below unchanged
90+
float tk_tmp = tk;
91+
92+
if ((tk_tmp + tkgn) > bolt) {
93+
tk_tmp = bolt - tkgn;
94+
}
95+
tkgo = tkgn + tk_tmp * (1.0f - 1.0f / cr);
96+
pblt = cr * (bolt - tkgo);
97+
const float cr_const = ((1.0f / cr) - 1.0f);
98+
for (k = 0; k < n; k++) {
99+
if ((pdb[k] < tk_tmp) && (cr >= 1.0f)) {
100+
gdb = tkgn;
101+
} else if (pdb[k] > pblt) {
102+
gdb = bolt + ((pdb[k] - pblt) / 10.0f) - pdb[k];
103+
} else {
104+
gdb = cr_const * pdb[k] + tkgo;
105+
}
106+
gain_out[k] = undb2(gdb);
107+
//y[k] = x[k] * undb2(gdb); //apply the gain
108+
}
109+
}
110+
111+
void setDefaultValues(void) {
112+
CHA_WDRC gha = {1.0f, // attack time (ms), IGNORED HERE
113+
50.0f, // release time (ms), IGNORED HERE
114+
24000.0f, // fs, sampling rate (Hz), IGNORED HERE
115+
119.0f, // maxdB, maximum signal (dB SPL)
116+
0.0f, // tkgain, compression-start gain
117+
105.0f, // tk, compression-start kneepoint
118+
10.0f, // cr, compression ratio
119+
105.0f // bolt, broadband output limiting threshold
120+
};
121+
//setParams(gha.maxdB, gha.tkgain, gha.cr, gha.tk, gha.bolt); //also sets calcEnvelope
122+
setParams_from_CHA_WDRC(&gha);
123+
}
124+
void setParams_from_CHA_WDRC(CHA_WDRC *gha) {
125+
setParams(gha->maxdB, gha->tkgain, gha->cr, gha->tk, gha->bolt); //also sets calcEnvelope
126+
}
127+
void setParams(float _maxdB, float _tkgain, float _cr, float _tk, float _bolt) {
128+
maxdB = _maxdB;
129+
tkgn = _tkgain;
130+
tk = _tk;
131+
cr = _cr;
132+
bolt = _bolt;
133+
}
134+
135+
static float undb2(const float &x) { return expf(0.11512925464970228420089957273422f*x); } //faster: exp(log(10.0f)*x/20); this is exact
136+
static float db2(const float &x) { return 6.020599913279623f*log2f_approx(x); } //faster: 20*log2_approx(x)/log2(10); this is approximate
137+
138+
/* ----------------------------------------------------------------------
139+
** Fast approximation to the log2() function. It uses a two step
140+
** process. First, it decomposes the floating-point number into
141+
** a fractional component F and an exponent E. The fraction component
142+
** is used in a polynomial approximation and then the exponent added
143+
** to the result. A 3rd order polynomial is used and the result
144+
** when computing db20() is accurate to 7.984884e-003 dB.
145+
** ------------------------------------------------------------------- */
146+
//https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621
147+
static float log2f_approx(float X) {
148+
//float *C = &log2f_approx_coeff[0];
149+
float Y;
150+
float F;
151+
int E;
152+
153+
// This is the approximation to log2()
154+
F = frexpf(fabsf(X), &E);
155+
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
156+
Y = 1.23149591368684f; //C[0]
157+
Y *= F;
158+
Y += -4.11852516267426f; //C[1]
159+
Y *= F;
160+
Y += 6.02197014179219f; //C[2]
161+
Y *= F;
162+
Y += -3.13396450166353f; //C[3]
163+
Y += E;
164+
165+
return(Y);
166+
}
167+
168+
private:
169+
audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module
170+
float maxdB, tkgn, tk, cr, bolt;
171+
};
172+
173+
#endif

0 commit comments

Comments
 (0)