Libarary: big code update from my Tympan_Library

pull/5/head
Chip Audette 7 years ago
parent d46ab56ed7
commit 81492cc5ac
  1. 2
      AudioCalcEnvelope_F32.h
  2. 101
      AudioCalcGainWDRC_F32.h
  3. 82
      AudioConfigFIRFilterBank_F32.cpp
  4. 78
      AudioConfigFIRFilterBank_F32.h
  5. 101
      AudioControlTester.cpp
  6. 538
      AudioControlTester.h
  7. 2
      AudioConvert_F32.h
  8. 187
      AudioEffectCompWDRC_F32.h
  9. 17
      AudioEffectCompressor_F32.h
  10. 1
      AudioEffectEmpty_F32.h
  11. 5
      AudioEffectGain_F32.h
  12. 41
      AudioFilterBiquad_F32.cpp
  13. 204
      AudioFilterBiquad_F32.h
  14. 55
      AudioFilterFIR_F32.cpp
  15. 53
      AudioFilterFIR_F32.h
  16. 30
      AudioMixer4_F32.cpp
  17. 38
      AudioMixer4_F32.h
  18. 14
      AudioMixer_F32.h
  19. 2
      AudioMultiply_F32.h
  20. 29
      AudioSettings_F32.cpp
  21. 18
      AudioSettings_F32.h
  22. 18
      AudioStream_F32.cpp
  23. 61
      AudioStream_F32.h
  24. 181
      BTNRH_WDRC_Types.h
  25. 19
      OpenAudio_ArduinoLibrary.h
  26. 146
      USB_Audio_F32.h
  27. 96
      control_tlv320aic3206.cpp
  28. 6
      control_tlv320aic3206.h
  29. 4
      examples/BasicCompressor_Float/BasicCompressor_Float.ino
  30. 2
      examples/BasicGain_Float/BasicGain_Float.ino
  31. 6
      examples/MixStereoToMono_Float/MixStereoToMono_Float.ino
  32. 4
      examples/MyAudioEffect_Float/MyAudioEffect_Float.ino
  33. 8
      examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino
  34. 2
      output_i2s_f32.cpp
  35. 2
      play_queue_f32.h
  36. 2
      record_queue_f32.h
  37. 11
      synth_pinknoise_f32.h
  38. 2
      synth_sine_f32.cpp
  39. 5
      synth_whitenoise_f32.h
  40. 393
      utility/BTNRH_rfft.cpp
  41. 19
      utility/BTNRH_rfft.h
  42. 384
      utility/rfft.c

@ -47,7 +47,7 @@ class AudioCalcEnvelope_F32 : public AudioStream_F32
audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); audio_block_f32_t *out_block = AudioStream_F32::allocate_f32();
if (!out_block) return; if (!out_block) return;
// //////////////////////add your processing here! // /////////// put the actual processing here
smooth_env(in_block->data, out_block->data, in_block->length); smooth_env(in_block->data, out_block->data, in_block->length);
out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz; out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz;

@ -17,17 +17,8 @@
#include <arm_math.h> //ARM DSP extensions. for speed! #include <arm_math.h> //ARM DSP extensions. for speed!
#include <AudioStream_F32.h> #include <AudioStream_F32.h>
#include "BTNRH_WDRC_Types.h"
typedef struct {
float attack; // attack time (ms), unused in this class
float release; // release time (ms), unused in this class
float fs; // sampling rate (Hz), set through other means in this class
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
float tkgain; // compression-start gain
float tk; // compression-start kneepoint
float cr; // compression ratio
float bolt; // broadband output limiting threshold
} CHA_WDRC;
class AudioCalcGainWDRC_F32 : public AudioStream_F32 class AudioCalcGainWDRC_F32 : public AudioStream_F32
@ -35,8 +26,9 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:calc_WDRCGain //GUI: shortName:calc_WDRCGain
public: public:
//default constructor //constructors
AudioCalcGainWDRC_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { setDefaultValues(); }; AudioCalcGainWDRC_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { setDefaultValues(); };
AudioCalcGainWDRC_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { setDefaultValues(); };
//here's the method that does all the work //here's the method that does all the work
void update(void) { void update(void) {
@ -49,7 +41,7 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); audio_block_f32_t *out_block = AudioStream_F32::allocate_f32();
if (!out_block) return; if (!out_block) return;
// //////////////////////add your processing here! // ////////////////////// do the processing here!
calcGainFromEnvelope(in_block->data, out_block->data, in_block->length); calcGainFromEnvelope(in_block->data, out_block->data, in_block->length);
out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz; out_block->length = in_block->length; out_block->fs_Hz = in_block->fs_Hz;
@ -69,7 +61,7 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
audio_block_f32_t *env_dB_block = AudioStream_F32::allocate_f32(); audio_block_f32_t *env_dB_block = AudioStream_F32::allocate_f32();
if (!env_dB_block) return; if (!env_dB_block) return;
//convert to dB //convert to dB and calibrate (via maxdB)
for (int k=0; k < n; k++) env_dB_block->data[k] = maxdB + db2(env[k]); //maxdb in the private section for (int k=0; k < n; k++) env_dB_block->data[k] = maxdB + db2(env[k]); //maxdb in the private section
// apply wide-dynamic range compression // apply wide-dynamic range compression
@ -80,48 +72,60 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
//original call to WDRC_circuit //original call to WDRC_circuit
//void WDRC_circuit(float *x, float *y, float *pdb, int n, float tkgn, float tk, float cr, float bolt) //void WDRC_circuit(float *x, float *y, float *pdb, int n, float tkgn, float tk, float cr, float bolt)
//void WDRC_circuit(float *orig_signal, float *signal_out, float *env_dB, int n, float tkgn, float tk, float cr, float bolt) //void WDRC_circuit(float *orig_signal, float *signal_out, float *env_dB, int n, float tkgn, float tk, float cr, float bolt)
//modified to output the gain instead of the fully processed signal //modified to output just the gain instead of the fully processed signal
void WDRC_circuit_gain(float *env_dB, float *gain_out, const int n, void WDRC_circuit_gain(float *env_dB, float *gain_out, const int n,
const float tkgn, const float tk, const float cr, const float bolt) { const float tkgn, const float tk, const float cr, const float bolt)
//tkgn = gain (dB?) at start of compression (ie, gain for linear behavior?)
//tk = compression start kneepoint (pre-compression, dB SPL?)
//cr = compression ratio
//bolt = broadband output limiting threshold (post-compression, dB SPL?)
{
//tkgain = 30; tk = 50; bolt = 100; cr = 3;
float gdb, tkgo, pblt; float gdb, tkgo, pblt;
int k; int k;
float *pdb = env_dB; //just rename it to keep the code below unchanged float *pdb = env_dB; //just rename it to keep the code below unchanged (input SPL dB)
float tk_tmp = tk; float tk_tmp = tk; //temporary, threshold for start of compression (input SPL dB)
if ((tk_tmp + tkgn) > bolt) { if ((tk_tmp + tkgn) > bolt) { //after gain, would the compression threshold be above the output-limitting threshold ("bolt")
tk_tmp = bolt - tkgn; tk_tmp = bolt - tkgn; //if so, lower the compression threshold to be the pre-gain value resulting in "bolt"
} }
tkgo = tkgn + tk_tmp * (1.0f - 1.0f / cr);
pblt = cr * (bolt - tkgo); tkgo = tkgn + tk_tmp * (1.0f - 1.0f / cr); //intermediate calc
const float cr_const = ((1.0f / cr) - 1.0f); pblt = cr * (bolt - tkgo); //calc input level (dB) where we need to start limiting, no just compression
for (k = 0; k < n; k++) { const float cr_const = ((1.0f / cr) - 1.0f); //pre-calc a constant that we'll need later
if ((pdb[k] < tk_tmp) && (cr >= 1.0f)) {
gdb = tkgn; for (k = 0; k < n; k++) { //loop over each sample
} else if (pdb[k] > pblt) { if ((pdb[k] < tk_tmp) && (cr >= 1.0f)) { //if below threshold and we're compressing
gdb = bolt + ((pdb[k] - pblt) / 10.0f) - pdb[k]; gdb = tkgn; //we're in the linear region. Apply linear gain.
} else if (pdb[k] > pblt) { //we're beyond the compression region into the limitting region
gdb = bolt + ((pdb[k] - pblt) / 10.0f) - pdb[k]; //10:1 limiting!
} else { } else {
gdb = cr_const * pdb[k] + tkgo; gdb = cr_const * pdb[k] + tkgo;
} }
gain_out[k] = undb2(gdb); gain_out[k] = undb2(gdb);
//y[k] = x[k] * undb2(gdb); //apply the gain //y[k] = x[k] * undb2(gdb); //apply the gain
}
}
last_gain = gain_out[n-1]; //hold this value, in case the user asks for it later (not needed for the algorithm)
} }
void setDefaultValues(void) { void setDefaultValues(void) { //set as limiter
CHA_WDRC gha = {1.0f, // attack time (ms), IGNORED HERE BTNRH_WDRC::CHA_WDRC gha = {
50.0f, // release time (ms), IGNORED HERE 5.0f, // attack time (ms)
24000.0f, // fs, sampling rate (Hz), IGNORED HERE 50.0f, // release time (ms)
119.0f, // maxdB, maximum signal (dB SPL) 24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED!
0.0f, // tkgain, compression-start gain 115.0f, // maxdB, maximum signal (dB SPL)...assumed SPL for full-scale input signal
105.0f, // tk, compression-start kneepoint 0.0f, // tkgain, compression-start gain (dB)
10.0f, // cr, compression ratio 55.0f, // tk, compression-start kneepoint (dB SPL)
105.0f // bolt, broadband output limiting threshold 1.0f, // cr, compression ratio (set to 1.0 to defeat)
100.0f // bolt, broadband output limiting threshold (ie, the limiter. SPL. 10:1 comp ratio)
}; };
//setParams(gha.maxdB, gha.tkgain, gha.cr, gha.tk, gha.bolt); //also sets calcEnvelope //setParams(gha.maxdB, gha.tkgain, gha.cr, gha.tk, gha.bolt); //also sets calcEnvelope
setParams_from_CHA_WDRC(&gha); setParams_from_CHA_WDRC(&gha);
} }
void setParams_from_CHA_WDRC(CHA_WDRC *gha) { void setParams_from_CHA_WDRC(BTNRH_WDRC::CHA_WDRC *gha) {
setParams(gha->maxdB, gha->tkgain, gha->cr, gha->tk, gha->bolt); //also sets calcEnvelope setParams(gha->maxdB, gha->tkgain, gha->cr, gha->tk, gha->bolt); //also sets calcEnvelope
} }
void setParams(float _maxdB, float _tkgain, float _cr, float _tk, float _bolt) { void setParams(float _maxdB, float _tkgain, float _cr, float _tk, float _bolt) {
@ -132,6 +136,26 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
bolt = _bolt; bolt = _bolt;
} }
void setKneeLimiter_dBSPL(float _bolt) { bolt = _bolt; }
void setKneeLimiter_dBFS(float _bolt_dBFS) { //convert to dB SPL
float bolt_dBSPL = maxdB + _bolt_dBFS;
setKneeLimiter_dBSPL(bolt_dBSPL);
}
void setGain_dB(float _gain_dB) { tkgn = _gain_dB; } //gain at start of compression
void setKneeCompressor_dBSPL(float _tk) { tk = _tk; }
void setKneeCompressor_dBFS(float _tk_dBFS) { // convert to dB SPL
float tk_dBSPL = maxdB + _tk_dBFS;
setKneeCompressor_dBSPL(tk_dBSPL);
}
void setCompRatio(float _cr) { cr = _cr; };
void setMaxdB(float _maxdB) { maxdB = _maxdB; }
float getGain_dB(void) { return tkgn; } //returns the linear gain of the system
float getCurrentGain(void) { return last_gain; }
float getCurrentGain_dB(void) { return db2(getCurrentGain()); }
//dB functions. Feed it the envelope amplitude (not squared) and it computes 20*log10(x) or it does 10.^(x/20)
static float undb2(const float &x) { return expf(0.11512925464970228420089957273422f*x); } //faster: exp(log(10.0f)*x/20); this is exact static float undb2(const float &x) { return expf(0.11512925464970228420089957273422f*x); } //faster: exp(log(10.0f)*x/20); this is exact
static float db2(const float &x) { return 6.020599913279623f*log2f_approx(x); } //faster: 20*log2_approx(x)/log2(10); this is approximate static float db2(const float &x) { return 6.020599913279623f*log2f_approx(x); } //faster: 20*log2_approx(x)/log2(10); this is approximate
@ -168,6 +192,7 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32
private: private:
audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module
float maxdB, tkgn, tk, cr, bolt; float maxdB, tkgn, tk, cr, bolt;
float last_gain = 1.0; //what was the last gain value computed for the signal
}; };
#endif #endif

@ -0,0 +1,82 @@
/*
* fir_filterbank.cpp
*
* Created: Chip Audette, Creare LLC, Feb 2017
* Primarly built upon CHAPRO "Generic Hearing Aid" from
* Boys Town National Research Hospital (BTNRH): https://github.com/BTNRH/chapro
*
* License: MIT License. Use at your own risk.
*
*/
#include "AudioConfigFIRFilterBank_F32.h"
#include "utility/BTNRH_rfft.h"
void AudioConfigFIRFilterBank_F32::fir_filterbank(float *bb, float *cf, const int nc, const int nw_orig, const int wt, const float sr)
{
double p, w, a = 0.16, sm = 0;
float *ww, *bk, *xx, *yy;
int j, k, kk, nt, nf, ns, *be;
int nw = nextPowerOfTwo(nw_orig);
//Serial.print("AudioConfigFIRFilterBank: fir_filterbank: nw_orig = "); Serial.print(nw_orig);
//Serial.print(", nw = "); Serial.println(nw);
nt = nw * 2; //we're going to do an fft that's twice as long (zero padded)
nf = nw + 1; //number of bins to nyquist in the zero-padded FFT. Also nf = nt/2+1
ns = nf * 2;
be = (int *) calloc(nc + 1, sizeof(int));
ww = (float *) calloc(nw, sizeof(float));
xx = (float *) calloc(ns, sizeof(float));
yy = (float *) calloc(ns, sizeof(float));
// window
for (j = 0; j < nw; j++) ww[j]=0.0f; //clear
for (j = 0; j < nw_orig; j++) {
p = M_PI * (2.0 * j - nw_orig) / nw_orig;
if (wt == 0) {
w = 0.54 + 0.46 * cos(p); // Hamming
} else if (wt==1) {
w = (1 - a + cos(p) + a * cos(2 * p)) / 2; // Blackman
} else {
//win = (1 - cos(2*pi*[1:N]/(N+1)))/2; //WEA's matlab call, indexing starts from 1, not zero
w = (1.0 - cosf(2.0*M_PI*((float)(j))/((float)(nw_orig-1))))/2.0;
}
sm += w;
ww[j] = (float) w;
}
// frequency bands...add the DC-facing band and add the Nyquist-facing band
be[0] = 0;
for (k = 1; k < nc; k++) {
kk = round(nf * cf[k - 1] * (2 / sr)); //original
be[k] = (kk > nf) ? nf : kk;
}
be[nc] = nf;
// channel tranfer functions
fzero(xx, ns);
xx[nw_orig / 2] = 1; //make a single-sample impulse centered on our eventual window
BTNRH_FFT::cha_fft_rc(xx, nt);
for (k = 0; k < nc; k++) {
fzero(yy, ns); //zero the temporary output
//int nbins = (be[k + 1] - be[k]) * 2; Serial.print("fir_filterbank: chan ");Serial.print(k); Serial.print(", nbins = ");Serial.println(nbins);
fcopy(yy + be[k] * 2, xx + be[k] * 2, (be[k + 1] - be[k]) * 2); //copy just our passband
BTNRH_FFT::cha_fft_cr(yy, nt); //IFFT back into the time domain
// apply window to iFFT of bandpass
for (j = 0; j < nw; j++) {
yy[j] *= ww[j];
}
bk = bb + k * nw_orig; //pointer to location in output array
fcopy(bk, yy, nw_orig); //copy the filter coefficients to the output array
//print out the coefficients
//for (int i=0; i<nw; i++) { Serial.print(yy[i]*1000.0f);Serial.print(" "); }; Serial.println();
}
free(be);
free(ww);
free(xx);
free(yy);
}

