From 81492cc5aca290d95cd6b681729302148cb7e109 Mon Sep 17 00:00:00 2001 From: Chip Audette Date: Thu, 6 Jul 2017 08:04:30 -0400 Subject: [PATCH] Libarary: big code update from my Tympan_Library --- AudioCalcEnvelope_F32.h | 2 +- AudioCalcGainWDRC_F32.h | 107 ++-- AudioConfigFIRFilterBank_F32.cpp | 82 +++ AudioConfigFIRFilterBank_F32.h | 78 +-- AudioControlTester.cpp | 101 ++++ AudioControlTester.h | 538 ++++++++++++++++++ AudioConvert_F32.h | 2 + AudioEffectCompWDRC_F32.h | 187 ++---- AudioEffectCompressor_F32.h | 19 +- AudioEffectEmpty_F32.h | 1 + AudioEffectGain_F32.h | 5 +- AudioFilterBiquad_F32.cpp | 41 ++ AudioFilterBiquad_F32.h | 204 +++++++ AudioFilterFIR_F32.cpp | 55 ++ AudioFilterFIR_F32.h | 55 +- AudioMixer4_F32.cpp | 30 - AudioMixer4_F32.h | 38 -- AudioMixer_F32.h | 16 +- AudioMultiply_F32.h | 2 + AudioSettings_F32.cpp | 29 + AudioSettings_F32.h | 18 + AudioStream_F32.cpp | 18 + AudioStream_F32.h | 61 +- BTNRH_WDRC_Types.h | 181 ++++++ OpenAudio_ArduinoLibrary.h | 19 +- USB_Audio_F32.h | 146 +++++ control_tlv320aic3206.cpp | 98 ++-- control_tlv320aic3206.h | 6 +- .../BasicCompressor_Float.ino | 4 +- examples/BasicGain_Float/BasicGain_Float.ino | 2 +- .../MixStereoToMono_Float.ino | 6 +- .../MyAudioEffect_Float.ino | 4 +- .../OscillatorWithPitchmod_Float.ino | 8 +- output_i2s_f32.cpp | 2 +- play_queue_f32.h | 2 + record_queue_f32.h | 2 + synth_pinknoise_f32.h | 11 +- synth_sine_f32.cpp | 2 +- synth_whitenoise_f32.h | 5 +- utility/BTNRH_rfft.cpp | 393 +++++++++++++ utility/BTNRH_rfft.h | 19 + utility/rfft.c | 384 ------------- 42 files changed, 2097 insertions(+), 886 deletions(-) create mode 100644 AudioConfigFIRFilterBank_F32.cpp create mode 100644 AudioControlTester.cpp create mode 100644 AudioControlTester.h create mode 100644 AudioFilterBiquad_F32.cpp create mode 100644 AudioFilterBiquad_F32.h create mode 100644 AudioFilterFIR_F32.cpp delete mode 100644 AudioMixer4_F32.cpp delete mode 100644 AudioMixer4_F32.h create mode 100644 AudioSettings_F32.cpp create mode 100644 AudioSettings_F32.h create mode 100644 BTNRH_WDRC_Types.h create mode 100644 USB_Audio_F32.h create mode 100644 utility/BTNRH_rfft.cpp create mode 100644 utility/BTNRH_rfft.h delete mode 100644 utility/rfft.c diff --git a/AudioCalcEnvelope_F32.h b/AudioCalcEnvelope_F32.h index c1975d5..8a7c248 100644 --- a/AudioCalcEnvelope_F32.h +++ b/AudioCalcEnvelope_F32.h @@ -47,7 +47,7 @@ class AudioCalcEnvelope_F32 : public AudioStream_F32 audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); if (!out_block) return; - // //////////////////////add your processing here! + // /////////// put the actual processing here 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; diff --git a/AudioCalcGainWDRC_F32.h b/AudioCalcGainWDRC_F32.h index e08a798..8b744d0 100644 --- a/AudioCalcGainWDRC_F32.h +++ b/AudioCalcGainWDRC_F32.h @@ -17,17 +17,8 @@ #include //ARM DSP extensions. for speed! #include +#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 @@ -35,9 +26,10 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32 //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: shortName:calc_WDRCGain public: - //default constructor + //constructors 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 void update(void) { @@ -49,7 +41,7 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32 audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); if (!out_block) return; - // //////////////////////add your processing here! + // ////////////////////// do the processing here! calcGainFromEnvelope(in_block->data, out_block->data, in_block->length); 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(); 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 // apply wide-dynamic range compression @@ -80,48 +72,60 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32 //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 *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, - 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; int k; - float *pdb = env_dB; //just rename it to keep the code below unchanged - float tk_tmp = tk; + float *pdb = env_dB; //just rename it to keep the code below unchanged (input SPL dB) + float tk_tmp = tk; //temporary, threshold for start of compression (input SPL dB) - if ((tk_tmp + tkgn) > bolt) { - tk_tmp = bolt - tkgn; + if ((tk_tmp + tkgn) > bolt) { //after gain, would the compression threshold be above the output-limitting threshold ("bolt") + 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); - const float cr_const = ((1.0f / cr) - 1.0f); - for (k = 0; k < n; k++) { - if ((pdb[k] < tk_tmp) && (cr >= 1.0f)) { - gdb = tkgn; - } else if (pdb[k] > pblt) { - gdb = bolt + ((pdb[k] - pblt) / 10.0f) - pdb[k]; + + tkgo = tkgn + tk_tmp * (1.0f - 1.0f / cr); //intermediate calc + pblt = cr * (bolt - tkgo); //calc input level (dB) where we need to start limiting, no just compression + const float cr_const = ((1.0f / cr) - 1.0f); //pre-calc a constant that we'll need later + + for (k = 0; k < n; k++) { //loop over each sample + if ((pdb[k] < tk_tmp) && (cr >= 1.0f)) { //if below threshold and we're compressing + 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 { - gdb = cr_const * pdb[k] + tkgo; + gdb = cr_const * pdb[k] + tkgo; } gain_out[k] = undb2(gdb); //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) { - CHA_WDRC gha = {1.0f, // attack time (ms), IGNORED HERE - 50.0f, // release time (ms), IGNORED HERE - 24000.0f, // fs, sampling rate (Hz), IGNORED HERE - 119.0f, // maxdB, maximum signal (dB SPL) - 0.0f, // tkgain, compression-start gain - 105.0f, // tk, compression-start kneepoint - 10.0f, // cr, compression ratio - 105.0f // bolt, broadband output limiting threshold + void setDefaultValues(void) { //set as limiter + BTNRH_WDRC::CHA_WDRC gha = { + 5.0f, // attack time (ms) + 50.0f, // release time (ms) + 24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED! + 115.0f, // maxdB, maximum signal (dB SPL)...assumed SPL for full-scale input signal + 0.0f, // tkgain, compression-start gain (dB) + 55.0f, // tk, compression-start kneepoint (dB SPL) + 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_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 } void setParams(float _maxdB, float _tkgain, float _cr, float _tk, float _bolt) { @@ -131,7 +135,27 @@ class AudioCalcGainWDRC_F32 : public AudioStream_F32 cr = _cr; 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 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: audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module float maxdB, tkgn, tk, cr, bolt; + float last_gain = 1.0; //what was the last gain value computed for the signal }; #endif diff --git a/AudioConfigFIRFilterBank_F32.cpp b/AudioConfigFIRFilterBank_F32.cpp new file mode 100644 index 0000000..e20ae00 --- /dev/null +++ b/AudioConfigFIRFilterBank_F32.cpp @@ -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 +#include #define fmove(x,y,n) memmove(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: shortName:config_FIRbank public: - AudioConfigFIRFilterBank_F32(void) { - } - AudioConfigFIRFilterBank_F32(const int n_chan, const int n_fir, const float sample_rate_Hz, float *corner_freq, float *filter_coeff) { + 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) + { createFilterCoeff(n_chan, n_fir, sample_rate_Hz, corner_freq, filter_coeff); } @@ -51,7 +53,7 @@ class AudioConfigFIRFilterBank_F32 { flag__free_cf = 1; 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); if (flag__free_cf) free(cf); } @@ -84,71 +86,7 @@ class AudioConfigFIRFilterBank_F32 { return n; } - 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; ilength; 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); +} diff --git a/AudioControlTester.h b/AudioControlTester.h new file mode 100644 index 0000000..98d8e7b --- /dev/null +++ b/AudioControlTester.h @@ -0,0 +1,538 @@ + +#ifndef _AudioControlTester_h +#define _AudioControlTester_h + +//include +#include + + +#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= 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 diff --git a/AudioConvert_F32.h b/AudioConvert_F32.h index 7c8d80f..485e867 100644 --- a/AudioConvert_F32.h +++ b/AudioConvert_F32.h @@ -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 public: AudioConvert_I16toF32(void) : AudioStream_F32(1, inputQueueArray_f32) { }; + AudioConvert_I16toF32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { }; + void update(void) { //get the Int16 block audio_block_t *int_block; diff --git a/AudioEffectCompWDRC_F32.h b/AudioEffectCompWDRC_F32.h index ac2d204..0ee2a70 100644 --- a/AudioEffectCompWDRC_F32.h +++ b/AudioEffectCompWDRC_F32.h @@ -12,150 +12,21 @@ #ifndef _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 #include #include #include -#include "AudioCalcGainWDRC_F32.h" //has definition of CHA_WDRC -#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; iprint(cross_freq[i]); s->print(", ");}; s->println(); - s->print(" : tkgain = "); - for (int i=0; iprint(tkgain[i]); s->print(", ");}; s->println(); - s->print(" : cr = "); - for (int i=0; iprint(cr[i]); s->print(", ");}; s->println(); - s->print(" : tk = "); - for (int i=0; iprint(tk[i]); s->print(", ");}; s->println(); - s->print(" : bolt = "); - for (int i=0; iprint(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; +#include //has definition of CHA_WDRC +#include "BTNRH_WDRC_Types.h" +//#include "utility/textAndStringUtils.h" class AudioEffectCompWDRC_F32 : public AudioStream_F32 { //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node - //GUI: shortName: CompWDRC + //GUI: shortName: CompressWDRC public: AudioEffectCompWDRC_F32(void): AudioStream_F32(1,inputQueueArray) { //need to modify this for user to set sample rate setSampleRate_Hz(AUDIO_SAMPLE_RATE); @@ -178,7 +49,7 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 if (!out_block) return; //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 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 - void cha_agc_channel(float *input, float *output, int cs) { - //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); - compress(input, output, cs); - } + //void cha_agc_channel(float *input, float *output, int cs) { + // //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); + // compress(input, output, cs); + //} //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) @@ -222,23 +93,23 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 void setDefaultValues(void) { - //set default values...taken from CHAPRO, GHA_Demo.c from "amplify()"...ignores given sample rate - //assumes that the sample rate has already been set!!!! - CHA_WDRC gha = {1.0f, // attack time (ms) + //set default values...configure as limitter + BTNRH_WDRC::CHA_WDRC gha = { + 5.0f, // attack time (ms) 50.0f, // release time (ms) 24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED! - 119.0f, // maxdB, maximum signal (dB SPL) - 0.0f, // tkgain, compression-start gain - 105.0f, // tk, compression-start kneepoint - 10.0f, // cr, compression ratio - 105.0f // bolt, broadband output limiting threshold + 115.0f, // maxdB, maximum signal (dB SPL)...assumed SPL for full-scale input signal + 0.0f, // tkgain, compression-start gain (dB) + 55.0f, // tk, compression-start kneepoint (dB SPL) + 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_from_CHA_WDRC(&gha); } //set all of the parameters for the compressor using the CHA_WDRC structure //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! calcEnvelope.setAttackRelease_msec(gha->attack,gha->release); //these are in milliseconds @@ -262,9 +133,25 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 given_sample_rate_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 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; AudioCalcGainWDRC_F32 calcGain; diff --git a/AudioEffectCompressor_F32.h b/AudioEffectCompressor_F32.h index f31ddd6..f1be565 100644 --- a/AudioEffectCompressor_F32.h +++ b/AudioEffectCompressor_F32.h @@ -22,13 +22,20 @@ class AudioEffectCompressor_F32 : public AudioStream_F32 public: //constructor 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 setCompressionRatio(5.0f); //set the default copression ratio - setAttack_sec(0.005f, AUDIO_SAMPLE_RATE); //default to this value - setRelease_sec(0.200f, AUDIO_SAMPLE_RATE); //default to this value - setHPFilterCoeff(); enableHPFilter(true); //enable the HP filter to remove any DC offset from the audio - resetStates(); - }; + setAttack_sec(0.005f, sample_rate_Hz); //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 + } //here's the method that does all the work void update(void) { @@ -133,7 +140,7 @@ class AudioEffectCompressor_F32 : public AudioStream_F32 inst_targ_gain_dB_block->data, //this is the output 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" above_thresh_dB_block->data, //this is the vector to be subtracted inst_targ_gain_dB_block->data, //this is the output diff --git a/AudioEffectEmpty_F32.h b/AudioEffectEmpty_F32.h index 91db1f9..9878f48 100644 --- a/AudioEffectEmpty_F32.h +++ b/AudioEffectEmpty_F32.h @@ -23,6 +23,7 @@ class AudioEffectEmpty_F32 : public AudioStream_F32 public: //constructor 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 void update(void) { diff --git a/AudioEffectGain_F32.h b/AudioEffectGain_F32.h index 1ad3aa1..1183431 100644 --- a/AudioEffectGain_F32.h +++ b/AudioEffectGain_F32.h @@ -9,8 +9,8 @@ * MIT License. use at your own risk. */ -#ifndef _AudioEffectGain_h -#define _AudioEffectGain_h +#ifndef _AudioEffectGain_F32_h +#define _AudioEffectGain_F32_h #include //ARM DSP extensions. for speed! #include @@ -21,6 +21,7 @@ class AudioEffectGain_F32 : public AudioStream_F32 public: //constructor 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 void update(void) { diff --git a/AudioFilterBiquad_F32.cpp b/AudioFilterBiquad_F32.cpp new file mode 100644 index 0000000..44a6fea --- /dev/null +++ b/AudioFilterBiquad_F32.cpp @@ -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); +} diff --git a/AudioFilterBiquad_F32.h b/AudioFilterBiquad_F32.h new file mode 100644 index 0000000..2a2bd5c --- /dev/null +++ b/AudioFilterBiquad_F32.h @@ -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 + + diff --git a/AudioFilterFIR_F32.cpp b/AudioFilterFIR_F32.cpp new file mode 100644 index 0000000..4eea0d1 --- /dev/null +++ b/AudioFilterFIR_F32.cpp @@ -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); +} diff --git a/AudioFilterFIR_F32.h b/AudioFilterFIR_F32.h index a8408b2..97496f2 100644 --- a/AudioFilterFIR_F32.h +++ b/AudioFilterFIR_F32.h @@ -16,7 +16,6 @@ // Indicates that the code should just pass through the audio // without any filtering (as opposed to doing nothing at all) #define FIR_F32_PASSTHRU ((const float32_t *) 1) - #define FIR_MAX_COEFFS 200 class AudioFilterFIR_F32 : public AudioStream_F32 @@ -25,7 +24,9 @@ class AudioFilterFIR_F32 : public AudioStream_F32 public: AudioFilterFIR_F32(void): AudioStream_F32(1,inputQueueArray), 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 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, const int block_size) { //or, you can provide it with the block size @@ -36,8 +37,8 @@ class AudioFilterFIR_F32 : public AudioStream_F32 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); configured_block_size = block_size; - Serial.print("AudioFilterFIR_F32: FIR is initialized. N_FIR = "); Serial.print(n_coeffs); - Serial.print(", Block Size = "); Serial.println(block_size); + //Serial.print("AudioFilterFIR_F32: FIR is initialized. N_FIR = "); Serial.print(n_coeffs); + //Serial.print(", Block Size = "); Serial.println(block_size); //} else { // Serial.print("AudioFilterFIR_F32: *** ERROR ***: Cound not initialize. N_FIR = "); Serial.print(n_coeffs); // Serial.print(", Block Size = "); Serial.println(block_size); @@ -45,7 +46,7 @@ class AudioFilterFIR_F32 : public AudioStream_F32 } } 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 @@ -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 diff --git a/AudioMixer4_F32.cpp b/AudioMixer4_F32.cpp deleted file mode 100644 index 40e9757..0000000 --- a/AudioMixer4_F32.cpp +++ /dev/null @@ -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); - } -} diff --git a/AudioMixer4_F32.h b/AudioMixer4_F32.h deleted file mode 100644 index ac153a5..0000000 --- a/AudioMixer4_F32.h +++ /dev/null @@ -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 -#include - -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 \ No newline at end of file diff --git a/AudioMixer_F32.h b/AudioMixer_F32.h index 21b1720..bd3067a 100644 --- a/AudioMixer_F32.h +++ b/AudioMixer_F32.h @@ -24,10 +24,13 @@ class AudioMixer4_F32 : public AudioStream_F32 { //GUI: inputs:4, outputs:1 //this line used for automatic generation of GUI node //GUI: shortName:Mixer4 public: - AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) { - for (int i=0; i<4; i++) multiplier[i] = 1.0; - } - + AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) { setDefaultValues(); } + 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); void gain(unsigned int channel, float gain) { @@ -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: shortName:Mixer8 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; } diff --git a/AudioMultiply_F32.h b/AudioMultiply_F32.h index 2d4d7f7..3895673 100644 --- a/AudioMultiply_F32.h +++ b/AudioMultiply_F32.h @@ -20,6 +20,8 @@ class AudioMultiply_F32 : public AudioStream_F32 //GUI: inputs:2, outputs:1 //this line used for automatic generation of GUI node public: AudioMultiply_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {}; + AudioMultiply_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {}; + void update(void); private: diff --git a/AudioSettings_F32.cpp b/AudioSettings_F32.cpp new file mode 100644 index 0000000..ee1b9f4 --- /dev/null +++ b/AudioSettings_F32.cpp @@ -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; +} \ No newline at end of file diff --git a/AudioSettings_F32.h b/AudioSettings_F32.h new file mode 100644 index 0000000..7832af7 --- /dev/null +++ b/AudioSettings_F32.h @@ -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 \ No newline at end of file diff --git a/AudioStream_F32.cpp b/AudioStream_F32.cpp index 6519dd2..fdf8737 100644 --- a/AudioStream_F32.cpp +++ b/AudioStream_F32.cpp @@ -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_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 // placing them all onto the free list void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned int num) diff --git a/AudioStream_F32.h b/AudioStream_F32.h index feba657..4894187 100644 --- a/AudioStream_F32.h +++ b/AudioStream_F32.h @@ -15,37 +15,15 @@ #include //ARM DSP extensions. for speed! #include //Teensy Audio Library +#include "AudioSettings_F32.h" +// /////////////// class prototypes class AudioStream_F32; class AudioConnection_F32; -class 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) { //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; } -}; +// ///////////// class definitions //create a new structure to hold audio as floating point values. //modeled on the existing teensy audio block struct, which uses Int16 @@ -91,20 +69,6 @@ class AudioConnection_F32 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 { public: @@ -121,12 +85,12 @@ class AudioStream_F32 : public AudioStream { //virtual void update(audio_block_f32_t *) = 0; static uint8_t f32_memory_used; static uint8_t f32_memory_used_max; + static audio_block_f32_t * allocate_f32(void); + static void release(audio_block_f32_t * block); protected: //bool active_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); audio_block_f32_t * receiveReadOnly_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]; }; +/* +#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 \ No newline at end of file diff --git a/BTNRH_WDRC_Types.h b/BTNRH_WDRC_Types.h new file mode 100644 index 0000000..80e745c --- /dev/null +++ b/BTNRH_WDRC_Types.h @@ -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; iprint(cross_freq[i]); s->print(", ");}; s->println(); + s->print(" : tkgain = "); + for (int i=0; iprint(tkgain[i]); s->print(", ");}; s->println(); + s->print(" : cr = "); + for (int i=0; iprint(cr[i]); s->print(", ");}; s->println(); + s->print(" : tk = "); + for (int i=0; iprint(tk[i]); s->print(", ");}; s->println(); + s->print(" : bolt = "); + for (int i=0; iprint(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 \ No newline at end of file diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index 8120e24..b5af6e1 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -5,20 +5,23 @@ #include "AudioCalcEnvelope_F32.h" #include "AudioCalcGainWDRC_F32.h" #include "AudioConfigFIRFilterBank_F32.h" -#include +#include "AudioControlTester.h" +#include "AudioConvert_F32.h" +#include "AudioEffectCompressor_F32.h" #include "AudioEffectCompWDRC_F32.h" #include "AudioEffectEmpty_F32.h" -#include -#include +#include "AudioEffectGain_F32.h" +#include "AudioFilterBiquad_F32.h" #include #include -#include -#include +#include "AudioMixer_F32.h" +#include "AudioMultiply_F32.h" +#include "AudioSettings_F32.h" #include "input_i2s_f32.h" +#include "output_i2s_f32.h" #include "play_queue_f32.h" #include "record_queue_f32.h" #include "synth_pinknoise_f32.h" -#include -#include "synth_whitenoise_f32.h" #include "synth_sine_f32.h" -#include "output_i2s_f32.h" +#include "synth_waveform_F32.h" +#include "synth_whitenoise_f32.h" diff --git a/USB_Audio_F32.h b/USB_Audio_F32.h new file mode 100644 index 0000000..c734155 --- /dev/null +++ b/USB_Audio_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 +#include + +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 \ No newline at end of file diff --git a/control_tlv320aic3206.cpp b/control_tlv320aic3206.cpp index c4a36e4..8618268 100644 --- a/control_tlv320aic3206.cpp +++ b/control_tlv320aic3206.cpp @@ -142,14 +142,22 @@ bool AudioControlTLV320AIC3206::enable(void) Wire.begin(); 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_initADC(); delay(100); aic_initDAC(); delay(100); 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; @@ -174,7 +182,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) { // 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; } else if (n == TYMPAN_INPUT_JACK_AS_MIC) { // mic-jack = IN3 @@ -185,7 +193,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) { // BIAS on, using default 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; } else if (n == TYMPAN_INPUT_JACK_AS_LINEIN) { // 1 @@ -197,7 +205,7 @@ bool AudioControlTLV320AIC3206::inputSelect(int n) { // 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; } else if (n == TYMPAN_INPUT_ON_BOARD_MIC) { // 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); // 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; } - Serial.print("ERROR: Unable to Select Input - Value not supported: "); + Serial.print("controlTLV320AIC3206: ERROR: Unable to Select Input - Value not supported: "); Serial.println(n); 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 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); return false; } void AudioControlTLV320AIC3206::aic_reset() { - Serial.println("INFO: Reseting AIC"); + if (debugToSerial) Serial.println("INFO: Reseting AIC"); aic_writePage(0x00, 0x01, 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); 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_writePage(1, 61, 0); // 0x3D // Select ADC PTM_R4 Power Tune? 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) { if (volume < 0.0) { 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) { 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) int8_t volume_int = (int8_t) (round(volume)); // round - Serial.print(". Converted to volume map => "); - Serial.println(volume_int); + if (debugToSerial) { + 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_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 if (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) { 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) int8_t volume_int = (int8_t) (round(volume)); // round - Serial.print(". Converted to volume map => "); - Serial.println(volume_int); + if (debugToSerial) { + 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_LEFT_REG, volume_int); @@ -340,7 +350,7 @@ bool AudioControlTLV320AIC3206::volume_dB(float volume) { } void AudioControlTLV320AIC3206::aic_initDAC() { - Serial.println("INFO: Initializing AIC DAC"); + if (debugToSerial) Serial.println("INFO: Initializing AIC DAC"); // PLAYBACK SETUP aic_writeAddress(TYMPAN_DAC_PROCESSING_BLOCK_REG, PRB_P); // processing blocks - DAC @@ -363,8 +373,8 @@ void AudioControlTLV320AIC3206::aic_initDAC() { } void AudioControlTLV320AIC3206::aic_init() { - Serial.println("INFO: Initializing AIC"); - + if (debugToSerial) Serial.println("INFO: Initializing AIC"); + // PLL aic_writePage(0, 4, 3); // 0x04 low PLL clock range, MCLK is PLL input, PLL_OUT is CODEC_CLKIN aic_writePage(0, 5, (PLL_J != 0 ? 0x91 : 0x11)); @@ -407,7 +417,7 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg) Wire.write(reg); unsigned int result = Wire.endTransmission(); 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(". Received Error During Read Page: "); Serial.println(result); @@ -415,7 +425,7 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg) return val; } 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.println(". Nothing to return"); val = 400; @@ -423,16 +433,18 @@ unsigned int AudioControlTLV320AIC3206::aic_readPage(uint8_t page, uint8_t reg) } if (Wire.available() >= 1) { uint16_t val = Wire.read(); - Serial.print("INFO: Read Page. Page: ");Serial.print(page); - Serial.print(" Reg: ");Serial.print(reg); - Serial.print(". Received: "); - Serial.println(val, HEX); + if (debugToSerial) { + Serial.print("INFO: Read Page. Page: ");Serial.print(page); + Serial.print(" Reg: ");Serial.print(reg); + Serial.print(". Received: "); + Serial.println(val, HEX); + } return val; } } 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.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; 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) { - Serial.print("INFO: Write Page. Page: ");Serial.print(page); - Serial.print(" Reg: ");Serial.print(reg); - Serial.print(" Val: ");Serial.println(val); + if (debugToSerial) { + Serial.print("INFO: Write Page. Page: ");Serial.print(page); + Serial.print(" Reg: ");Serial.print(reg); + Serial.print(" Val: ");Serial.println(val); + } if (aic_goToPage(page)) { Wire.beginTransmission(AIC3206_I2C_ADDR); 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(); if (result == 0) return true; else { - Serial.print("ERROR: Received Error During Write Page: "); + Serial.print("controlTLV320AIC3206: Received Error During writePage(): Error = "); 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) { Wire.beginTransmission(AIC3206_I2C_ADDR); - Wire.write(0x00); delay(10);// page register - Wire.write(page); delay(10);// go to page + Wire.write(0x00); delay(10);// page register //was delay(10) from BPF + Wire.write(page); delay(10);// go to page //was delay(10) from BPF byte result = Wire.endTransmission(); if (result != 0) { - Serial.print("ERROR: Received Error During GoTo Page: "); + Serial.print("controlTLV320AIC3206: Received Error During goToPage(): Error = "); Serial.println(result); if (result == 2) { // failed to transmit address diff --git a/control_tlv320aic3206.h b/control_tlv320aic3206.h index ed6a1b5..a682829 100644 --- a/control_tlv320aic3206.h +++ b/control_tlv320aic3206.h @@ -16,7 +16,8 @@ class AudioControlTLV320AIC3206: public AudioControl { public: //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 disable(void); bool volume(float n); @@ -25,7 +26,7 @@ public: bool inputSelect(int n); bool setInputGain_dB(float n); bool setMicBias(int n); - + bool debugToSerial; private: void aic_reset(void); void aic_init(void); @@ -35,6 +36,7 @@ private: bool aic_writePage(uint8_t page, uint8_t reg, uint8_t val); bool aic_writeAddress(uint16_t address, uint8_t val); bool aic_goToPage(uint8_t page); + }; #define TYMPAN_OUTPUT_HEADPHONE_JACK_OUT 1 diff --git a/examples/BasicCompressor_Float/BasicCompressor_Float.ino b/examples/BasicCompressor_Float/BasicCompressor_Float.ino index 02f267e..9af34dc 100644 --- a/examples/BasicCompressor_Float/BasicCompressor_Float.ino +++ b/examples/BasicCompressor_Float/BasicCompressor_Float.ino @@ -23,10 +23,10 @@ //create audio library objects for handling the audio AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board 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_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h 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 //note that you ALWAYS have to have an I2S connection (either in or out) to have a clock diff --git a/examples/BasicGain_Float/BasicGain_Float.ino b/examples/BasicGain_Float/BasicGain_Float.ino index 402ef7d..2a80a60 100644 --- a/examples/BasicGain_Float/BasicGain_Float.ino +++ b/examples/BasicGain_Float/BasicGain_Float.ino @@ -24,10 +24,10 @@ //create audio library objects for handling the audio AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board 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 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 +AudioOutputI2S i2s_out; //Digital audio *to* the Teensy Audio Board DAC. Expects Int16. Stereo //Make all of the audio connections AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter diff --git a/examples/MixStereoToMono_Float/MixStereoToMono_Float.ino b/examples/MixStereoToMono_Float/MixStereoToMono_Float.ino index 80453a3..2dd41c0 100644 --- a/examples/MixStereoToMono_Float/MixStereoToMono_Float.ino +++ b/examples/MixStereoToMono_Float/MixStereoToMono_Float.ino @@ -23,13 +23,13 @@ //create audio library objects for handling the audio AudioControlSGTL5000_Extended sgtl5000; //controller for the Teensy Audio Board 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_F32toI16 float2Int1, float2Int2; //Converts Float to Int16. See class in AudioStream_F32.h 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? -#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 AudioInputUSB usb_in; AudioConnection patchCord1(usb_in, 0, int2Float1, 0); diff --git a/examples/MyAudioEffect_Float/MyAudioEffect_Float.ino b/examples/MyAudioEffect_Float/MyAudioEffect_Float.ino index 861a602..68a58c2 100644 --- a/examples/MyAudioEffect_Float/MyAudioEffect_Float.ino +++ b/examples/MyAudioEffect_Float/MyAudioEffect_Float.ino @@ -24,10 +24,10 @@ //create audio library objects for handling the audio AudioControlSGTL5000 sgtl5000_1; //controller for the Teensy Audio Board 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 -AudioEffectMine_F32 effect1, effect2; //This is your own algorithms 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 AudioConnection patchCord1(i2s_in, 0, int2Float1, 0); //connect the Left input to the Left Int->Float converter diff --git a/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino b/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino index b3626f3..519fb09 100644 --- a/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino +++ b/examples/OscillatorWithPitchmod_Float/OscillatorWithPitchmod_Float.ino @@ -19,15 +19,15 @@ #include //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 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 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 patchCord2(lfo1, 0, osc1, 0); // connect the output of the lfo to the mod input of the oscillator diff --git a/output_i2s_f32.cpp b/output_i2s_f32.cpp index 1ff85f2..da57e6b 100644 --- a/output_i2s_f32.cpp +++ b/output_i2s_f32.cpp @@ -41,7 +41,7 @@ float setI2SFreq(const float freq_Hz) { } __attribute__((__packed__)) tmclk; 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) 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} }; diff --git a/play_queue_f32.h b/play_queue_f32.h index 0cf9212..80f8d4c 100644 --- a/play_queue_f32.h +++ b/play_queue_f32.h @@ -19,6 +19,8 @@ class AudioPlayQueue_F32 : public AudioStream_F32 public: AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL), 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(const int16_t *data, uint32_t len); //void play(float32_t data); diff --git a/record_queue_f32.h b/record_queue_f32.h index ad39d04..9d13940 100644 --- a/record_queue_f32.h +++ b/record_queue_f32.h @@ -20,6 +20,8 @@ class AudioRecordQueue_F32 : public AudioStream_F32 public: AudioRecordQueue_F32(void) : AudioStream_F32(1, inputQueueArray), 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) { clear(); enabled = 1; diff --git a/synth_pinknoise_f32.h b/synth_pinknoise_f32.h index d010abb..0a9d624 100644 --- a/synth_pinknoise_f32.h +++ b/synth_pinknoise_f32.h @@ -44,13 +44,20 @@ class AudioSynthNoisePink_F32 : public AudioStream_F32 //GUI: shortName:pinknoise //this line used for automatic generation of GUI node public: 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++; paccu = 0; pncnt = 0; pinc = 0x0CCC; pdec = 0x0CCC; - - enabled = 1; } void amplitude(float n) { if (n < 0.0) n = 0.0; diff --git a/synth_sine_f32.cpp b/synth_sine_f32.cpp index 0dd0555..6e7c2e1 100644 --- a/synth_sine_f32.cpp +++ b/synth_sine_f32.cpp @@ -47,7 +47,7 @@ void AudioSynthWaveformSine_F32::update(void) #endif 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; diff --git a/synth_whitenoise_f32.h b/synth_whitenoise_f32.h index 2f6fa36..28a6b8b 100644 --- a/synth_whitenoise_f32.h +++ b/synth_whitenoise_f32.h @@ -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: shortName:whitenoise //this line used for automatic generation of GUI node 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; seed = 1 + instance_count++; } diff --git a/utility/BTNRH_rfft.cpp b/utility/BTNRH_rfft.cpp new file mode 100644 index 0000000..5cc18cb --- /dev/null +++ b/utility/BTNRH_rfft.cpp @@ -0,0 +1,393 @@ + +//ifndef _BTNRH_FFT_H +//define _BTNRH_FFT_H + +#include "BTNRH_rfft.h" +#include +//#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 \ No newline at end of file diff --git a/utility/BTNRH_rfft.h b/utility/BTNRH_rfft.h new file mode 100644 index 0000000..0ed5023 --- /dev/null +++ b/utility/BTNRH_rfft.h @@ -0,0 +1,19 @@ + +#ifndef _BTNRH_FFT_H +#define _BTNRH_FFT_H + +#include +//#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 \ No newline at end of file diff --git a/utility/rfft.c b/utility/rfft.c deleted file mode 100644 index df59701..0000000 --- a/utility/rfft.c +++ /dev/null @@ -1,384 +0,0 @@ - -#include -//#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; - } -} -