@ -12,7 +12,8 @@
#ifndef AudioConfigFIRFilterBank_F32_h #ifndef AudioConfigFIRFilterBank_F32_h
#define AudioConfigFIRFilterBank_F32_h #define AudioConfigFIRFilterBank_F32_h
#include "utility/rfft.c" //include <Tympan_Library.h>
#include <OpenAudio_ArduinoLibrary.h>
#define fmove(x,y,n) memmove(x,y,(n)*sizeof(float)) #define fmove(x,y,n) memmove(x,y,(n)*sizeof(float))
#define fcopy(x,y,n) memcpy(x,y,(n)*sizeof(float)) #define fcopy(x,y,n) memcpy(x,y,(n)*sizeof(float))
@ -22,9 +23,10 @@ class AudioConfigFIRFilterBank_F32 {
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node //GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName:config_FIRbank //GUI: shortName:config_FIRbank
public: public:
AudioConfigFIRFilterBank_F32(void) { AudioConfigFIRFilterBank_F32(void) {}
} AudioConfigFIRFilterBank_F32(const AudioSettings_F32 &settings) {}
AudioConfigFIRFilterBank_F32(const int n_chan, const int n_fir, const float sample_rate_Hz, float *corner_freq, float *filter_coeff) { AudioConfigFIRFilterBank_F32(const int n_chan, const int n_fir, const float sample_rate_Hz, float *corner_freq, float *filter_coeff)
{
createFilterCoeff(n_chan, n_fir, sample_rate_Hz, corner_freq, filter_coeff); createFilterCoeff(n_chan, n_fir, sample_rate_Hz, corner_freq, filter_coeff);
} }
@ -51,7 +53,7 @@ class AudioConfigFIRFilterBank_F32 {
flag__free_cf = 1; flag__free_cf = 1;
computeLogSpacedCornerFreqs(n_chan, sample_rate_Hz, cf); computeLogSpacedCornerFreqs(n_chan, sample_rate_Hz, cf);
} }
const int window_type = 0; //0 = Hamming const int window_type = 0; //0 = Hamming, 1=Blackmann, 2 = Hanning
fir_filterbank(filter_coeff, cf, n_chan, n_fir, window_type, sample_rate_Hz); fir_filterbank(filter_coeff, cf, n_chan, n_fir, window_type, sample_rate_Hz);
if (flag__free_cf) free(cf); if (flag__free_cf) free(cf);
} }
@ -84,71 +86,7 @@ class AudioConfigFIRFilterBank_F32 {
return n; return n;
} }
void fir_filterbank(float *bb, float *cf, const int nc, const int nw_orig, const int wt, const float sr) void fir_filterbank(float *bb, float *cf, const int nc, const int nw_orig, const int wt, const float sr);
{
double p, w, a = 0.16, sm = 0;
float *ww, *bk, *xx, *yy;
int j, k, kk, nt, nf, ns, *be;
int nw = nextPowerOfTwo(nw_orig);
Serial.print("fir_filterbank: nw_orig = "); Serial.print(nw_orig);
Serial.print(", nw = "); Serial.println(nw);
nt = nw * 2;
nf = nw + 1;
ns = nf * 2;
be = (int *) calloc(nc + 1, sizeof(int));
ww = (float *) calloc(nw, sizeof(float));
xx = (float *) calloc(ns, sizeof(float));
yy = (float *) calloc(ns, sizeof(float));
// window
for (j = 0; j < nw; j++) ww[j]=0.0f; //clear
for (j = 0; j < nw_orig; j++) {
p = M_PI * (2.0 * j - nw_orig) / nw_orig;
if (wt == 0) {
w = 0.54 + 0.46 * cos(p); // Hamming
} else {
w = (1 - a + cos(p) + a * cos(2 * p)) / 2; // Blackman
}
sm += w;
ww[j] = (float) w;
}
// frequency bands...add the DC-facing band and add the Nyquist-facing band
be[0] = 0;
for (k = 1; k < nc; k++) {
kk = round(nf * cf[k - 1] * (2 / sr));
be[k] = (kk > nf) ? nf : kk;
}
be[nc] = nf;
// channel tranfer functions
fzero(xx, ns);
xx[nw_orig / 2] = 1; //make a single-sample impulse centered on our eventual window
cha_fft_rc(xx, nt);
for (k = 0; k < nc; k++) {
fzero(yy, ns); //zero the temporary output
//int nbins = (be[k + 1] - be[k]) * 2; Serial.print("fir_filterbank: chan ");Serial.print(k); Serial.print(", nbins = ");Serial.println(nbins);
fcopy(yy + be[k] * 2, xx + be[k] * 2, (be[k + 1] - be[k]) * 2); //copy just our passband
cha_fft_cr(yy, nt); //IFFT back into the time domain
// apply window to iFFT of bandpass
for (j = 0; j < nw; j++) {
yy[j] *= ww[j];
}
bk = bb + k * nw_orig; //pointer to location in output array
fcopy(bk, yy, nw_orig); //copy the filter coefficients to the output array
//print out the coefficients
//for (int i=0; i<nw; i++) { Serial.print(yy[i]*1000.0f);Serial.print(" "); }; Serial.println();
}
free(be);
free(ww);
free(xx);
free(yy);
}
}; };
#endif #endif

@ -0,0 +1,101 @@
#include "AudioControlTester.h"
void AudioTestSignalGenerator_F32::update(void) {
//receive the input audio data
audio_block_f32_t *in_block = AudioStream_F32::receiveReadOnly_f32();
if (!in_block) return;
//if we're not testing, just transmit the block
if (!is_testing) {
AudioStream_F32::transmit(in_block); // send the FIR output
AudioStream_F32::release(in_block);
return;
}
//if we are testing, we're going to substitute a new signal,
//so we can release the block for the original signal
AudioStream_F32::release(in_block);
//allocate memory for block that we'll send
audio_block_f32_t *out_block = allocate_f32();
if (!out_block) return;
//now generate the new siginal
sine_gen.begin(); record_queue.begin(); //activate
sine_gen.update(); sine_gen.end();
gain_alg.update();
record_queue.update(); record_queue.end();
audio_block_f32_t *queue_block = record_queue.getAudioBlock();
//copy to out_block (why can't I just send the queue_block? I tried. It doesn't seem to work. try it again later!
for (int i=0; i < queue_block->length; i++) out_block->data[i] = queue_block->data[i];
record_queue.freeAudioBlock();
//send the data
AudioStream_F32::transmit(out_block); // send the FIR output
AudioStream_F32::release(out_block);
}
void AudioTestSignalMeasurement_F32::update(void) {
//if we're not testing, just return
if (!is_testing) {
return;
}
//receive the input audio data...the baseline and the test
audio_block_f32_t *in_block_baseline = AudioStream_F32::receiveReadOnly_f32(0);
if (!in_block_baseline) return;
audio_block_f32_t *in_block_test = AudioStream_F32::receiveReadOnly_f32(1);
if (!in_block_test) {
AudioStream_F32::release(in_block_baseline);
return;
}
//compute the rms of both signals
float baseline_rms = computeRMS(in_block_baseline->data, in_block_baseline->length);
float test_rms = computeRMS(in_block_test->data, in_block_test->length);
//Release memory
AudioStream_F32::release(in_block_baseline);
AudioStream_F32::release(in_block_test);
//notify controller
if (testController != NULL) testController->transferRMSValues(baseline_rms, test_rms);
}
void AudioTestSignalMeasurementMulti_F32::update(void) {
//if we're not testing, just return
if (!is_testing) {
return;
}
//receive the input audio data...the baseline and the test
audio_block_f32_t *in_block_baseline = AudioStream_F32::receiveReadOnly_f32(0);
if (in_block_baseline==NULL) return;
float baseline_rms = computeRMS(in_block_baseline->data, in_block_baseline->length);
AudioStream_F32::release(in_block_baseline);
//loop over each of the test data connections
float test_rms[num_test_values];
int n_with_data = 0;
for (int Ichan=0; Ichan < num_test_values; Ichan++) {
audio_block_f32_t *in_block_test = AudioStream_F32::receiveReadOnly_f32(1+Ichan);
if (in_block_test==NULL) {
//no data
test_rms[Ichan]=0.0f;
} else {
//process data
n_with_data = Ichan+1;
test_rms[Ichan]=computeRMS(in_block_test->data, in_block_test->length);
AudioStream_F32::release(in_block_test);
}
}
//notify controller
if (testController != NULL) testController->transferRMSValues(baseline_rms, test_rms, n_with_data);
}

@ -0,0 +1,538 @@
#ifndef _AudioControlTester_h
#define _AudioControlTester_h
//include <Tympan_Library.h>
#include <OpenAudio_ArduinoLibrary.h>
#define max_steps 64
#define max_num_chan 16 //max number of test signal inputs to the AudioTestSignalMeasurementMulti_F32
//prototypes
class AudioTestSignalGenerator_F32;
class AudioTestSignalMeasurementInterface_F32;
class AudioTestSignalMeasurement_F32;
class AudioTestSignalMeasurementMulti_F32;
class AudioControlSignalTesterInterface_F32;
class AudioControlSignalTester_F32;
class AudioControlTestAmpSweep_F32;
class AudioControlTestFreqSweep_F32;
// class definitions
class AudioTestSignalGenerator_F32 : public AudioStream_F32
{
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName: testSignGen
public:
AudioTestSignalGenerator_F32(void): AudioStream_F32(1,inputQueueArray) {
setSampleRate_Hz(AUDIO_SAMPLE_RATE);
setDefaultValues();
makeConnections();
}
AudioTestSignalGenerator_F32(const AudioSettings_F32 &settings): AudioStream_F32(1,inputQueueArray) {
setAudioSettings(settings);
setDefaultValues();
makeConnections();
}
~AudioTestSignalGenerator_F32(void) {
if (patchCord1 != NULL) delete patchCord1;
}
void setAudioSettings(const AudioSettings_F32 &settings) {
setSampleRate_Hz(settings.sample_rate_Hz);
}
void setSampleRate_Hz(const float _fs_Hz) {
//pass this data on to its components that care
sine_gen.setSampleRate_Hz(_fs_Hz);
}
void makeConnections(void) {
patchCord1 = new AudioConnection_F32(sine_gen, 0, gain_alg, 0);
patchCord2 = new AudioConnection_F32(gain_alg, 0, record_queue, 0);
}
virtual void update(void);
void begin(void) {
is_testing = true;
//if (Serial) Serial.println("AudioTestSignalGenerator_F32: begin(): ...");
}
void end(void) { is_testing = false; }
AudioSynthWaveformSine_F32 sine_gen;
AudioEffectGain_F32 gain_alg;
AudioRecordQueue_F32 record_queue;
AudioConnection_F32 *patchCord1;
AudioConnection_F32 *patchCord2;
void amplitude(float val) {
sine_gen.amplitude(1.0);
gain_alg.setGain(val);
}
void frequency(float val) {
sine_gen.frequency(val);
}
virtual void setSignalAmplitude_dBFS(float val_dBFS) {
amplitude(sqrtf(2.0)*sqrtf(powf(10.0f,0.1*val_dBFS)));
};
virtual void setSignalFrequency_Hz(float val_Hz) {
frequency(val_Hz);
}
private:
bool is_testing = false;
audio_block_f32_t *inputQueueArray[1];
void setDefaultValues(void) {
sine_gen.end(); //disable it for now
record_queue.end(); //disable it for now;
is_testing = false;
frequency(1000.f);
amplitude(0.0f);
}
};
// //////////////////////////////////////////////////////////////////////////
class AudioTestSignalMeasurementInterface_F32 {
public:
AudioTestSignalMeasurementInterface_F32 (void) {};
void setAudioSettings(const AudioSettings_F32 &settings) {
setSampleRate_Hz(settings.sample_rate_Hz);
}
void setSampleRate_Hz(const float _fs_Hz) {
//pass this data on to its components that care. None care right now.
}
virtual void update(void);
virtual float computeRMS(float data[], int n) {
float rms_value;
arm_rms_f32 (data, n, &rms_value);
return rms_value;
}
virtual void begin(AudioControlSignalTester_F32 *p_controller) {
//if (Serial) Serial.println("AudioTestSignalMeasurement_F32: begin(): ...");
testController = p_controller;
is_testing = true;
}
virtual void end(void) {
//if (Serial) Serial.println("AudioTestSignalMeasurement_F32: end(): ...");
testController = NULL;
is_testing = false;
}
protected:
bool is_testing = false;
//audio_block_f32_t *inputQueueArray[2];
AudioControlSignalTester_F32 *testController = NULL;
virtual void setDefaultValues(void) {
is_testing = false;
}
};
class AudioTestSignalMeasurement_F32 : public AudioStream_F32, public AudioTestSignalMeasurementInterface_F32
{
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName: testSigMeas
public:
AudioTestSignalMeasurement_F32(void): AudioStream_F32(2,inputQueueArray) {
setSampleRate_Hz(AUDIO_SAMPLE_RATE);
setDefaultValues();
}
AudioTestSignalMeasurement_F32(const AudioSettings_F32 &settings): AudioStream_F32(2,inputQueueArray) {
setAudioSettings(settings);
setDefaultValues();
}
void update(void);
private:
audio_block_f32_t *inputQueueArray[2];
};
class AudioTestSignalMeasurementMulti_F32 : public AudioStream_F32, public AudioTestSignalMeasurementInterface_F32
{
//GUI: inputs:10, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName: testSigMeas
public:
AudioTestSignalMeasurementMulti_F32(void): AudioStream_F32(max_num_chan+1,inputQueueArray) {
setSampleRate_Hz(AUDIO_SAMPLE_RATE);
setDefaultValues();
}
AudioTestSignalMeasurementMulti_F32(const AudioSettings_F32 &settings): AudioStream_F32(max_num_chan+1,inputQueueArray) {
setAudioSettings(settings);
setDefaultValues();
}
void update(void);
private:
//int num_input_connections = max_num_chan+1;
int num_test_values = max_num_chan;
audio_block_f32_t *inputQueueArray[max_num_chan+1];
};
// ///////////////////////////////////////////////////////////////////////////////////
class AudioControlSignalTesterInterface_F32 {
public:
AudioControlSignalTesterInterface_F32(void) {};
//virtual void setAudioBlockSamples(void) = 0;
//virtual void setSampleRate_hz(void) = 0;
virtual void begin(void) = 0;
virtual void end(void) = 0;
virtual void setStepPattern(float, float, float) = 0;
virtual void transferRMSValues(float, float) = 0;
virtual void transferRMSValues(float, float *, int) = 0;
virtual bool available(void) = 0;
};
class AudioControlSignalTester_F32 : public AudioControlSignalTesterInterface_F32
{
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName: sigTest(Abstract)
public:
AudioControlSignalTester_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
: AudioControlSignalTesterInterface_F32(), sig_gen(_sig_gen), sig_meas(_sig_meas) {
setAudioBlockSamples(settings.audio_block_samples);
setSampleRate_Hz(settings.sample_rate_Hz);
resetState();
}
virtual void begin(void) {
Serial.println("AudioControlSignalTester_F32: begin(): ...");
recomputeTargetCountsPerStep(); //not needed, just to print some debugging messages
//activate the instrumentation
sig_gen.begin();
sig_meas.begin(this);
//start the test
resetState();
gotoNextStep();
}
//use this to cancel the test
virtual void end(void) {
finishTest();
}
void setAudioSettings(AudioSettings_F32 audio_settings) {
setAudioBlockSamples(audio_settings.audio_block_samples);
setSampleRate_Hz(audio_settings.sample_rate_Hz);
}
void setAudioBlockSamples(int block_samples) {
audio_block_samples = block_samples;
recomputeTargetCountsPerStep();
}
void setSampleRate_Hz(float fs_Hz) {
sample_rate_Hz = fs_Hz;
recomputeTargetCountsPerStep();
}
//define how long (seconds) to spend at each step of the test
void setTargetDurPerStep_sec(float sec) {
if (sec > 0.001) {
target_dur_per_step_sec = sec;
recomputeTargetCountsPerStep();
} else {
Serial.print(F("AudioControlSignalTester_F32: setTargetDurPerStep_sec: given duration too short: "));
Serial.print(target_dur_per_step_sec);
Serial.print(F(". Ignoring..."));
return;
}
}
virtual void setStepPattern(float _start_val, float _end_val, float _step_val) {
start_val = _start_val; end_val = _end_val; step_val = _step_val;
recomputeTargetNumberOfSteps();
}
virtual void transferRMSValues(float baseline_rms, float test_rms) {
transferRMSValues(baseline_rms, &test_rms, 1);
}
virtual void transferRMSValues(float baseline_rms, float *test_rms, int num_chan) {
if (counter_ignore > 0) {
//ignore this reading
counter_ignore--;
return;
}
given_num_chan = num_chan;
if (given_num_chan > max_num_chan) {
Serial.println(F("AudioControlSignalTester_F32: transferRMSValues: *** ERROR ***"));
Serial.print(F(" : num_chan (")); Serial.print(num_chan); Serial.print(")");
Serial.print(F(" is bigger max_num_chan (")); Serial.println(max_num_chan);
Serial.println(F(" : Skipping..."));
return;
}
//add this number
sum_sig_pow_baseline[counter_step] += (baseline_rms*baseline_rms);
for (int Ichan=0; Ichan < num_chan; Ichan++) {
sum_sig_pow_test[counter_step][Ichan] += (test_rms[Ichan]*test_rms[Ichan]);
}
freq_at_each_step_Hz[counter_step] = signal_frequency_Hz;
counter_sum[counter_step]++;
//have all the channels checked in?
if (counter_sum[counter_step] >= target_counts_per_step) {
gotoNextStep();
}
}
virtual void setSignalFrequency_Hz(float freq_Hz) {
signal_frequency_Hz = freq_Hz;
sig_gen.setSignalFrequency_Hz(signal_frequency_Hz);
}
virtual void setSignalAmplitude_dBFS(float amp_dBFS) {
signal_amplitude_dBFS = amp_dBFS;
sig_gen.setSignalAmplitude_dBFS(amp_dBFS);
}
virtual void printTableOfResults(Stream *s) {
float ave1_dBFS, ave2_dBFS, ave3_dBFS, gain_dB, total_pow, total_wav, foo_pow;
s->println(" : Freq (Hz), Input (dBFS), Per-Chan Output (dBFS), Total Gain (inc) (dB), Total Gain (coh) (dB)");
//s->print(" : given_num_chan = ");s->println(given_num_chan);
for (int i=0; i < target_n_steps; i++) {
ave1_dBFS = 10.f*log10f(sum_sig_pow_baseline[i]/counter_sum[i]);
s->print(" "); s->print(freq_at_each_step_Hz[i],0);
s->print(", "); s->print(ave1_dBFS,1);
total_pow = 0.0f;
total_wav = 0.0f;
for (int Ichan=0; Ichan < given_num_chan; Ichan++) {
if (Ichan==0) {
s->print(", ");
} else {
s->print(", ");
}
foo_pow = sum_sig_pow_test[i][Ichan]/counter_sum[i];
ave2_dBFS = 10.f*log10f(foo_pow);
s->print(ave2_dBFS,1);
total_pow += foo_pow; //sum as if it's noise being recombined incoherently
total_wav += sqrtf(foo_pow); //sum as it it's a in-phase tone being combined coherently
}
ave2_dBFS = 10.f*log10f(total_pow);
gain_dB = ave2_dBFS - ave1_dBFS;
s->print(", "); s->print(gain_dB,2);
ave3_dBFS = 20.f*log10f(total_wav);
gain_dB = ave3_dBFS - ave1_dBFS;
s->print(", "); s->println(gain_dB,2);
}
}
bool isDataAvailable = false;
bool available(void) { return isDataAvailable; }
protected:
AudioTestSignalGenerator_F32 &sig_gen;
AudioTestSignalMeasurementInterface_F32 &sig_meas;
float signal_frequency_Hz = 1000.f;
float signal_amplitude_dBFS = -50.0f;
int counter_ignore = 0;
//bool is_testing = 0;
int audio_block_samples = AUDIO_BLOCK_SAMPLES;
float sample_rate_Hz = AUDIO_SAMPLE_RATE_EXACT;
float target_dur_per_step_sec = 0.2;
int target_counts_per_step = 1;
//const int max_steps = 64;
float start_val = 0, end_val = 1.f, step_val = 1.f;
int target_n_steps = 1;
int given_num_chan = max_num_chan;
float sum_sig_pow_baseline[max_steps];
float sum_sig_pow_test[max_steps][max_num_chan];
float freq_at_each_step_Hz[max_steps];
int counter_sum[max_steps], counter_step=-1;
int recomputeTargetCountsPerStep(void) {
target_counts_per_step = max(1,(int)((target_dur_per_step_sec * sample_rate_Hz / ((float)audio_block_samples))+0.5)); //round
// if (Serial) {
// Serial.println("AudioControlSignalTester_F32: recomputeTargetCountsPerStep: ");
// Serial.print(" : target_dur_per_step_sec = "); Serial.println(target_dur_per_step_sec);
// Serial.print(" : sample_rate_Hz = "); Serial.println(sample_rate_Hz);
// Serial.print(" : audio_block_samples = "); Serial.println(audio_block_samples);
// Serial.print(" : target_counts_per_step = "); Serial.println(target_counts_per_step);
// }
return target_counts_per_step;
}
virtual int recomputeTargetNumberOfSteps(void) {
return target_n_steps = (int)((end_val - start_val)/step_val + 0.5)+1; //round
}
virtual void resetState(void) {
isDataAvailable = false;
for (int i=0; i<max_steps; i++) {
sum_sig_pow_baseline[i]=0.0f;
for (int Ichan=0; Ichan<max_num_chan; Ichan++) sum_sig_pow_test[i][Ichan]=0.0f;
counter_sum[i] = 0;
}
counter_step = -1;
}
virtual void gotoNextStep(void) {
counter_step++;
//Serial.print("AudioControlSignalTester_F32: gotoNextStep: starting step ");
//Serial.println(counter_step);
if (counter_step >= target_n_steps) {
finishTest();
return;
} else {
counter_ignore = 10; //ignore first 10 packets
counter_sum[counter_step]=0;
sum_sig_pow_baseline[counter_step]=0.0f;
for (int Ichan=0; Ichan < max_num_chan; Ichan++) sum_sig_pow_test[counter_step][Ichan]=0.0f;
updateSignalGenerator();
freq_at_each_step_Hz[counter_step]=0.0f;
}
}
virtual void updateSignalGenerator(void)
{
//if (Serial) Serial.println("AudioControlSignalTester_F32: updateSignalGenerator(): did the child version get called?");
} //override this is a child class!
virtual void finishTest(void) {
//Serial.println("AudioControlSignalTester_F32: finishTest()...");
//disable the test instrumentation
sig_gen.end();
sig_meas.end();
//let listeners know that data is available
isDataAvailable = true;
}
};
// //////////////////////////////////////////////////////////////////////////
class AudioControlTestAmpSweep_F32 : public AudioControlSignalTester_F32
{
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName: ampSweepTester
public:
AudioControlTestAmpSweep_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
: AudioControlSignalTester_F32(settings, _sig_gen,_sig_meas)
{
float start_amp_dB = -100.0f, end_amp_dB = 0.0f, step_amp_dB = 2.5f;
setStepPattern(start_amp_dB, end_amp_dB, step_amp_dB);
setTargetDurPerStep_sec(0.25);
resetState();
}
void begin(void) {
//activate the instrumentation
sig_gen.begin();
sig_meas.begin(this);
//start the test
resetState();
gotoNextStep();
}
//use this to cancel the test
//void end(void) {
// finishTest();
//}
void printTableOfResults(Stream *s) {
s->println("AudioControlTestAmpSweep_F32: Start Table of Results...");
AudioControlSignalTester_F32::printTableOfResults(s);
s->println("AudioControlTestAmpSweep_F32: End Table of Results...");
}
protected:
virtual void updateSignalGenerator(void) {
float new_amp_dB = start_val + ((float)counter_step)*step_val; //start_val and step_val are in parent class
Serial.print("AudioControlTestAmpSweep_F32: updateSignalGenerator(): setting amplitude to (dBFS) ");
Serial.println(new_amp_dB);
setSignalAmplitude_dBFS(new_amp_dB);
}
void finishTest(void) {
//disable the test instrumentation
setSignalAmplitude_dBFS(-1000.0f); //some very quiet value
//do all of the common actions
AudioControlSignalTester_F32::finishTest();
//print results
printTableOfResults(&Serial);
}
//void resetState(void) {
// AudioControlSignalTester_F32::resetState();
//}
};
// //////////////////////////////////////////////////////////////////////////
class AudioControlTestFreqSweep_F32 : public AudioControlSignalTester_F32
{
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName: freqSweepTester
public:
AudioControlTestFreqSweep_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
: AudioControlSignalTester_F32(settings, _sig_gen,_sig_meas)
{
float start_freq_Hz = 125.f, end_freq_Hz = 16000.f, step_freq_octave = sqrtf(2.0);
setStepPattern(start_freq_Hz, end_freq_Hz, step_freq_octave);
setTargetDurPerStep_sec(0.25);
resetState();
}
void begin(void) {
//activate the instrumentation
sig_gen.begin();
sig_meas.begin(this);
//start the test
resetState();
recomputeTargetNumberOfSteps();
gotoNextStep();
}
//use this to cancel the test
//void end(void) {
// finishTest();
//}
void printTableOfResults(Stream *s) {
s->println("AudioControlTestFreqSweep_F32: Start Table of Results...");
AudioControlSignalTester_F32::printTableOfResults(s);
s->println("AudioControlTestFreqSweep_F32: End Table of Results...");
}
protected:
float signal_frequency_Hz = 1000.f;
float signal_amplitude_dBFS = -50.0f;
virtual int recomputeTargetNumberOfSteps(void) {
return target_n_steps = (int)(log10f(end_val/start_val)/log10f(step_val)+0.5) + 1; //round
}
virtual void updateSignalGenerator(void) {
//logarithmically step the frequency
float new_freq_Hz = start_val * powf(step_val,counter_step);
Serial.print("AudioControlTestFreqSweep_F32: updateSignalGenerator(): setting freq to ");
Serial.println(new_freq_Hz);
setSignalFrequency_Hz(new_freq_Hz);
}
void finishTest(void) {
//disable the test instrumentation
setSignalAmplitude_dBFS(-1000.0f); //some very quiet value
setSignalFrequency_Hz(1000.f);
//do all of the common actions
AudioControlSignalTester_F32::finishTest();
//print results
printTableOfResults(&Serial);
}
//void resetState(void) {
// AudioControlSignalTester_F32::resetState();
//}
};
#endif

@ -9,6 +9,8 @@ class AudioConvert_I16toF32 : public AudioStream_F32 //receive Int and transmits
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
public: public:
AudioConvert_I16toF32(void) : AudioStream_F32(1, inputQueueArray_f32) { }; AudioConvert_I16toF32(void) : AudioStream_F32(1, inputQueueArray_f32) { };
AudioConvert_I16toF32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { };
void update(void) { void update(void) {
//get the Int16 block //get the Int16 block
audio_block_t *int_block; audio_block_t *int_block;

@ -12,150 +12,21 @@
#ifndef _AudioEffectCompWDRC_F32 #ifndef _AudioEffectCompWDRC_F32
#define _AudioEffectCompWDRC_F32 #define _AudioEffectCompWDRC_F32
class AudioCalcGainWDRC_F32; //forward declared. Actually defined in later header file, but I need this here to avoid circularity
#include <Arduino.h> #include <Arduino.h>
#include <AudioStream_F32.h> #include <AudioStream_F32.h>
#include <arm_math.h> #include <arm_math.h>
#include <AudioCalcEnvelope_F32.h> #include <AudioCalcEnvelope_F32.h>
#include "AudioCalcGainWDRC_F32.h" //has definition of CHA_WDRC #include <AudioCalcGainWDRC_F32.h> //has definition of CHA_WDRC
#include "utility/textAndStringUtils.h" #include "BTNRH_WDRC_Types.h"
//#include "utility/textAndStringUtils.h"
// from CHAPRO cha_ff.h
#define DSL_MXCH 32
//class CHA_DSL {
typedef struct {
//public:
//CHA_DSL(void) {};
//static const int DSL_MXCH = 32; // maximum number of channels
float attack; // attack time (ms)
float release; // release time (ms)
float maxdB; // maximum signal (dB SPL)
int ear; // 0=left, 1=right
int nchannel; // number of channels
float cross_freq[DSL_MXCH]; // cross frequencies (Hz)
float tkgain[DSL_MXCH]; // compression-start gain
float cr[DSL_MXCH]; // compression ratio
float tk[DSL_MXCH]; // compression-start kneepoint
float bolt[DSL_MXCH]; // broadband output limiting threshold
} CHA_DSL;
/* int parseStringIntoDSL(String &text_buffer) {
int position = 0;
float foo_val;
const bool print_debug = false;
if (print_debug) Serial.println("parseTextAsDSL: values from file:");
position = parseNextNumberFromString(text_buffer, position, foo_val);
attack = foo_val;
if (print_debug) { Serial.print(" attack: "); Serial.println(attack); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
release = foo_val;
if (print_debug) { Serial.print(" release: "); Serial.println(release); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
maxdB = foo_val;
if (print_debug) { Serial.print(" maxdB: "); Serial.println(maxdB); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
ear = int(foo_val + 0.5); //round
if (print_debug) { Serial.print(" ear: "); Serial.println(ear); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
nchannel = int(foo_val + 0.5); //round
if (print_debug) { Serial.print(" nchannel: "); Serial.println(nchannel); }
//check to see if the number of channels is acceptable.
if ((nchannel < 0) || (nchannel > DSL_MXCH)) {
if (print_debug) Serial.print(" : channel number is too big (or negative). stopping.");
return -1;
}
//read the cross-over frequencies. There should be nchan-1 of them (0 and Nyquist are assumed)
if (print_debug) Serial.print(" cross_freq: ");
for (int i=0; i < (nchannel-1); i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
cross_freq[i] = foo_val;
if (print_debug) { Serial.print(cross_freq[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the tkgain values. There should be nchan of them
if (print_debug) Serial.print(" tkgain: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
tkgain[i] = foo_val;
if (print_debug) { Serial.print(tkgain[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the cr values. There should be nchan of them
if (print_debug) Serial.print(" cr: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
cr[i] = foo_val;
if (print_debug) { Serial.print(cr[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the tk values. There should be nchan of them
if (print_debug) Serial.print(" tk: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
tk[i] = foo_val;
if (print_debug) { Serial.print(tk[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the bolt values. There should be nchan of them
if (print_debug) Serial.print(" bolt: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
bolt[i] = foo_val;
if (print_debug) { Serial.print(bolt[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
return 0;
}
void printToStream(Stream *s) {
s->print("CHA_DSL: attack (ms) = "); s->println(attack);
s->print(" : release (ms) = "); s->println(release);
s->print(" : maxdB (dB SPL) = "); s->println(maxdB);
s->print(" : ear (0 = left, 1 = right) "); s->println(ear);
s->print(" : nchannel = "); s->println(nchannel);
s->print(" : cross_freq (Hz) = ");
for (int i=0; i<nchannel-1;i++) { s->print(cross_freq[i]); s->print(", ");}; s->println();
s->print(" : tkgain = ");
for (int i=0; i<nchannel;i++) { s->print(tkgain[i]); s->print(", ");}; s->println();
s->print(" : cr = ");
for (int i=0; i<nchannel;i++) { s->print(cr[i]); s->print(", ");}; s->println();
s->print(" : tk = ");
for (int i=0; i<nchannel;i++) { s->print(tk[i]); s->print(", ");}; s->println();
s->print(" : bolt = ");
for (int i=0; i<nchannel;i++) { s->print(bolt[i]); s->print(", ");}; s->println();
}
} ; */
typedef struct {
float alfa; // attack constant (not time)
float beta; // release constant (not time
float fs; // sampling rate (Hz)
float maxdB; // maximum signal (dB SPL)
float tkgain; // compression-start gain
float tk; // compression-start kneepoint
float cr; // compression ratio
float bolt; // broadband output limiting threshold
} CHA_DVAR_t;
class AudioEffectCompWDRC_F32 : public AudioStream_F32 class AudioEffectCompWDRC_F32 : public AudioStream_F32
{ {
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName: CompWDRC //GUI: shortName: CompressWDRC
public: public:
AudioEffectCompWDRC_F32(void): AudioStream_F32(1,inputQueueArray) { //need to modify this for user to set sample rate AudioEffectCompWDRC_F32(void): AudioStream_F32(1,inputQueueArray) { //need to modify this for user to set sample rate
setSampleRate_Hz(AUDIO_SAMPLE_RATE); setSampleRate_Hz(AUDIO_SAMPLE_RATE);
@ -178,7 +49,7 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32
if (!out_block) return; if (!out_block) return;
//do the algorithm //do the algorithm
cha_agc_channel(block->data, out_block->data, block->length); compress(block->data, out_block->data, block->length);
// transmit the block and release memory // transmit the block and release memory
AudioStream_F32::transmit(out_block); // send the FIR output AudioStream_F32::transmit(out_block); // send the FIR output
@ -188,11 +59,11 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32
//here is the function that does all the work //here is the function that does all the work
void cha_agc_channel(float *input, float *output, int cs) { //void cha_agc_channel(float *input, float *output, int cs) {
//compress(input, output, cs, &prev_env, // //compress(input, output, cs, &prev_env,
// CHA_DVAR.alfa, CHA_DVAR.beta, CHA_DVAR.tkgain, CHA_DVAR.tk, CHA_DVAR.cr, CHA_DVAR.bolt, CHA_DVAR.maxdB); // // CHA_DVAR.alfa, CHA_DVAR.beta, CHA_DVAR.tkgain, CHA_DVAR.tk, CHA_DVAR.cr, CHA_DVAR.bolt, CHA_DVAR.maxdB);
compress(input, output, cs); // compress(input, output, cs);
} //}
//void compress(float *x, float *y, int n, float *prev_env, //void compress(float *x, float *y, int n, float *prev_env,
// float &alfa, float &beta, float &tkgn, float &tk, float &cr, float &bolt, float &mxdB) // float &alfa, float &beta, float &tkgn, float &tk, float &cr, float &bolt, float &mxdB)
@ -222,23 +93,23 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32
void setDefaultValues(void) { void setDefaultValues(void) {
//set default values...taken from CHAPRO, GHA_Demo.c from "amplify()"...ignores given sample rate //set default values...configure as limitter
//assumes that the sample rate has already been set!!!! BTNRH_WDRC::CHA_WDRC gha = {
CHA_WDRC gha = {1.0f, // attack time (ms) 5.0f, // attack time (ms)
50.0f, // release time (ms) 50.0f, // release time (ms)
24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED! 24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED!
119.0f, // maxdB, maximum signal (dB SPL) 115.0f, // maxdB, maximum signal (dB SPL)...assumed SPL for full-scale input signal
0.0f, // tkgain, compression-start gain 0.0f, // tkgain, compression-start gain (dB)
105.0f, // tk, compression-start kneepoint 55.0f, // tk, compression-start kneepoint (dB SPL)
10.0f, // cr, compression ratio 1.0f, // cr, compression ratio (set to 1.0 to defeat)
105.0f // bolt, broadband output limiting threshold 100.0f // bolt, broadband output limiting threshold (ie, the limiter. SPL. 10:1 comp ratio)
}; };
setParams_from_CHA_WDRC(&gha); setParams_from_CHA_WDRC(&gha);
} }
//set all of the parameters for the compressor using the CHA_WDRC structure //set all of the parameters for the compressor using the CHA_WDRC structure
//assumes that the sample rate has already been set!!! //assumes that the sample rate has already been set!!!
void setParams_from_CHA_WDRC(CHA_WDRC *gha) { void setParams_from_CHA_WDRC(BTNRH_WDRC::CHA_WDRC *gha) {
//configure the envelope calculator...assumes that the sample rate has already been set! //configure the envelope calculator...assumes that the sample rate has already been set!
calcEnvelope.setAttackRelease_msec(gha->attack,gha->release); //these are in milliseconds calcEnvelope.setAttackRelease_msec(gha->attack,gha->release); //these are in milliseconds
@ -263,8 +134,24 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32
calcEnvelope.setSampleRate_Hz(_fs_Hz); calcEnvelope.setSampleRate_Hz(_fs_Hz);
} }
void setAttackRelease_msec(const float atk_msec, const float rel_msec) {
calcEnvelope.setAttackRelease_msec(atk_msec, rel_msec);
}
void setKneeLimiter_dBSPL(float _bolt) { calcGain.setKneeLimiter_dBSPL(_bolt); }
void setKneeLimiter_dBFS(float _bolt_dBFS) { calcGain.setKneeLimiter_dBFS(_bolt_dBFS); }
void setGain_dB(float _gain_dB) { calcGain.setGain_dB(_gain_dB); } //gain at start of compression
void setKneeCompressor_dBSPL(float _tk) { calcGain.setKneeCompressor_dBSPL(_tk); }
void setKneeCompressor_dBFS(float _tk_dBFS) { calcGain.setKneeCompressor_dBFS(_tk_dBFS); }
void setCompRatio(float _cr) { calcGain.setCompRatio(_cr); }
void setMaxdB(float _maxdB) { calcGain.setMaxdB(_maxdB); };
float getCurrentLevel_dB(void) { return AudioCalcGainWDRC_F32::db2(calcEnvelope.getCurrentLevel()); } //this is 20*log10(abs(signal)) after the envelope smoothing float getCurrentLevel_dB(void) { return AudioCalcGainWDRC_F32::db2(calcEnvelope.getCurrentLevel()); } //this is 20*log10(abs(signal)) after the envelope smoothing
float getGain_dB(void) { return calcGain.getGain_dB(); } //returns the linear gain of the system
float getCurrentGain(void) { return calcGain.getCurrentGain(); }
float getCurrentGain_dB(void) { return calcGain.getCurrentGain_dB(); }
AudioCalcEnvelope_F32 calcEnvelope; AudioCalcEnvelope_F32 calcEnvelope;
AudioCalcGainWDRC_F32 calcGain; AudioCalcGainWDRC_F32 calcGain;

@ -22,13 +22,20 @@ class AudioEffectCompressor_F32 : public AudioStream_F32
public: public:
//constructor //constructor
AudioEffectCompressor_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { AudioEffectCompressor_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {
setDefaultValues(AUDIO_SAMPLE_RATE); resetStates();
};
AudioEffectCompressor_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) {
setDefaultValues(settings.sample_rate_Hz); resetStates();
};
void setDefaultValues(const float sample_rate_Hz) {
setThresh_dBFS(-20.0f); //set the default value for the threshold for compression setThresh_dBFS(-20.0f); //set the default value for the threshold for compression
setCompressionRatio(5.0f); //set the default copression ratio setCompressionRatio(5.0f); //set the default copression ratio
setAttack_sec(0.005f, AUDIO_SAMPLE_RATE); //default to this value setAttack_sec(0.005f, sample_rate_Hz); //default to this value
setRelease_sec(0.200f, AUDIO_SAMPLE_RATE); //default to this value setRelease_sec(0.200f, sample_rate_Hz); //default to this value
setHPFilterCoeff(); enableHPFilter(true); //enable the HP filter to remove any DC offset from the audio setHPFilterCoeff(); enableHPFilter(true); //enable the HP filter to remove any DC offset from the audio
resetStates(); }
};
//here's the method that does all the work //here's the method that does all the work
void update(void) { void update(void) {
@ -133,7 +140,7 @@ class AudioEffectCompressor_F32 : public AudioStream_F32
inst_targ_gain_dB_block->data, //this is the output inst_targ_gain_dB_block->data, //this is the output
above_thresh_dB_block->length); above_thresh_dB_block->length);
// compute the instantaneous gai...which is the difference between the target level and the original level // compute the instantaneous gain...which is the difference between the target level and the original level
arm_sub_f32(inst_targ_gain_dB_block->data, //CMSIS DSP for "subtract two vectors element-by-element" arm_sub_f32(inst_targ_gain_dB_block->data, //CMSIS DSP for "subtract two vectors element-by-element"
above_thresh_dB_block->data, //this is the vector to be subtracted above_thresh_dB_block->data, //this is the vector to be subtracted
inst_targ_gain_dB_block->data, //this is the output inst_targ_gain_dB_block->data, //this is the output

@ -23,6 +23,7 @@ class AudioEffectEmpty_F32 : public AudioStream_F32
public: public:
//constructor //constructor
AudioEffectEmpty_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {}; AudioEffectEmpty_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {};
AudioEffectEmpty_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) {};
//here's the method that does all the work //here's the method that does all the work
void update(void) { void update(void) {

@ -9,8 +9,8 @@
* MIT License. use at your own risk. * MIT License. use at your own risk.
*/ */
#ifndef _AudioEffectGain_h #ifndef _AudioEffectGain_F32_h
#define _AudioEffectGain_h #define _AudioEffectGain_F32_h
#include <arm_math.h> //ARM DSP extensions. for speed! #include <arm_math.h> //ARM DSP extensions. for speed!
#include <AudioStream_F32.h> #include <AudioStream_F32.h>
@ -21,6 +21,7 @@ class AudioEffectGain_F32 : public AudioStream_F32
public: public:
//constructor //constructor
AudioEffectGain_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {}; AudioEffectGain_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {};
AudioEffectGain_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) {};
//here's the method that does all the work //here's the method that does all the work
void update(void) { void update(void) {

@ -0,0 +1,41 @@
/*
* AudioFilterBiquad_F32.cpp
*
* Chip Audette, OpenAudio, Apr 2017
*
* MIT License, Use at your own risk.
*
*/
#include "AudioFilterBiquad_F32.h"
void AudioFilterBiquad_F32::update(void)
{
audio_block_f32_t *block;
block = AudioStream_F32::receiveWritable_f32();
if (!block) return;
// If there's no coefficient table, give up.
if (coeff_p == NULL) {
AudioStream_F32::release(block);
return;
}
// do passthru
if (coeff_p == IIR_F32_PASSTHRU) {
// Just passthrough
AudioStream_F32::transmit(block);
AudioStream_F32::release(block);
return;
}
// do IIR
arm_biquad_cascade_df1_f32(&iir_inst, block->data, block->data, block->length);
//transmit the data
AudioStream_F32::transmit(block); // send the IIR output
AudioStream_F32::release(block);
}

@ -0,0 +1,204 @@
/*
* AudioFilterBiquad_F32
*
* Created: Chip Audette (OpenAudio) Feb 2017
*
* License: MIT License. Use at your own risk.
*
*/
#ifndef _filter_iir_f32
#define _filter_iir_f32
#include "Arduino.h"
#include "AudioStream_F32.h"
#include "arm_math.h"
// Indicates that the code should just pass through the audio
// without any filtering (as opposed to doing nothing at all)
#define IIR_F32_PASSTHRU ((const float32_t *) 1)
#define IIR_MAX_STAGES 1 //meaningless right now
class AudioFilterBiquad_F32 : public AudioStream_F32
{
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:IIR
public:
AudioFilterBiquad_F32(void): AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) {
setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT);
}
AudioFilterBiquad_F32(const AudioSettings_F32 &settings):
AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) {
setSampleRate_Hz(settings.sample_rate_Hz);
}
void begin(const float32_t *cp, int n_stages = 1) {
coeff_p = cp;
// Initialize FIR instance (ARM DSP Math Library)
if (coeff_p && (coeff_p != IIR_F32_PASSTHRU) && n_stages <= IIR_MAX_STAGES) {
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html
arm_biquad_cascade_df1_init_f32(&iir_inst, n_stages, (float32_t *)coeff_p, &StateF32[0]);
}
}
void end(void) {
coeff_p = NULL;
}
void setSampleRate_Hz(float _fs_Hz) { sampleRate_Hz = _fs_Hz; }
void setBlockDC(void) {
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
//Use matlab to compute the coeff for HP at 40Hz: [b,a]=butter(2,40/(44100/2),'high'); %assumes fs_Hz = 44100
float32_t b[] = {8.173653471988667e-01, -1.634730694397733e+00, 8.173653471988667e-01}; //from Matlab
float32_t a[] = { 1.000000000000000e+00, -1.601092394183619e+00, 6.683689946118476e-01}; //from Matlab
setFilterCoeff_Matlab(b, a);
}
void setFilterCoeff_Matlab(float32_t b[], float32_t a[]) { //one stage of N=2 IIR
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
//Use matlab to compute the coeff, such as: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100
coeff[0] = b[0]; coeff[1] = b[1]; coeff[2] = b[2]; //here are the matlab "b" coefficients
coeff[3] = -a[1]; coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab ;
begin(coeff);
}
//note: stage is currently ignored
void setCoefficients(int stage, float c[]) {
if (stage > 0) {
if (Serial) {
Serial.println(F("AudioFilterBiquad_F32: setCoefficients: *** ERROR ***"));
Serial.print(F(" : This module only accepts one stage."));
Serial.print(F(" : You are attempting to set stage "));Serial.print(stage);
Serial.print(F(" : Ignoring this filter."));
}
return;
}
coeff[0] = c[0];
coeff[1] = c[1];
coeff[2] = c[2];
coeff[3] = -c[3];
coeff[4] = -c[4];
begin(coeff);
}
// Compute common filter functions
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
//void setLowpass(uint32_t stage, float frequency, float q = 0.7071) {
void setLowpass(uint32_t stage, float frequency, float q = 0.7071) {
//int coeff[5];
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
//double scale = 1073741824.0 / (1.0 + alpha);
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = ((1.0 - cosW0) / 2.0) * scale;
/* b1 */ coeff[1] = (1.0 - cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = (1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setHighpass(uint32_t stage, float frequency, float q = 0.7071) {
//int coeff[5];
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = ((1.0 + cosW0) / 2.0) * scale;
/* b1 */ coeff[1] = -(1.0 + cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = (1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setBandpass(uint32_t stage, float frequency, float q = 1.0) {
//int coeff[5];
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = alpha * scale;
/* b1 */ coeff[1] = 0;
/* b2 */ coeff[2] = (-alpha) * scale;
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = (1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setNotch(uint32_t stage, float frequency, float q = 1.0) {
//int coeff[5];
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = scale;
/* b1 */ coeff[1] = (-2.0 * cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = (1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setLowShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) {
//int coeff[5];
double a = pow(10.0, gain/40.0);
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
//double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
double cosW0 = cos(w0);
//generate three helper-values (intermediate results):
double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a );
double aMinus = (a-1.0)*cosW0;
double aPlus = (a+1.0)*cosW0;
double scale = 1.0 / ( (a+1.0) + aMinus + sinsq);
/* b0 */ coeff[0] = a * ( (a+1.0) - aMinus + sinsq ) * scale;
/* b1 */ coeff[1] = 2.0*a * ( (a-1.0) - aPlus ) * scale;
/* b2 */ coeff[2] = a * ( (a+1.0) - aMinus - sinsq ) * scale;
/* a1 */ coeff[3] = -2.0* ( (a-1.0) + aPlus ) * scale;
/* a2 */ coeff[4] = ( (a+1.0) + aMinus - sinsq ) * scale;
setCoefficients(stage, coeff);
}
void setHighShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) {
//int coeff[5];
double a = pow(10.0, gain/40.0);
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT);
double sinW0 = sin(w0);
//double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
double cosW0 = cos(w0);
//generate three helper-values (intermediate results):
double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a );
double aMinus = (a-1.0)*cosW0;
double aPlus = (a+1.0)*cosW0;
double scale = 1.0 / ( (a+1.0) - aMinus + sinsq);
/* b0 */ coeff[0] = a * ( (a+1.0) + aMinus + sinsq ) * scale;
/* b1 */ coeff[1] = -2.0*a * ( (a-1.0) + aPlus ) * scale;
/* b2 */ coeff[2] = a * ( (a+1.0) + aMinus - sinsq ) * scale;
/* a1 */ coeff[3] = 2.0* ( (a-1.0) - aPlus ) * scale;
/* a2 */ coeff[4] = ( (a+1.0) - aMinus - sinsq ) * scale;
setCoefficients(stage, coeff);
}
void update(void);
private:
audio_block_f32_t *inputQueueArray[1];
float32_t coeff[5 * 1] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later
float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h??
// pointer to current coefficients or NULL or FIR_PASSTHRU
const float32_t *coeff_p;
// ARM DSP Math library filter instance
arm_biquad_casd_df1_inst_f32 iir_inst;
float32_t StateF32[4*IIR_MAX_STAGES];
};
#endif

@ -0,0 +1,55 @@
/*
* AudioFilterFIR_F32.cpp
*
* Chip Audette, OpenAudio, Apr 2017
*
* MIT License, Use at your own risk.
*
*/
#include "AudioFilterFIR_F32.h"
void AudioFilterFIR_F32::update(void)
{
audio_block_f32_t *block, *block_new;
block = AudioStream_F32::receiveReadOnly_f32();
if (!block) return;
// If there's no coefficient table, give up.
if (coeff_p == NULL) {
AudioStream_F32::release(block);
return;
}
// do passthru
if (coeff_p == FIR_F32_PASSTHRU) {
// Just passthrough
AudioStream_F32::transmit(block);
AudioStream_F32::release(block);
//Serial.println("AudioFilterFIR_F32: update(): PASSTHRU.");
return;
}
// get a block for the FIR output
block_new = AudioStream_F32::allocate_f32();
if (block_new) {
//check to make sure our FIR instance has the right size
if (block->length != configured_block_size) {
//doesn't match. re-initialize
Serial.println("AudioFilterFIR_F32: block size doesn't match. Re-initializing FIR.");
begin(coeff_p, n_coeffs, block->length); //initialize with same coefficients, just a new block length
}
//apply the FIR
arm_fir_f32(&fir_inst, block->data, block_new->data, block->length);
block_new->length = block->length;
//transmit the data
AudioStream_F32::transmit(block_new); // send the FIR output
AudioStream_F32::release(block_new);
}
AudioStream_F32::release(block);
}

@ -16,7 +16,6 @@
// Indicates that the code should just pass through the audio // Indicates that the code should just pass through the audio
// without any filtering (as opposed to doing nothing at all) // without any filtering (as opposed to doing nothing at all)
#define FIR_F32_PASSTHRU ((const float32_t *) 1) #define FIR_F32_PASSTHRU ((const float32_t *) 1)
#define FIR_MAX_COEFFS 200 #define FIR_MAX_COEFFS 200
class AudioFilterFIR_F32 : public AudioStream_F32 class AudioFilterFIR_F32 : public AudioStream_F32
@ -25,6 +24,8 @@ class AudioFilterFIR_F32 : public AudioStream_F32
public: public:
AudioFilterFIR_F32(void): AudioStream_F32(1,inputQueueArray), AudioFilterFIR_F32(void): AudioStream_F32(1,inputQueueArray),
coeff_p(FIR_F32_PASSTHRU), n_coeffs(1), configured_block_size(0) { } coeff_p(FIR_F32_PASSTHRU), n_coeffs(1), configured_block_size(0) { }
AudioFilterFIR_F32(const AudioSettings_F32 &settings): AudioStream_F32(1,inputQueueArray),
coeff_p(FIR_F32_PASSTHRU), n_coeffs(1), configured_block_size(0) { }
//initialize the FIR filter by giving it the filter coefficients //initialize the FIR filter by giving it the filter coefficients
void begin(const float32_t *cp, const int _n_coeffs) { begin(cp, _n_coeffs, AUDIO_BLOCK_SAMPLES); } //assume that the block size is the maximum void begin(const float32_t *cp, const int _n_coeffs) { begin(cp, _n_coeffs, AUDIO_BLOCK_SAMPLES); } //assume that the block size is the maximum
@ -36,8 +37,8 @@ class AudioFilterFIR_F32 : public AudioStream_F32
if (coeff_p && (coeff_p != FIR_F32_PASSTHRU) && n_coeffs <= FIR_MAX_COEFFS) { if (coeff_p && (coeff_p != FIR_F32_PASSTHRU) && n_coeffs <= FIR_MAX_COEFFS) {
arm_fir_init_f32(&fir_inst, n_coeffs, (float32_t *)coeff_p, &StateF32[0], block_size); arm_fir_init_f32(&fir_inst, n_coeffs, (float32_t *)coeff_p, &StateF32[0], block_size);
configured_block_size = block_size; configured_block_size = block_size;
Serial.print("AudioFilterFIR_F32: FIR is initialized. N_FIR = "); Serial.print(n_coeffs); //Serial.print("AudioFilterFIR_F32: FIR is initialized. N_FIR = "); Serial.print(n_coeffs);
Serial.print(", Block Size = "); Serial.println(block_size); //Serial.print(", Block Size = "); Serial.println(block_size);
//} else { //} else {
// Serial.print("AudioFilterFIR_F32: *** ERROR ***: Cound not initialize. N_FIR = "); Serial.print(n_coeffs); // Serial.print("AudioFilterFIR_F32: *** ERROR ***: Cound not initialize. N_FIR = "); Serial.print(n_coeffs);
// Serial.print(", Block Size = "); Serial.println(block_size); // Serial.print(", Block Size = "); Serial.println(block_size);
@ -45,7 +46,7 @@ class AudioFilterFIR_F32 : public AudioStream_F32
} }
} }
void end(void) { coeff_p = NULL; } void end(void) { coeff_p = NULL; }
virtual void update(void); void update(void);
//void setBlockDC(void) {} //helper function that sets this up for a first-order HP filter at 20Hz //void setBlockDC(void) {} //helper function that sets this up for a first-order HP filter at 20Hz
@ -63,50 +64,6 @@ class AudioFilterFIR_F32 : public AudioStream_F32
}; };
void AudioFilterFIR_F32::update(void)
{
audio_block_f32_t *block, *block_new;
block = AudioStream_F32::receiveReadOnly_f32();
if (!block) return;
// If there's no coefficient table, give up.
if (coeff_p == NULL) {
AudioStream_F32::release(block);
return;
}
// do passthru
if (coeff_p == FIR_F32_PASSTHRU) {
// Just passthrough
AudioStream_F32::transmit(block);
AudioStream_F32::release(block);
//Serial.println("AudioFilterFIR_F32: update(): PASSTHRU.");
return;
}
// get a block for the FIR output
block_new = AudioStream_F32::allocate_f32();
if (block_new) {
//check to make sure our FIR instance has the right size
if (block->length != configured_block_size) {
//doesn't match. re-initialize
Serial.println("AudioFilterFIR_F32: block size doesn't match. Re-initializing FIR.");
begin(coeff_p, n_coeffs, block->length); //initialize with same coefficients, just a new block length
}
//apply the FIR
arm_fir_f32(&fir_inst, block->data, block_new->data, block->length);
block_new->length = block->length;
//transmit the data
AudioStream_F32::transmit(block_new); // send the FIR output
AudioStream_F32::release(block_new);
}
AudioStream_F32::release(block);
}
#endif #endif

@ -1,30 +0,0 @@
#include "AudioMixer4_F32.h"
void AudioMixer4_F32::update(void) {
audio_block_f32_t *in, *out=NULL;
out = receiveWritable_f32(0);
if (!out) return;
arm_scale_f32(out->data, multiplier[0], out->data, AUDIO_BLOCK_SAMPLES);
for (int channel=1; channel < 4; channel++) {
in = receiveReadOnly_f32(channel);
if (!in) {
continue;
}
audio_block_f32_t *tmp = allocate_f32();
arm_scale_f32(in->data, multiplier[channel], tmp->data, AUDIO_BLOCK_SAMPLES);
arm_add_f32(out->data, tmp->data, out->data, AUDIO_BLOCK_SAMPLES);
release(tmp);
release(in);
}
if (out) {
transmit(out);
release(out);
}
}

@ -1,38 +0,0 @@
/*
* AudioMixer4
*
* Created: Patrick Radius, December 2016
* Purpose: Mix up to 4 audio channels with individual gain controls.
* Assumes floating-point data.
*
* This processes a single stream fo audio data (ie, it is mono)
*
* MIT License. use at your own risk.
*/
#ifndef AUDIOMIXER4F32_H
#define AUDIOMIXER4F32_H
#include <arm_math.h>
#include <AudioStream_F32.h>
class AudioMixer4_F32 : public AudioStream_F32 {
//GUI: inputs:4, outputs:1 //this line used for automatic generation of GUI node
public:
AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) {
for (int i=0; i<4; i++) multiplier[i] = 1.0;
}
virtual void update(void);
void gain(unsigned int channel, float gain) {
if (channel >= 4 || channel < 0) return;
multiplier[channel] = gain;
}
private:
audio_block_f32_t *inputQueueArray[4];
float multiplier[4];
};
#endif

@ -24,9 +24,12 @@ class AudioMixer4_F32 : public AudioStream_F32 {
//GUI: inputs:4, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:4, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:Mixer4 //GUI: shortName:Mixer4
public: public:
AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) { AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) { setDefaultValues(); }
for (int i=0; i<4; i++) multiplier[i] = 1.0; AudioMixer4_F32(const AudioSettings_F32 &settings) : AudioStream_F32(4, inputQueueArray) { setDefaultValues(); }
}
void setDefaultValues(void) {
for (int i=0; i<4; i++) multiplier[i] = 1.0;
}
virtual void update(void); virtual void update(void);
@ -44,7 +47,10 @@ class AudioMixer8_F32 : public AudioStream_F32 {
//GUI: inputs:8, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:8, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:Mixer8 //GUI: shortName:Mixer8
public: public:
AudioMixer8_F32() : AudioStream_F32(8, inputQueueArray) { AudioMixer8_F32() : AudioStream_F32(8, inputQueueArray) { setDefaultValues();}
AudioMixer8_F32(const AudioSettings_F32 &settings) : AudioStream_F32(8, inputQueueArray) { setDefaultValues();}
void setDefaultValues(void) {
for (int i=0; i<8; i++) multiplier[i] = 1.0; for (int i=0; i<8; i++) multiplier[i] = 1.0;
} }

@ -20,6 +20,8 @@ class AudioMultiply_F32 : public AudioStream_F32
//GUI: inputs:2, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:2, outputs:1 //this line used for automatic generation of GUI node
public: public:
AudioMultiply_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {}; AudioMultiply_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {};
AudioMultiply_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {};
void update(void); void update(void);
private: private:

@ -0,0 +1,29 @@
#include "AudioSettings_F32.h"
#include "AudioStream_F32.h"
float AudioSettings_F32::cpu_load_percent(const int n) {
//n is the number of cycles
#define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100))
float foo1 = ((float)(F_CPU / 32))/sample_rate_Hz;
foo1 *= ((float)audio_block_samples);
foo1 /= 100.f;
foo1 += (float)n;
float foo2 = (float)(F_CPU / 16)/sample_rate_Hz;
foo2 *= ((float)audio_block_samples);
foo2 /= 100.f;
return foo1 / foo2;
//return (((n) + (F_CPU / 32 / sample_rate_Hz * audio_block_samples / 100)) / (F_CPU / 16 / sample_rate_Hz * audio_block_samples / 100));
}
float AudioSettings_F32::processorUsage(void) {
return cpu_load_percent(AudioStream::cpu_cycles_total);
};
float AudioSettings_F32::processorUsageMax(void) {
return cpu_load_percent(AudioStream::cpu_cycles_total_max);
}
void AudioSettings_F32::processorUsageMaxReset(void) {
AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total;
}

@ -0,0 +1,18 @@
#ifndef _AudioSettings_F32_
#define _AudioSettings_F32_
class AudioSettings_F32 {
public:
AudioSettings_F32(float fs_Hz, int block_size) :
sample_rate_Hz(fs_Hz), audio_block_samples(block_size) {}
const float sample_rate_Hz;
const int audio_block_samples;
float cpu_load_percent(const int n);
float processorUsage(void);
float processorUsageMax(void);
void processorUsageMaxReset(void);
};
#endif

@ -7,6 +7,24 @@ uint32_t AudioStream_F32::f32_memory_pool_available_mask[6];
uint8_t AudioStream_F32::f32_memory_used = 0; uint8_t AudioStream_F32::f32_memory_used = 0;
uint8_t AudioStream_F32::f32_memory_used_max = 0; uint8_t AudioStream_F32::f32_memory_used_max = 0;
audio_block_f32_t* allocate_f32_memory(const int num) {
static bool firstTime=true;
static audio_block_f32_t *data_f32;
if (firstTime == true) {
firstTime = false;
data_f32 = new audio_block_f32_t[num];
}
return data_f32;
}
void AudioMemory_F32(const int num) {
audio_block_f32_t *data_f32 = allocate_f32_memory(num);
if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num);
}
void AudioMemory_F32(const int num, const AudioSettings_F32 &settings) {
audio_block_f32_t *data_f32 = allocate_f32_memory(num);
if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num, settings);
}
// Set up the pool of audio data blocks // Set up the pool of audio data blocks
// placing them all onto the free list // placing them all onto the free list
void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned int num) void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned int num)

@ -15,37 +15,15 @@
#include <arm_math.h> //ARM DSP extensions. for speed! #include <arm_math.h> //ARM DSP extensions. for speed!
#include <Audio.h> //Teensy Audio Library #include <Audio.h> //Teensy Audio Library
#include "AudioSettings_F32.h"
// /////////////// class prototypes
class AudioStream_F32; class AudioStream_F32;
class AudioConnection_F32; class AudioConnection_F32;
class AudioSettings_F32;
class AudioSettings_F32 { // ///////////// class definitions
public:
AudioSettings_F32(float fs_Hz, int block_size) :
sample_rate_Hz(fs_Hz), audio_block_samples(block_size) {}
const float sample_rate_Hz;
const int audio_block_samples;
float cpu_load_percent(const int n) { //n is the number of cycles
#define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100))
float foo1 = ((float)(F_CPU / 32))/sample_rate_Hz;
foo1 *= ((float)audio_block_samples);
foo1 /= 100.f;
foo1 += (float)n;
float foo2 = (float)(F_CPU / 16)/sample_rate_Hz;
foo2 *= ((float)audio_block_samples);
foo2 /= 100.f;
return foo1 / foo2;
//return (((n) + (F_CPU / 32 / sample_rate_Hz * audio_block_samples / 100)) / (F_CPU / 16 / sample_rate_Hz * audio_block_samples / 100));
}
float processorUsage(void) { return cpu_load_percent(AudioStream::cpu_cycles_total); };
float processorUsageMax(void) { return cpu_load_percent(AudioStream::cpu_cycles_total_max); }
void processorUsageMaxReset(void) { AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total; }
};
//create a new structure to hold audio as floating point values. //create a new structure to hold audio as floating point values.
//modeled on the existing teensy audio block struct, which uses Int16 //modeled on the existing teensy audio block struct, which uses Int16
@ -91,20 +69,6 @@ class AudioConnection_F32
AudioConnection_F32 *next_dest; AudioConnection_F32 *next_dest;
}; };
#define AudioMemory_F32(num) ({ \
static audio_block_f32_t data_f32[num]; \
AudioStream_F32::initialize_f32_memory(data_f32, num); \
})
#define AudioMemory_F32_wSettings(num,settings) ({ \
static audio_block_f32_t data_f32[num]; \
AudioStream_F32::initialize_f32_memory(data_f32, num, settings); \
})
#define AudioMemoryUsage_F32() (AudioStream_F32::f32_memory_used)
#define AudioMemoryUsageMax_F32() (AudioStream_F32::f32_memory_used_max)
#define AudioMemoryUsageMaxReset_F32() (AudioStream_F32::f32_memory_used_max = AudioStream_F32::f32_memory_used)
class AudioStream_F32 : public AudioStream { class AudioStream_F32 : public AudioStream {
public: public:
@ -121,12 +85,12 @@ class AudioStream_F32 : public AudioStream {
//virtual void update(audio_block_f32_t *) = 0; //virtual void update(audio_block_f32_t *) = 0;
static uint8_t f32_memory_used; static uint8_t f32_memory_used;
static uint8_t f32_memory_used_max; static uint8_t f32_memory_used_max;
static audio_block_f32_t * allocate_f32(void);
static void release(audio_block_f32_t * block);
protected: protected:
//bool active_f32; //bool active_f32;
unsigned char num_inputs_f32; unsigned char num_inputs_f32;
static audio_block_f32_t * allocate_f32(void);
static void release(audio_block_f32_t * block);
void transmit(audio_block_f32_t *block, unsigned char index = 0); void transmit(audio_block_f32_t *block, unsigned char index = 0);
audio_block_f32_t * receiveReadOnly_f32(unsigned int index = 0); audio_block_f32_t * receiveReadOnly_f32(unsigned int index = 0);
audio_block_f32_t * receiveWritable_f32(unsigned int index = 0); audio_block_f32_t * receiveWritable_f32(unsigned int index = 0);
@ -141,6 +105,21 @@ class AudioStream_F32 : public AudioStream {
static uint32_t f32_memory_pool_available_mask[6]; static uint32_t f32_memory_pool_available_mask[6];
}; };
/*
#define AudioMemory_F32(num) ({ \
static audio_block_f32_t data_f32[num]; \
AudioStream_F32::initialize_f32_memory(data_f32, num); \
})
*/
void AudioMemory_F32(const int num);
void AudioMemory_F32(const int num, const AudioSettings_F32 &settings);
#define AudioMemory_F32_wSettings(num,settings) (AudioMemory_F32(num,settings)) //for historical compatibility
#define AudioMemoryUsage_F32() (AudioStream_F32::f32_memory_used)
#define AudioMemoryUsageMax_F32() (AudioStream_F32::f32_memory_used_max)
#define AudioMemoryUsageMaxReset_F32() (AudioStream_F32::f32_memory_used_max = AudioStream_F32::f32_memory_used)
#endif #endif

@ -0,0 +1,181 @@
#ifndef _BTNRH_WDRC_TYPES_H
#define _BTNRH_WDRC_TYPES_H
namespace BTNRH_WDRC {
// from CHAPRO cha_ff.h
#define DSL_MXCH 32
//class CHA_DSL {
typedef struct {
//public:
//CHA_DSL(void) {};
//static const int DSL_MXCH = 32; // maximum number of channels
float attack; // attack time (ms)
float release; // release time (ms)
float maxdB; // maximum signal (dB SPL)
int ear; // 0=left, 1=right
int nchannel; // number of channels
float cross_freq[DSL_MXCH]; // cross frequencies (Hz)
float tkgain[DSL_MXCH]; // compression-start gain
float cr[DSL_MXCH]; // compression ratio
float tk[DSL_MXCH]; // compression-start kneepoint
float bolt[DSL_MXCH]; // broadband output limiting threshold
} CHA_DSL;
typedef struct {
//public:
//CHA_DSL(void) {};
//static const int DSL_MXCH = 32; // maximum number of channels
float attack; // attack time (ms)
float release; // release time (ms)
float maxdB; // maximum signal (dB SPL)
int ear; // 0=left, 1=right
int nchannel; // number of channels
float cross_freq[DSL_MXCH]; // cross frequencies (Hz)
float exp_cr[DSL_MXCH]; // compression ratio for low-SPL region (ie, the expander)
float exp_end_knee[DSL_MXCH]; // expansion-end kneepoint
float tkgain[DSL_MXCH]; // compression-start gain
float cr[DSL_MXCH]; // compression ratio
float tk[DSL_MXCH]; // compression-start kneepoint
float bolt[DSL_MXCH]; // broadband output limiting threshold
} CHA_DSL2;
/* int parseStringIntoDSL(String &text_buffer) {
int position = 0;
float foo_val;
const bool print_debug = false;
if (print_debug) Serial.println("parseTextAsDSL: values from file:");
position = parseNextNumberFromString(text_buffer, position, foo_val);
attack = foo_val;
if (print_debug) { Serial.print(" attack: "); Serial.println(attack); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
release = foo_val;
if (print_debug) { Serial.print(" release: "); Serial.println(release); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
maxdB = foo_val;
if (print_debug) { Serial.print(" maxdB: "); Serial.println(maxdB); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
ear = int(foo_val + 0.5); //round
if (print_debug) { Serial.print(" ear: "); Serial.println(ear); }
position = parseNextNumberFromString(text_buffer, position, foo_val);
nchannel = int(foo_val + 0.5); //round
if (print_debug) { Serial.print(" nchannel: "); Serial.println(nchannel); }
//check to see if the number of channels is acceptable.
if ((nchannel < 0) || (nchannel > DSL_MXCH)) {
if (print_debug) Serial.print(" : channel number is too big (or negative). stopping.");
return -1;
}
//read the cross-over frequencies. There should be nchan-1 of them (0 and Nyquist are assumed)
if (print_debug) Serial.print(" cross_freq: ");
for (int i=0; i < (nchannel-1); i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
cross_freq[i] = foo_val;
if (print_debug) { Serial.print(cross_freq[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the tkgain values. There should be nchan of them
if (print_debug) Serial.print(" tkgain: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
tkgain[i] = foo_val;
if (print_debug) { Serial.print(tkgain[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the cr values. There should be nchan of them
if (print_debug) Serial.print(" cr: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
cr[i] = foo_val;
if (print_debug) { Serial.print(cr[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the tk values. There should be nchan of them
if (print_debug) Serial.print(" tk: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
tk[i] = foo_val;
if (print_debug) { Serial.print(tk[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
//read the bolt values. There should be nchan of them
if (print_debug) Serial.print(" bolt: ");
for (int i=0; i < nchannel; i++) {
position = parseNextNumberFromString(text_buffer, position, foo_val);
bolt[i] = foo_val;
if (print_debug) { Serial.print(bolt[i]); Serial.print(", ");}
}
if (print_debug) Serial.println();
return 0;
}
void printToStream(Stream *s) {
s->print("CHA_DSL: attack (ms) = "); s->println(attack);
s->print(" : release (ms) = "); s->println(release);
s->print(" : maxdB (dB SPL) = "); s->println(maxdB);
s->print(" : ear (0 = left, 1 = right) "); s->println(ear);
s->print(" : nchannel = "); s->println(nchannel);
s->print(" : cross_freq (Hz) = ");
for (int i=0; i<nchannel-1;i++) { s->print(cross_freq[i]); s->print(", ");}; s->println();
s->print(" : tkgain = ");
for (int i=0; i<nchannel;i++) { s->print(tkgain[i]); s->print(", ");}; s->println();
s->print(" : cr = ");
for (int i=0; i<nchannel;i++) { s->print(cr[i]); s->print(", ");}; s->println();
s->print(" : tk = ");
for (int i=0; i<nchannel;i++) { s->print(tk[i]); s->print(", ");}; s->println();
s->print(" : bolt = ");
for (int i=0; i<nchannel;i++) { s->print(bolt[i]); s->print(", ");}; s->println();
}
} ; */
typedef struct {
float alfa; // attack constant (not time)
float beta; // release constant (not time
float fs; // sampling rate (Hz)
float maxdB; // maximum signal (dB SPL)
float tkgain; // compression-start gain
float tk; // compression-start kneepoint
float cr; // compression ratio
float bolt; // broadband output limiting threshold
} CHA_DVAR_t;
typedef struct {
float attack; // attack time (ms), unused in this class
float release; // release time (ms), unused in this class
float fs; // sampling rate (Hz), set through other means in this class
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
float tkgain; // compression-start gain
float tk; // compression-start kneepoint
float cr; // compression ratio
float bolt; // broadband output limiting threshold
} CHA_WDRC;
typedef struct {
float attack; // attack time (ms), unused in this class
float release; // release time (ms), unused in this class
float fs; // sampling rate (Hz), set through other means in this class
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
float exp_cr; // compression ratio for low-SPL region (ie, the expander)
float exp_end_knee; // expansion-end kneepoint
float tkgain; // compression-start gain
float tk; // compression-start kneepoint
float cr; // compression ratio
float bolt; // broadband output limiting threshold
} CHA_WDRC2;
};
#endif

@ -5,20 +5,23 @@
#include "AudioCalcEnvelope_F32.h" #include "AudioCalcEnvelope_F32.h"
#include "AudioCalcGainWDRC_F32.h" #include "AudioCalcGainWDRC_F32.h"
#include "AudioConfigFIRFilterBank_F32.h" #include "AudioConfigFIRFilterBank_F32.h"
#include <AudioConvert_F32.h> #include "AudioControlTester.h"
#include "AudioConvert_F32.h"
#include "AudioEffectCompressor_F32.h"
#include "AudioEffectCompWDRC_F32.h" #include "AudioEffectCompWDRC_F32.h"
#include "AudioEffectEmpty_F32.h" #include "AudioEffectEmpty_F32.h"
#include <AudioEffectGain_F32.h> #include "AudioEffectGain_F32.h"
#include <AudioEffectCompressor_F32.h> #include "AudioFilterBiquad_F32.h"
#include <AudioFilterFIR_F32.h> #include <AudioFilterFIR_F32.h>
#include <AudioFilterIIR_F32.h> #include <AudioFilterIIR_F32.h>
#include <AudioMixer_F32.h> #include "AudioMixer_F32.h"
#include <AudioMultiply_F32.h> #include "AudioMultiply_F32.h"
#include "AudioSettings_F32.h"
#include "input_i2s_f32.h" #include "input_i2s_f32.h"
#include "output_i2s_f32.h"
#include "play_queue_f32.h" #include "play_queue_f32.h"
#include "record_queue_f32.h" #include "record_queue_f32.h"
#include "synth_pinknoise_f32.h" #include "synth_pinknoise_f32.h"
#include <synth_waveform_F32.h>
#include "synth_whitenoise_f32.h"
#include "synth_sine_f32.h" #include "synth_sine_f32.h"
#include "output_i2s_f32.h" #include "synth_waveform_F32.h"
#include "synth_whitenoise_f32.h"

@ -0,0 +1,146 @@
/*
* USB_Audio_F32
*
* Created: Chip Audette (OpenAudio), Mar 2017
* Float32 wrapper for the Audio USB classes from the Teensy Audio Library
*
* License: MIT License. Use at your own risk.
*/
#ifndef usb_audio_f32_h_
#define usb_audio_f32_h_
//#include "Arduino.h"
#include <AudioStream_F32.h>
#include <Audio.h>
class AudioInputUSB_F32 : public AudioStream_F32
{
//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI node
//GUI: shortName:usbAudioIn //this line used for automatic generation of GUI node
public:
AudioInputUSB_F32() : AudioStream_F32(0, NULL) {
//i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h
//output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h
makeConnections();
}
AudioInputUSB_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) {
//i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h
//output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h
makeConnections();
}
void makeConnections(void) {
//make the audio connections
patchCord100_L = new AudioConnection(usb_in, 0, i16_to_f32_L, 0); //usb_in is an Int16 audio object. So, convert it!
patchCord100_R = new AudioConnection(usb_in, 1, i16_to_f32_R, 0); //usb_in is an Int16 audio object. So, convert it!
patchCord101_L = new AudioConnection_F32(i16_to_f32_L, 0, output_queue_L, 0);
patchCord101_R = new AudioConnection_F32(i16_to_f32_R, 0, output_queue_R, 0);
}
//define audio processing blocks.
AudioInputUSB usb_in; //from the original Teensy Audio Library, expects Int16 audio data
AudioConvert_I16toF32 i16_to_f32_L, i16_to_f32_R;
AudioRecordQueue_F32 output_queue_L,output_queue_R;
//define the audio connections
AudioConnection *patchCord100_L, *patchCord100_R;
AudioConnection_F32 *patchCord101_L, *patchCord101_R;
void update(void) {
//Serial.println("AudioSynthNoiseWhite_F32: update().");
output_queue_L.begin();
output_queue_R.begin();
//manually update audio blocks in the desired order
usb_in.update(); //the output should be routed directly via the AudioConnection
i16_to_f32_L.update(); // output is routed via the AudioConnection
i16_to_f32_R.update(); // output is routed via the AudioConnection
output_queue_L.update();
output_queue_R.update();
//handle the output for the left channel
audio_block_f32_t *block;
block = output_queue_L.getAudioBlock();
if (block == NULL) return;
AudioStream_F32::transmit(block,0);
output_queue_L.freeAudioBlock();
output_queue_L.end();
//handle the output for the left channel
block = output_queue_R.getAudioBlock();
if (block == NULL) return;
AudioStream_F32::transmit(block,1);
output_queue_R.freeAudioBlock();
output_queue_R.end();
}
private:
};
class AudioOutputUSB_F32 : public AudioStream_F32
{
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName:usbAudioOut //this line used for automatic generation of GUI node
public:
AudioOutputUSB_F32() : AudioStream_F32(2, inputQueueArray_f32) {
makeConnections();
}
AudioOutputUSB_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {
makeConnections();
}
void makeConnections(void) {
//make the audio connections
patchCord100_L = new AudioConnection_F32(queue_L, 0, f32_to_i16_L, 0); //noise is an Int16 audio object. So, convert it!
patchCord100_R = new AudioConnection_F32(queue_R, 0, f32_to_i16_R, 0); //noise is an Int16 audio object. So, convert it!
patchCord101_L = new AudioConnection(f32_to_i16_L, 0, usb_out, 0); //Int16 audio connection
patchCord101_R = new AudioConnection(f32_to_i16_R, 0, usb_out, 1); //Int16 audio connection
}
//define audio processing blocks.
AudioPlayQueue_F32 queue_L,queue_R;
AudioConvert_F32toI16 f32_to_i16_L, f32_to_i16_R;
AudioOutputUSB usb_out; //from the original Teensy Audio Library, expects Int16 audio data
//define the audio connections
AudioConnection_F32 *patchCord100_L, *patchCord100_R;
AudioConnection *patchCord101_L, *patchCord101_R;
void update(void) {
//Serial.println("AudioSynthNoiseWhite_F32: update().");
//queue_L.begin();
//queue_R.begin();
//is there audio waiting for us for the left channel?
audio_block_f32_t *block;
block = receiveReadOnly_f32(0);
if (!block) return; //if no audio, return now.
//there is some audio, so execute the processing chain for the left channel
queue_L.playAudioBlock(block);
AudioStream_F32::release(block);
queue_L.update();
f32_to_i16_L.update();
//see if there is a right channel
block = receiveReadOnly_f32(1);
if (block) {
//there is a right channel. process it now
queue_R.playAudioBlock(block);
AudioStream_F32::release(block);
queue_R.update();
f32_to_i16_R.update();
}
//whether or not there was right-channel audio, update the usb_out
usb_out.update();
return;
}
private:
audio_block_f32_t *inputQueueArray_f32[2];
};
#endif

@ -142,14 +142,22 @@ bool AudioControlTLV320AIC3206::enable(void)
Wire.begin(); Wire.begin();
delay(5); delay(5);
aic_reset(); delay(100); //hard reset the AIC
//Serial.println("Hardware reset of AIC...");
#define RESET_PIN 21
pinMode(RESET_PIN,OUTPUT);
digitalWrite(RESET_PIN,HIGH);delay(50); //not reset
digitalWrite(RESET_PIN,LOW);delay(50); //reset
digitalWrite(RESET_PIN,HIGH);delay(50);//not reset
aic_reset(); delay(100); //soft reset
aic_init(); delay(100); aic_init(); delay(100);
aic_initADC(); delay(100); aic_initADC(); delay(100);
aic_initDAC(); delay(100); aic_initDAC(); delay(100);
aic_readPage(0, 27); // check a specific register - a register read test aic_readPage(0, 27); // check a specific register - a register read test
Serial.println("TLV320 enable done"); if (debugToSerial) Serial.println("TLV320 enable done");
return true; return true;
@ -174,7 +182,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) {
// BIAS OFF // BIAS OFF
setMicBias(TYMPAN_MIC_BIAS_OFF); setMicBias(TYMPAN_MIC_BIAS_OFF);
Serial.println("Set Audio Input to Line In"); if (debugToSerial) Serial.println("Set Audio Input to Line In");
return true; return true;
} else if (n == TYMPAN_INPUT_JACK_AS_MIC) { } else if (n == TYMPAN_INPUT_JACK_AS_MIC) {
// mic-jack = IN3 // mic-jack = IN3
@ -185,7 +193,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) {
// BIAS on, using default // BIAS on, using default
setMicBias(TYMPAN_DEFAULT_MIC_BIAS); setMicBias(TYMPAN_DEFAULT_MIC_BIAS);
Serial.println("Set Audio Input to JACK AS MIC, BIAS SET TO DEFAULT 2.5V"); if (debugToSerial) Serial.println("Set Audio Input to JACK AS MIC, BIAS SET TO DEFAULT 2.5V");
return true; return true;
} else if (n == TYMPAN_INPUT_JACK_AS_LINEIN) { } else if (n == TYMPAN_INPUT_JACK_AS_LINEIN) {
// 1 // 1
@ -197,7 +205,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) {
// BIAS Off // BIAS Off
setMicBias(TYMPAN_MIC_BIAS_OFF); setMicBias(TYMPAN_MIC_BIAS_OFF);
Serial.println("Set Audio Input to JACK AS LINEIN, BIAS OFF"); if (debugToSerial) Serial.println("Set Audio Input to JACK AS LINEIN, BIAS OFF");
return true; return true;
} else if (n == TYMPAN_INPUT_ON_BOARD_MIC) { } else if (n == TYMPAN_INPUT_ON_BOARD_MIC) {
// on-board = IN2 // on-board = IN2
@ -207,11 +215,11 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) {
aic_writeAddress(TYMPAN_MICPGA_RIGHT_NEGATIVE_REG, TYMPAN_MIC_ROUTING_NEGATIVE_CM_TO_CM1L & TYMPAN_MIC_ROUTING_RESISTANCE_DEFAULT); aic_writeAddress(TYMPAN_MICPGA_RIGHT_NEGATIVE_REG, TYMPAN_MIC_ROUTING_NEGATIVE_CM_TO_CM1L & TYMPAN_MIC_ROUTING_RESISTANCE_DEFAULT);
// BIAS Off // BIAS Off
setMicBias(TYMPAN_MIC_BIAS_OFF); setMicBias(TYMPAN_MIC_BIAS_OFF);
Serial.println("Set Audio Input to Tympan On-Board MIC, BIAS OFF"); if (debugToSerial) Serial.println("Set Audio Input to Tympan On-Board MIC, BIAS OFF");
return true; return true;
} }
Serial.print("ERROR: Unable to Select Input - Value not supported: "); Serial.print("controlTLV320AIC3206: ERROR: Unable to Select Input - Value not supported: ");
Serial.println(n); Serial.println(n);
return false; return false;
} }
@ -233,13 +241,13 @@ bool AudioControlTLV320AIC3206::setMicBias(int n) {
aic_writeAddress(TYMPAN_MIC_BIAS_REG, TYMPAN_MIC_BIAS_POWER_OFF); // power up mic bias aic_writeAddress(TYMPAN_MIC_BIAS_REG, TYMPAN_MIC_BIAS_POWER_OFF); // power up mic bias
return true; return true;
} }
Serial.print("ERROR: Unable to set MIC BIAS - Value not supported: "); Serial.print("controlTLV320AIC3206: ERROR: Unable to set MIC BIAS - Value not supported: ");
Serial.println(n); Serial.println(n);
return false; return false;
} }
void AudioControlTLV320AIC3206::aic_reset() { void AudioControlTLV320AIC3206::aic_reset() {
Serial.println("INFO: Reseting AIC"); if (debugToSerial) Serial.println("INFO: Reseting AIC");
aic_writePage(0x00, 0x01, 0x01); aic_writePage(0x00, 0x01, 0x01);
// aic_writeAddress(0x0001, 0x01); // aic_writeAddress(0x0001, 0x01);
@ -254,7 +262,7 @@ void AudioControlTLV320AIC3206::aic_reset() {
// aic_writeAddress(TYMPAN_RIGHT_MICPGA_NEGATIVE_REG, TYMPAN_MIC_ROUTING_NEGATIVE_CM_TO_CM1L & TYMPAN_MIC_ROUTING_RESISTANCE_DEFAULT); // aic_writeAddress(TYMPAN_RIGHT_MICPGA_NEGATIVE_REG, TYMPAN_MIC_ROUTING_NEGATIVE_CM_TO_CM1L & TYMPAN_MIC_ROUTING_RESISTANCE_DEFAULT);
void AudioControlTLV320AIC3206::aic_initADC() { void AudioControlTLV320AIC3206::aic_initADC() {
Serial.println("INFO: Initializing AIC ADC"); if (debugToSerial) Serial.println("INFO: Initializing AIC ADC");
aic_writeAddress(TYMPAN_ADC_PROCESSING_BLOCK_REG, PRB_R); // processing blocks - ADC aic_writeAddress(TYMPAN_ADC_PROCESSING_BLOCK_REG, PRB_R); // processing blocks - ADC
aic_writePage(1, 61, 0); // 0x3D // Select ADC PTM_R4 Power Tune? aic_writePage(1, 61, 0); // 0x3D // Select ADC PTM_R4 Power Tune?
aic_writePage(1, 71, 0b00110001); // 0x47 // Set MicPGA startup delay to 3.1ms aic_writePage(1, 71, 0b00110001); // 0x47 // Set MicPGA startup delay to 3.1ms
@ -276,21 +284,22 @@ void AudioControlTLV320AIC3206::aic_initADC() {
bool AudioControlTLV320AIC3206::setInputGain_dB(float volume) { bool AudioControlTLV320AIC3206::setInputGain_dB(float volume) {
if (volume < 0.0) { if (volume < 0.0) {
volume = 0.0; // 0.0 dB volume = 0.0; // 0.0 dB
Serial.println("WARNING: Attempting to set MIC volume outside range"); Serial.println("controlTLV320AIC3206: WARNING: Attempting to set MIC volume outside range");
} }
if (volume > 47.5) { if (volume > 47.5) {
volume = 47.5; // 47.5 dB volume = 47.5; // 47.5 dB
Serial.println("WARNING: Attempting to set MIC volume outside range"); Serial.println("controlTLV320AIC3206: WARNING: Attempting to set MIC volume outside range");
} }
Serial.print("INFO: Setting MIC volume to ");
Serial.print(volume, 1);
volume = volume * 2.0; // convert to value map (0.5 dB steps) volume = volume * 2.0; // convert to value map (0.5 dB steps)
int8_t volume_int = (int8_t) (round(volume)); // round int8_t volume_int = (int8_t) (round(volume)); // round
Serial.print(". Converted to volume map => "); if (debugToSerial) {
Serial.println(volume_int); Serial.print("INFO: Setting MIC volume to ");
Serial.print(volume, 1);
Serial.print(". Converted to volume map => ");
Serial.println(volume_int);
}
aic_writeAddress(TYMPAN_MICPGA_LEFT_VOLUME_REG, TYMPAN_MICPGA_VOLUME_ENABLE | volume_int); // enable Left MicPGA, set gain to 0 dB aic_writeAddress(TYMPAN_MICPGA_LEFT_VOLUME_REG, TYMPAN_MICPGA_VOLUME_ENABLE | volume_int); // enable Left MicPGA, set gain to 0 dB
aic_writeAddress(TYMPAN_MICPGA_RIGHT_VOLUME_REG, TYMPAN_MICPGA_VOLUME_ENABLE | volume_int); // enable Right MicPGA, set gain to 0 dB aic_writeAddress(TYMPAN_MICPGA_RIGHT_VOLUME_REG, TYMPAN_MICPGA_VOLUME_ENABLE | volume_int); // enable Right MicPGA, set gain to 0 dB
@ -318,21 +327,22 @@ bool AudioControlTLV320AIC3206::volume_dB(float volume) {
// Constrain to limits // Constrain to limits
if (volume > 24.0) { if (volume > 24.0) {
volume = 24.0; volume = 24.0;
Serial.println("WARNING: Attempting to set DAC Volume outside range"); Serial.println("controlTLV320AIC3206: WARNING: Attempting to set DAC Volume outside range");
} }
if (volume < -63.5) { if (volume < -63.5) {
volume = -63.5; volume = -63.5;
Serial.println("WARNING: Attempting to set DAC Volume outside range"); Serial.println("controlTLV320AIC3206: WARNING: Attempting to set DAC Volume outside range");
} }
Serial.print("INFO: Setting DAC volume to ");
Serial.print(volume, 1);
volume = volume * 2.0; // convert to value map (0.5 dB steps) volume = volume * 2.0; // convert to value map (0.5 dB steps)
int8_t volume_int = (int8_t) (round(volume)); // round int8_t volume_int = (int8_t) (round(volume)); // round
Serial.print(". Converted to volume map => "); if (debugToSerial) {
Serial.println(volume_int); Serial.print("INFO: Setting DAC volume to ");
Serial.print(volume, 1);
Serial.print(". Converted to volume map => ");
Serial.println(volume_int);
}
aic_writeAddress(TYMPAN_DAC_VOLUME_RIGHT_REG, volume_int); aic_writeAddress(TYMPAN_DAC_VOLUME_RIGHT_REG, volume_int);
aic_writeAddress(TYMPAN_DAC_VOLUME_LEFT_REG, volume_int); aic_writeAddress(TYMPAN_DAC_VOLUME_LEFT_REG, volume_int);
@ -340,7 +350,7 @@ bool AudioControlTLV320AIC3206::volume_dB(float volume) {
} }
void AudioControlTLV320AIC3206::aic_initDAC() { void AudioControlTLV320AIC3206::aic_initDAC() {
Serial.println("INFO: Initializing AIC DAC"); if (debugToSerial) Serial.println("INFO: Initializing AIC DAC");
// PLAYBACK SETUP // PLAYBACK SETUP
aic_writeAddress(TYMPAN_DAC_PROCESSING_BLOCK_REG, PRB_P); // processing blocks - DAC aic_writeAddress(TYMPAN_DAC_PROCESSING_BLOCK_REG, PRB_P); // processing blocks - DAC
@ -363,7 +373,7 @@ void AudioControlTLV320AIC3206::aic_initDAC() {
} }
void AudioControlTLV320AIC3206::aic_init() { void AudioControlTLV320AIC3206::aic_init() {
Serial.println("INFO: Initializing AIC"); if (debugToSerial) Serial.println("INFO: Initializing AIC");
// PLL // PLL
aic_writePage(0, 4, 3); // 0x04 low PLL clock range, MCLK is PLL input, PLL_OUT is CODEC_CLKIN aic_writePage(0, 4, 3); // 0x04 low PLL clock range, MCLK is PLL input, PLL_OUT is CODEC_CLKIN
@ -407,7 +417,7 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg)
Wire.write(reg); Wire.write(reg);
unsigned int result = Wire.endTransmission(); unsigned int result = Wire.endTransmission();
if (result != 0) { if (result != 0) {
Serial.print("ERROR: Read Page. Page: ");Serial.print(page); Serial.print("controlTLV320AIC3206: ERROR: Read Page. Page: ");Serial.print(page);
Serial.print(" Reg: ");Serial.print(reg); Serial.print(" Reg: ");Serial.print(reg);
Serial.print(". Received Error During Read Page: "); Serial.print(". Received Error During Read Page: ");
Serial.println(result); Serial.println(result);
@ -415,7 +425,7 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg)
return val; return val;
} }
if (Wire.requestFrom(AIC3206_I2C_ADDR, 1) < 1) { if (Wire.requestFrom(AIC3206_I2C_ADDR, 1) < 1) {
Serial.print("ERROR: Read Page. Page: ");Serial.print(page); Serial.print("controlTLV320AIC3206: ERROR: Read Page. Page: ");Serial.print(page);
Serial.print(" Reg: ");Serial.print(reg); Serial.print(" Reg: ");Serial.print(reg);
Serial.println(". Nothing to return"); Serial.println(". Nothing to return");
val = 400; val = 400;
@ -423,16 +433,18 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg)
} }
if (Wire.available() >= 1) { if (Wire.available() >= 1) {
uint16_t val = Wire.read(); uint16_t val = Wire.read();
Serial.print("INFO: Read Page. Page: ");Serial.print(page); if (debugToSerial) {
Serial.print(" Reg: ");Serial.print(reg); Serial.print("INFO: Read Page. Page: ");Serial.print(page);
Serial.print(". Received: "); Serial.print(" Reg: ");Serial.print(reg);
Serial.println(val, HEX); Serial.print(". Received: ");
Serial.println(val, HEX);
}
return val; return val;
} }
} else { } else {
Serial.print("INFO: Read Page. Page: ");Serial.print(page); Serial.print("controlTLV320AIC3206: INFO: Read Page. Page: ");Serial.print(page);
Serial.print(" Reg: ");Serial.print(reg); Serial.print(" Reg: ");Serial.print(reg);
Serial.println(". Failed to go to read page. Could not go there"); Serial.println(". Failed to go to read page. Could not go there.");
val = 500; val = 500;
return val; return val;
} }
@ -448,9 +460,11 @@ bool AudioControlTLV320AIC3206::aic_writeAddress(uint16_t address, uint8_t val)
} }
bool AudioControlTLV320AIC3206::aic_writePage(uint8_t page, uint8_t reg, uint8_t val) { bool AudioControlTLV320AIC3206::aic_writePage(uint8_t page, uint8_t reg, uint8_t val) {
Serial.print("INFO: Write Page. Page: ");Serial.print(page); if (debugToSerial) {
Serial.print(" Reg: ");Serial.print(reg); Serial.print("INFO: Write Page. Page: ");Serial.print(page);
Serial.print(" Val: ");Serial.println(val); Serial.print(" Reg: ");Serial.print(reg);
Serial.print(" Val: ");Serial.println(val);
}
if (aic_goToPage(page)) { if (aic_goToPage(page)) {
Wire.beginTransmission(AIC3206_I2C_ADDR); Wire.beginTransmission(AIC3206_I2C_ADDR);
Wire.write(reg);delay(10); Wire.write(reg);delay(10);
@ -458,7 +472,7 @@ bool AudioControlTLV320AIC3206::aic_writePage(uint8_t page, uint8_t reg, uint8_t
uint8_t result = Wire.endTransmission(); uint8_t result = Wire.endTransmission();
if (result == 0) return true; if (result == 0) return true;
else { else {
Serial.print("ERROR: Received Error During Write Page: "); Serial.print("controlTLV320AIC3206: Received Error During writePage(): Error = ");
Serial.println(result); Serial.println(result);
} }
} }
@ -467,11 +481,11 @@ bool AudioControlTLV320AIC3206::aic_writePage(uint8_t page, uint8_t reg, uint8_t
bool AudioControlTLV320AIC3206::aic_goToPage(byte page) { bool AudioControlTLV320AIC3206::aic_goToPage(byte page) {
Wire.beginTransmission(AIC3206_I2C_ADDR); Wire.beginTransmission(AIC3206_I2C_ADDR);
Wire.write(0x00); delay(10);// page register Wire.write(0x00); delay(10);// page register //was delay(10) from BPF
Wire.write(page); delay(10);// go to page Wire.write(page); delay(10);// go to page //was delay(10) from BPF
byte result = Wire.endTransmission(); byte result = Wire.endTransmission();
if (result != 0) { if (result != 0) {
Serial.print("ERROR: Received Error During GoTo Page: "); Serial.print("controlTLV320AIC3206: Received Error During goToPage(): Error = ");
Serial.println(result); Serial.println(result);
if (result == 2) { if (result == 2) {
// failed to transmit address // failed to transmit address

@ -16,7 +16,8 @@ class AudioControlTLV320AIC3206: public AudioControl
{ {
public: public:
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node //GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
AudioControlTLV320AIC3206(void) {}; AudioControlTLV320AIC3206(void) { debugToSerial = false; };
AudioControlTLV320AIC3206(bool _debugToSerial) { debugToSerial = _debugToSerial; };
bool enable(void); bool enable(void);
bool disable(void); bool disable(void);
bool volume(float n); bool volume(float n);
@ -25,7 +26,7 @@ public:
bool inputSelect(int n); bool inputSelect(int n);
bool setInputGain_dB(float n); bool setInputGain_dB(float n);
bool setMicBias(int n); bool setMicBias(int n);
bool debugToSerial;
private: private:
void aic_reset(void); void aic_reset(void);
void aic_init(void); void aic_init(void);
@ -35,6 +36,7 @@ private:
bool aic_writePage(uint8_t page, uint8_t reg, uint8_t val); bool aic_writePage(uint8_t page, uint8_t reg, uint8_t val);
bool aic_writeAddress(uint16_t address, uint8_t val); bool aic_writeAddress(uint16_t address, uint8_t val);
bool aic_goToPage(uint8_t page); bool aic_goToPage(uint8_t page);
}; };
#define TYMPAN_OUTPUT_HEADPHONE_JACK_OUT 1 #define TYMPAN_OUTPUT_HEADPHONE_JACK_OUT 1

@ -23,10 +23,10 @@
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board
AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo. AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo.
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioEffectCompressor_F32 comp1, comp2; AudioEffectCompressor_F32 comp1, comp2;
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
//Make all of the audio connections, with the option of USB audio in and out //Make all of the audio connections, with the option of USB audio in and out
//note that you ALWAYS have to have an I2S connection (either in or out) to have a clock //note that you ALWAYS have to have an I2S connection (either in or out) to have a clock

@ -24,10 +24,10 @@
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board
AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo. AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo.
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h
AudioEffectGain_F32 gain1, gain2; //Applies digital gain to audio data. Expected Float data. AudioEffectGain_F32 gain1, gain2; //Applies digital gain to audio data. Expected Float data.
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
//Make all of the audio connections //Make all of the audio connections
AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter

@ -23,13 +23,13 @@
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board
AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo. AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo.
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioMixer4_F32 mixer; //mix floating point data AudioMixer4_F32 mixer; //mix floating point data
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
//Do you want to use the USB audio as your input, or do you want to use i2s as your input? //Do you want to use the USB audio as your input, or do you want to use i2s as your input?
#define DO_USB 1 //set to 1 to enable USB audio. Be sure to go under the "Tools" menu and do "USB Type" -> "Audio" #define DO_USB 0 //set to 1 to enable USB audio. Be sure to go under the "Tools" menu and do "USB Type" -> "Audio"
#if DO_USB #if DO_USB
AudioInputUSB usb_in; AudioInputUSB usb_in;
AudioConnection patchCord1(usb_in, 0, int2Float1, 0); AudioConnection patchCord1(usb_in, 0, int2Float1, 0);

@ -24,10 +24,10 @@
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board
AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo. AudioInputI2S i2s_in; //Digital audio *from* the Teensy Audio Board ADC. Sends Int16. Stereo.
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h AudioConvert_I16toF32 int2Float1, int2Float2; //Converts Int16 to Float. See class in AudioStream_F32.h
AudioEffectMine_F32 effect1, effect2; //This is your own algorithms
AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h AudioConvert_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h
AudioEffectMine_F32 effect1, effect2; //This is your own algorithms
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
//Make all of the audio connections //Make all of the audio connections
AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter

@ -19,15 +19,15 @@
#include <OpenAudio_ArduinoLibrary.h> //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32 #include <OpenAudio_ArduinoLibrary.h> //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32
#define DO_USB 1 //set to 1 to enable USB audio. Be sure to go under the "Tools" menu and do "USB Type" -> "Audio" #define DO_USB 0 //set to 1 to enable USB audio. Be sure to go under the "Tools" menu and do "USB Type" -> "Audio"
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConvert_F32toI16 float2Int; //Converts Float to Int16. See class in AudioStream_F32.h
AudioSynthWaveform_F32 osc1; // Audio-rate oscillator. AudioSynthWaveform_F32 osc1; // Audio-rate oscillator.
AudioSynthWaveform_F32 lfo1; // Low-frequency oscillator to modulate the main oscillator with. AudioSynthWaveform_F32 lfo1; // Low-frequency oscillator to modulate the main oscillator with.
AudioConvert_F32toI16 float2Int; //Converts Float to Int16. See class in AudioStream_F32.h
AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo
AudioConnection_F32 patchCord1(osc1, 0, float2Int, 0); // connect the oscillator to the float 2 int converter AudioConnection_F32 patchCord1(osc1, 0, float2Int, 0); // connect the oscillator to the float 2 int converter
AudioConnection_F32 patchCord2(lfo1, 0, osc1, 0); // connect the output of the lfo to the mod input of the oscillator AudioConnection_F32 patchCord2(lfo1, 0, osc1, 0); // connect the output of the lfo to the mod input of the oscillator

@ -41,7 +41,7 @@ float setI2SFreq(const float freq_Hz) {
} __attribute__((__packed__)) tmclk; } __attribute__((__packed__)) tmclk;
const int numfreqs = 16; const int numfreqs = 16;
const int samplefreqs[numfreqs] = { 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000}; const int samplefreqs[numfreqs] = { 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)(44117.64706 * 2), 96000, 176400, (int)(44117.64706 * 4), 192000};
#if (F_PLL==16000000) #if (F_PLL==16000000)
const tmclk clkArr[numfreqs] = {{4, 125}, {16, 125}, {148, 839}, {32, 125}, {145, 411}, {48, 125}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} }; const tmclk clkArr[numfreqs] = {{4, 125}, {16, 125}, {148, 839}, {32, 125}, {145, 411}, {48, 125}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };

@ -19,6 +19,8 @@ class AudioPlayQueue_F32 : public AudioStream_F32
public: public:
AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL), AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL),
userblock(NULL), head(0), tail(0) { } userblock(NULL), head(0), tail(0) { }
AudioPlayQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL),
userblock(NULL), head(0), tail(0) { }
//void play(int16_t data); //void play(int16_t data);
//void play(const int16_t *data, uint32_t len); //void play(const int16_t *data, uint32_t len);
//void play(float32_t data); //void play(float32_t data);

@ -20,6 +20,8 @@ class AudioRecordQueue_F32 : public AudioStream_F32
public: public:
AudioRecordQueue_F32(void) : AudioStream_F32(1, inputQueueArray), AudioRecordQueue_F32(void) : AudioStream_F32(1, inputQueueArray),
userblock(NULL), head(0), tail(0), enabled(0) { } userblock(NULL), head(0), tail(0), enabled(0) { }
AudioRecordQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray),
userblock(NULL), head(0), tail(0), enabled(0) { }
void begin(void) { void begin(void) {
clear(); clear();
enabled = 1; enabled = 1;

@ -44,13 +44,20 @@ class AudioSynthNoisePink_F32 : public AudioStream_F32
//GUI: shortName:pinknoise //this line used for automatic generation of GUI node //GUI: shortName:pinknoise //this line used for automatic generation of GUI node
public: public:
AudioSynthNoisePink_F32() : AudioStream_F32(0, NULL) { AudioSynthNoisePink_F32() : AudioStream_F32(0, NULL) {
setDefaultValues();
enabled = 1;
}
AudioSynthNoisePink_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) {
setDefaultValues();
enabled = 1;
}
void setDefaultValues() {
plfsr = 0x5EED41F5 + instance_cnt++; plfsr = 0x5EED41F5 + instance_cnt++;
paccu = 0; paccu = 0;
pncnt = 0; pncnt = 0;
pinc = 0x0CCC; pinc = 0x0CCC;
pdec = 0x0CCC; pdec = 0x0CCC;
enabled = 1;
} }
void amplitude(float n) { void amplitude(float n) {
if (n < 0.0) n = 0.0; if (n < 0.0) n = 0.0;

@ -47,7 +47,7 @@ void AudioSynthWaveformSine_F32::update(void)
#endif #endif
ph += inc; ph += inc;
block->data[i] = block->data[i] / 32768.0f; // scale to float block->data[i] = block->data[i] / 32767.0f; // scale to float
} }
phase_accumulator = ph; phase_accumulator = ph;

@ -44,7 +44,10 @@ class AudioSynthNoiseWhite_F32 : public AudioStream_F32
//GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node //GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName:whitenoise //this line used for automatic generation of GUI node //GUI: shortName:whitenoise //this line used for automatic generation of GUI node
public: public:
AudioSynthNoiseWhite_F32() : AudioStream_F32(0, NULL) { AudioSynthNoiseWhite_F32() : AudioStream_F32(0, NULL) { setDefaultValues(); }
AudioSynthNoiseWhite_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) { setDefaultValues(); }
void setDefaultValues(void) {
level = 0; level = 0;
seed = 1 + instance_count++; seed = 1 + instance_count++;
} }

@ -0,0 +1,393 @@
//ifndef _BTNRH_FFT_H
//define _BTNRH_FFT_H
#include "BTNRH_rfft.h"
#include <math.h>
//#include "chapro.h"
//#include "cha_ff.h"
/***********************************************************/
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
// In IEEE Acoustics, Speech, and Signal Processing Society.
// "Programs for Digital Signal Processing," IEEE Press, New York,
namespace BTNRH_FFT {
static __inline int
ilog2(int n)
{
int m;
for (m = 1; m < 32; m++)
if (n == (1 << m))
return (m);
return (-1);
}
static __inline int
bitrev(int ii, int m)
{
register int jj;
jj = ii & 1;
--m;
while (--m > 0) {
ii >>= 1;
jj <<= 1;
jj |= ii & 1;
}
return (jj);
}
static __inline void
rad2(int ii, float *x0, float *x1)
{
int k;
float t;
for (k = 0; k < ii; k++) {
t = x0[k] + x1[k];
x1[k] = x0[k] - x1[k];
x0[k] = t;
}
}
static __inline void
reorder1(int m, float *x)
{
int j, k, kl, n;
float t;
k = 4;
kl = 2;
n = 1 << m;
for (j = 4; j <= n; j += 2) {
if (k > j) {
t = x[j - 1];
x[j - 1] = x[k - 1];
x[k - 1] = t;
}
k -= 2;
if (k <= kl) {
k = 2 * j;
kl = j;
}
}
}
static __inline void
reorder2(int m, float *x)
{
int ji, ij, n;
float t;
n = 1 << m;
for (ij = 0; ij <= (n - 2); ij += 2) {
ji = bitrev(ij >> 1, m) << 1;
if (ij < ji) {
t = x[ij];
x[ij] = x[ji];
x[ji] = t;
t = x[ij + 1];
x[ij + 1] = x[ji + 1];
x[ji + 1] = t;
}
}
}
/***********************************************************/
// rcfft
static void
rcrad4(int ii, int nn,
float *x0, float *x1, float *x2, float *x3,
float *x4, float *x5, float *x6, float *x7)
{
double arg, tpiovn;
float c1, c2, c3, s1, s2, s3, pr, pi, r1, r5;
float t0, t1, t2, t3, t4, t5, t6, t7;
int i0, i4, j, j0, ji, jl, jr, jlast, k, k0, kl, m, n, ni;
n = nn / 4;
for (m = 1; (1 << m) < n; m++)
continue;
tpiovn = 2 * M_PI / nn;
ji = 3;
jl = 2;
jr = 2;
ni = (n + 1) / 2;
for (i0 = 0; i0 < ni; i0++) {
if (i0 == 0) {
for (k = 0; k < ii; k++) {
t0 = x0[k] + x2[k];
t1 = x1[k] + x3[k];
x2[k] = x0[k] - x2[k];
x3[k] = x1[k] - x3[k];
x0[k] = t0 + t1;
x1[k] = t0 - t1;
}
if (nn > 4) {
k0 = ii * 4;
kl = k0 + ii;
for (k = k0; k < kl; k++) {
pr = (float) (M_SQRT1_2 * (x1[k] - x3[k]));
pi = (float) (M_SQRT1_2 * (x1[k] + x3[k]));
x3[k] = x2[k] + pi;
x1[k] = pi - x2[k];
x2[k] = x0[k] - pr;
x0[k] += pr;
}
}
} else {
arg = tpiovn * bitrev(i0, m);
c1 = cosf(arg);
s1 = sinf(arg);
c2 = c1 * c1 - s1 * s1;
s2 = c1 * s1 + c1 * s1;
c3 = c1 * c2 - s1 * s2;
s3 = c2 * s1 + s2 * c1;
i4 = ii * 4;
j0 = jr * i4;
k0 = ji * i4;
jlast = j0 + ii;
for (j = j0; j < jlast; j++) {
k = k0 + j - j0;
r1 = x1[j] * c1 - x5[k] * s1;
r5 = x1[j] * s1 + x5[k] * c1;
t2 = x2[j] * c2 - x6[k] * s2;
t6 = x2[j] * s2 + x6[k] * c2;
t3 = x3[j] * c3 - x7[k] * s3;
t7 = x3[j] * s3 + x7[k] * c3;
t0 = x0[j] + t2;
t4 = x4[k] + t6;
t2 = x0[j] - t2;
t6 = x4[k] - t6;
t1 = r1 + t3;
t5 = r5 + t7;
t3 = r1 - t3;
t7 = r5 - t7;
x0[j] = t0 + t1;
x7[k] = t4 + t5;
x6[k] = t0 - t1;
x1[j] = t5 - t4;
x2[j] = t2 - t7;
x5[k] = t6 + t3;
x4[k] = t2 + t7;
x3[j] = t3 - t6;
}
jr += 2;
ji -= 2;
if (ji <= jl) {
ji = 2 * jr - 1;
jl = jr;
}
}
}
}
//-----------------------------------------------------------
static int
rcfft2(float *x, int m)
{
int ii, nn, m2, it, n;
n = 1 << m;;
m2 = m / 2;
// radix 2
if (m <= m2 * 2) {
nn = 1;
} else {
nn = 2;
ii = n / nn;
rad2(ii, x, x + ii);
}
// radix 4
if (m2 != 0) {
for (it = 0; it < m2; it++) {
nn = nn * 4;
ii = n / nn;
rcrad4(ii, nn, x, x + ii, x + 2 * ii, x + 3 * ii,
x, x + ii, x + 2 * ii, x + 3 * ii);
}
}
// re-order
reorder1(m, x);
reorder2(m, x);
for (it = 3; it < n; it += 2)
x[it] = -x[it];
x[n] = x[1];
x[1] = 0.0;
x[n + 1] = 0.0;
return (0);
}
/***********************************************************/
// rcfft
static void
crrad4(int jj, int nn,
float *x0, float *x1, float *x2, float *x3,
float *x4, float *x5, float *x6, float *x7)
{
double arg, tpiovn;
float c1, c2, c3, s1, s2, s3;
float t0, t1, t2, t3, t4, t5, t6, t7;
int ii, j, j0, ji, jr, jl, jlast, j4, k, k0, kl, m, n, ni;
tpiovn = 2 * M_PI / nn;
ji = 3;
jl = 2;
jr = 2;
n = nn / 4;
for (m = 1; (1 << m) < n; m++)
continue;
ni = (n + 1) / 2;
for (ii = 0; ii < ni; ii++) {
if (ii == 0) {
for (k = 0; k < jj; k++) {
t0 = x0[k] + x1[k];
t1 = x0[k] - x1[k];
t2 = x2[k] * 2;
t3 = x3[k] * 2;
x0[k] = t0 + t2;
x2[k] = t0 - t2;
x1[k] = t1 + t3;
x3[k] = t1 - t3;
}
if (nn > 4) {
k0 = jj * 4;
kl = k0 + jj;
for (k = k0; k < kl; k++) {
t2 = x0[k] - x2[k];
t3 = x1[k] + x3[k];
x0[k] = (x0[k] + x2[k]) * 2;
x2[k] = (x3[k] - x1[k]) * 2;
x1[k] = (float) ((t2 + t3) * M_SQRT2);
x3[k] = (float) ((t3 - t2) * M_SQRT2);
}
}
} else {
arg = tpiovn * bitrev(ii, m);
c1 = cosf(arg);
s1 = -sinf(arg);
c2 = c1 * c1 - s1 * s1;
s2 = c1 * s1 + c1 * s1;
c3 = c1 * c2 - s1 * s2;
s3 = c2 * s1 + s2 * c1;
j4 = jj * 4;
j0 = jr * j4;
k0 = ji * j4;
jlast = j0 + jj;
for (j = j0; j < jlast; j++) {
k = k0 + j - j0;
t0 = x0[j] + x6[k];
t1 = x7[k] - x1[j];
t2 = x0[j] - x6[k];
t3 = x7[k] + x1[j];
t4 = x2[j] + x4[k];
t5 = x5[k] - x3[j];
t6 = x5[k] + x3[j];
t7 = x4[k] - x2[j];
x0[j] = t0 + t4;
x4[k] = t1 + t5;
x1[j] = (t2 + t6) * c1 - (t3 + t7) * s1;
x5[k] = (t2 + t6) * s1 + (t3 + t7) * c1;
x2[j] = (t0 - t4) * c2 - (t1 - t5) * s2;
x6[k] = (t0 - t4) * s2 + (t1 - t5) * c2;
x3[j] = (t2 - t6) * c3 - (t3 - t7) * s3;
x7[k] = (t2 - t6) * s3 + (t3 - t7) * c3;
}
jr += 2;
ji -= 2;
if (ji <= jl) {
ji = 2 * jr - 1;
jl = jr;
}
}
}
}
//-----------------------------------------------------------
static int
crfft2(float *x, int m)
{
int n, i, it, nn, jj, m2;
n = 1 << m;
x[1] = x[n];
m2 = m / 2;
// re-order
for (i = 3; i < n; i += 2)
x[i] = -x[i];
reorder2(m, x);
reorder1(m, x);
// radix 4
if (m2 != 0) {
nn = 4 * n;
for (it = 0; it < m2; it++) {
nn = nn / 4;
jj = n / nn;
crrad4(jj, nn, x, x + jj, x + 2 * jj, x + 3 * jj,
x, x + jj, x + 2 * jj, x + 3 * jj);
}
}
// radix 2
if (m > m2 * 2) {
jj = n / 2;
rad2(jj, x, x + jj);
}
return (0);
}
/***********************************************************/
// real-to-complex FFT
//FUNC(void)
void cha_fft_rc(float *x, int n)
{
int m;
// assume n is a power of two
m = ilog2(n);
rcfft2(x, m);
}
// complex-to-real inverse FFT
//FUNC(void)
void cha_fft_cr(float *x, int n)
{
int i, m;
// assume n is a power of two
m = ilog2(n);
crfft2(x, m);
// scale inverse by 1/n
for (i = 0; i < n; i++) {
x[i] /= n;
}
}
};
//endif

@ -0,0 +1,19 @@
#ifndef _BTNRH_FFT_H
#define _BTNRH_FFT_H
#include <math.h>
//#include "chapro.h"
//#include "cha_ff.h"
/***********************************************************/
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
// In IEEE Acoustics, Speech, and Signal Processing Society.
// "Programs for Digital Signal Processing," IEEE Press, New York,
namespace BTNRH_FFT {
void cha_fft_cr(float *, int);
void cha_fft_rc(float *, int);
}
#endif

@ -1,384 +0,0 @@
#include <math.h>
//#include "chapro.h"
//#include "cha_ff.h"
/***********************************************************/
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
// In IEEE Acoustics, Speech, and Signal Processing Society.
// "Programs for Digital Signal Processing," IEEE Press, New York,
static __inline int
ilog2(int n)
{
int m;
for (m = 1; m < 32; m++)
if (n == (1 << m))
return (m);
return (-1);
}
static __inline int
bitrev(int ii, int m)
{
register int jj;
jj = ii & 1;
--m;
while (--m > 0) {
ii >>= 1;
jj <<= 1;
jj |= ii & 1;
}
return (jj);
}
static __inline void
rad2(int ii, float *x0, float *x1)
{
int k;
float t;
for (k = 0; k < ii; k++) {
t = x0[k] + x1[k];
x1[k] = x0[k] - x1[k];
x0[k] = t;
}
}
static __inline void
reorder1(int m, float *x)
{
int j, k, kl, n;
float t;
k = 4;
kl = 2;
n = 1 << m;
for (j = 4; j <= n; j += 2) {
if (k > j) {
t = x[j - 1];
x[j - 1] = x[k - 1];
x[k - 1] = t;
}
k -= 2;
if (k <= kl) {
k = 2 * j;
kl = j;
}
}
}
static __inline void
reorder2(int m, float *x)
{
int ji, ij, n;
float t;
n = 1 << m;
for (ij = 0; ij <= (n - 2); ij += 2) {
ji = bitrev(ij >> 1, m) << 1;
if (ij < ji) {
t = x[ij];
x[ij] = x[ji];
x[ji] = t;
t = x[ij + 1];
x[ij + 1] = x[ji + 1];
x[ji + 1] = t;
}
}
}
/***********************************************************/
// rcfft
static void
rcrad4(int ii, int nn,
float *x0, float *x1, float *x2, float *x3,
float *x4, float *x5, float *x6, float *x7)
{
double arg, tpiovn;
float c1, c2, c3, s1, s2, s3, pr, pi, r1, r5;
float t0, t1, t2, t3, t4, t5, t6, t7;
int i0, i4, j, j0, ji, jl, jr, jlast, k, k0, kl, m, n, ni;
n = nn / 4;
for (m = 1; (1 << m) < n; m++)
continue;
tpiovn = 2 * M_PI / nn;
ji = 3;
jl = 2;
jr = 2;
ni = (n + 1) / 2;
for (i0 = 0; i0 < ni; i0++) {
if (i0 == 0) {
for (k = 0; k < ii; k++) {
t0 = x0[k] + x2[k];
t1 = x1[k] + x3[k];
x2[k] = x0[k] - x2[k];
x3[k] = x1[k] - x3[k];
x0[k] = t0 + t1;
x1[k] = t0 - t1;
}
if (nn > 4) {
k0 = ii * 4;
kl = k0 + ii;
for (k = k0; k < kl; k++) {
pr = (float) (M_SQRT1_2 * (x1[k] - x3[k]));
pi = (float) (M_SQRT1_2 * (x1[k] + x3[k]));
x3[k] = x2[k] + pi;
x1[k] = pi - x2[k];
x2[k] = x0[k] - pr;
x0[k] += pr;
}
}
} else {
arg = tpiovn * bitrev(i0, m);
c1 = cosf(arg);
s1 = sinf(arg);
c2 = c1 * c1 - s1 * s1;
s2 = c1 * s1 + c1 * s1;
c3 = c1 * c2 - s1 * s2;
s3 = c2 * s1 + s2 * c1;
i4 = ii * 4;
j0 = jr * i4;
k0 = ji * i4;
jlast = j0 + ii;
for (j = j0; j < jlast; j++) {
k = k0 + j - j0;
r1 = x1[j] * c1 - x5[k] * s1;
r5 = x1[j] * s1 + x5[k] * c1;
t2 = x2[j] * c2 - x6[k] * s2;
t6 = x2[j] * s2 + x6[k] * c2;
t3 = x3[j] * c3 - x7[k] * s3;
t7 = x3[j] * s3 + x7[k] * c3;
t0 = x0[j] + t2;
t4 = x4[k] + t6;
t2 = x0[j] - t2;
t6 = x4[k] - t6;
t1 = r1 + t3;
t5 = r5 + t7;
t3 = r1 - t3;
t7 = r5 - t7;
x0[j] = t0 + t1;
x7[k] = t4 + t5;
x6[k] = t0 - t1;
x1[j] = t5 - t4;
x2[j] = t2 - t7;
x5[k] = t6 + t3;
x4[k] = t2 + t7;
x3[j] = t3 - t6;
}
jr += 2;
ji -= 2;
if (ji <= jl) {
ji = 2 * jr - 1;
jl = jr;
}
}
}
}
//-----------------------------------------------------------
static int
rcfft2(float *x, int m)
{
int ii, nn, m2, it, n;
n = 1 << m;;
m2 = m / 2;
// radix 2
if (m <= m2 * 2) {
nn = 1;
} else {
nn = 2;
ii = n / nn;
rad2(ii, x, x + ii);
}
// radix 4
if (m2 != 0) {
for (it = 0; it < m2; it++) {
nn = nn * 4;
ii = n / nn;
rcrad4(ii, nn, x, x + ii, x + 2 * ii, x + 3 * ii,
x, x + ii, x + 2 * ii, x + 3 * ii);
}
}
// re-order
reorder1(m, x);
reorder2(m, x);
for (it = 3; it < n; it += 2)
x[it] = -x[it];
x[n] = x[1];
x[1] = 0.0;
x[n + 1] = 0.0;
return (0);
}
/***********************************************************/
// rcfft
static void
crrad4(int jj, int nn,
float *x0, float *x1, float *x2, float *x3,
float *x4, float *x5, float *x6, float *x7)
{
double arg, tpiovn;
float c1, c2, c3, s1, s2, s3;
float t0, t1, t2, t3, t4, t5, t6, t7;
int ii, j, j0, ji, jr, jl, jlast, j4, k, k0, kl, m, n, ni;
tpiovn = 2 * M_PI / nn;
ji = 3;
jl = 2;
jr = 2;
n = nn / 4;
for (m = 1; (1 << m) < n; m++)
continue;
ni = (n + 1) / 2;
for (ii = 0; ii < ni; ii++) {
if (ii == 0) {
for (k = 0; k < jj; k++) {
t0 = x0[k] + x1[k];
t1 = x0[k] - x1[k];
t2 = x2[k] * 2;
t3 = x3[k] * 2;
x0[k] = t0 + t2;
x2[k] = t0 - t2;
x1[k] = t1 + t3;
x3[k] = t1 - t3;
}
if (nn > 4) {
k0 = jj * 4;
kl = k0 + jj;
for (k = k0; k < kl; k++) {
t2 = x0[k] - x2[k];
t3 = x1[k] + x3[k];
x0[k] = (x0[k] + x2[k]) * 2;
x2[k] = (x3[k] - x1[k]) * 2;
x1[k] = (float) ((t2 + t3) * M_SQRT2);
x3[k] = (float) ((t3 - t2) * M_SQRT2);
}
}
} else {
arg = tpiovn * bitrev(ii, m);
c1 = cosf(arg);
s1 = -sinf(arg);
c2 = c1 * c1 - s1 * s1;
s2 = c1 * s1 + c1 * s1;
c3 = c1 * c2 - s1 * s2;
s3 = c2 * s1 + s2 * c1;
j4 = jj * 4;
j0 = jr * j4;
k0 = ji * j4;
jlast = j0 + jj;
for (j = j0; j < jlast; j++) {
k = k0 + j - j0;
t0 = x0[j] + x6[k];
t1 = x7[k] - x1[j];
t2 = x0[j] - x6[k];
t3 = x7[k] + x1[j];
t4 = x2[j] + x4[k];
t5 = x5[k] - x3[j];
t6 = x5[k] + x3[j];
t7 = x4[k] - x2[j];
x0[j] = t0 + t4;
x4[k] = t1 + t5;
x1[j] = (t2 + t6) * c1 - (t3 + t7) * s1;
x5[k] = (t2 + t6) * s1 + (t3 + t7) * c1;
x2[j] = (t0 - t4) * c2 - (t1 - t5) * s2;
x6[k] = (t0 - t4) * s2 + (t1 - t5) * c2;
x3[j] = (t2 - t6) * c3 - (t3 - t7) * s3;
x7[k] = (t2 - t6) * s3 + (t3 - t7) * c3;
}
jr += 2;
ji -= 2;
if (ji <= jl) {
ji = 2 * jr - 1;
jl = jr;
}
}
}
}
//-----------------------------------------------------------
static int
crfft2(float *x, int m)
{
int n, i, it, nn, jj, m2;
n = 1 << m;
x[1] = x[n];
m2 = m / 2;
// re-order
for (i = 3; i < n; i += 2)
x[i] = -x[i];
reorder2(m, x);
reorder1(m, x);
// radix 4
if (m2 != 0) {
nn = 4 * n;
for (it = 0; it < m2; it++) {
nn = nn / 4;
jj = n / nn;
crrad4(jj, nn, x, x + jj, x + 2 * jj, x + 3 * jj,
x, x + jj, x + 2 * jj, x + 3 * jj);
}
}
// radix 2
if (m > m2 * 2) {
jj = n / 2;
rad2(jj, x, x + jj);
}
return (0);
}
/***********************************************************/
// real-to-complex FFT
//FUNC(void)
void cha_fft_rc(float *x, int n)
{
int m;
// assume n is a power of two
m = ilog2(n);
rcfft2(x, m);
}
// complex-to-real inverse FFT
//FUNC(void)
void cha_fft_cr(float *x, int n)
{
int i, m;
// assume n is a power of two
m = ilog2(n);
crfft2(x, m);
// scale inverse by 1/n
for (i = 0; i < n; i++) {
x[i] /= n;
}
}
Loading…
Cancel
Save