parent
d46ab56ed7
commit
81492cc5ac
@ -0,0 +1,82 @@ |
|||||||
|
/*
|
||||||
|
* fir_filterbank.cpp |
||||||
|
*
|
||||||
|
* Created: Chip Audette, Creare LLC, Feb 2017 |
||||||
|
* Primarly built upon CHAPRO "Generic Hearing Aid" from
|
||||||
|
* Boys Town National Research Hospital (BTNRH): https://github.com/BTNRH/chapro
|
||||||
|
*
|
||||||
|
* License: MIT License. Use at your own risk. |
||||||
|
*
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "AudioConfigFIRFilterBank_F32.h" |
||||||
|
#include "utility/BTNRH_rfft.h" |
||||||
|
|
||||||
|
void AudioConfigFIRFilterBank_F32::fir_filterbank(float *bb, float *cf, const int nc, const int nw_orig, const int wt, const float sr) |
||||||
|
{ |
||||||
|
double p, w, a = 0.16, sm = 0; |
||||||
|
float *ww, *bk, *xx, *yy; |
||||||
|
int j, k, kk, nt, nf, ns, *be; |
||||||
|
|
||||||
|
int nw = nextPowerOfTwo(nw_orig); |
||||||
|
//Serial.print("AudioConfigFIRFilterBank: fir_filterbank: nw_orig = "); Serial.print(nw_orig);
|
||||||
|
//Serial.print(", nw = "); Serial.println(nw);
|
||||||
|
|
||||||
|
nt = nw * 2; //we're going to do an fft that's twice as long (zero padded)
|
||||||
|
nf = nw + 1; //number of bins to nyquist in the zero-padded FFT. Also nf = nt/2+1
|
||||||
|
ns = nf * 2; |
||||||
|
be = (int *) calloc(nc + 1, sizeof(int)); |
||||||
|
ww = (float *) calloc(nw, sizeof(float)); |
||||||
|
xx = (float *) calloc(ns, sizeof(float)); |
||||||
|
yy = (float *) calloc(ns, sizeof(float)); |
||||||
|
|
||||||
|
// window
|
||||||
|
for (j = 0; j < nw; j++) ww[j]=0.0f; //clear
|
||||||
|
for (j = 0; j < nw_orig; j++) { |
||||||
|
p = M_PI * (2.0 * j - nw_orig) / nw_orig; |
||||||
|
if (wt == 0) { |
||||||
|
w = 0.54 + 0.46 * cos(p); // Hamming
|
||||||
|
} else if (wt==1) { |
||||||
|
w = (1 - a + cos(p) + a * cos(2 * p)) / 2; // Blackman
|
||||||
|
} else { |
||||||
|
//win = (1 - cos(2*pi*[1:N]/(N+1)))/2; //WEA's matlab call, indexing starts from 1, not zero
|
||||||
|
w = (1.0 - cosf(2.0*M_PI*((float)(j))/((float)(nw_orig-1))))/2.0;
|
||||||
|
} |
||||||
|
sm += w; |
||||||
|
ww[j] = (float) w; |
||||||
|
} |
||||||
|
|
||||||
|
// frequency bands...add the DC-facing band and add the Nyquist-facing band
|
||||||
|
be[0] = 0; |
||||||
|
for (k = 1; k < nc; k++) { |
||||||
|
kk = round(nf * cf[k - 1] * (2 / sr)); //original
|
||||||
|
be[k] = (kk > nf) ? nf : kk; |
||||||
|
} |
||||||
|
be[nc] = nf; |
||||||
|
|
||||||
|
// channel tranfer functions
|
||||||
|
fzero(xx, ns); |
||||||
|
xx[nw_orig / 2] = 1; //make a single-sample impulse centered on our eventual window
|
||||||
|
BTNRH_FFT::cha_fft_rc(xx, nt); |
||||||
|
for (k = 0; k < nc; k++) { |
||||||
|
fzero(yy, ns); //zero the temporary output
|
||||||
|
//int nbins = (be[k + 1] - be[k]) * 2; Serial.print("fir_filterbank: chan ");Serial.print(k); Serial.print(", nbins = ");Serial.println(nbins);
|
||||||
|
fcopy(yy + be[k] * 2, xx + be[k] * 2, (be[k + 1] - be[k]) * 2); //copy just our passband
|
||||||
|
BTNRH_FFT::cha_fft_cr(yy, nt); //IFFT back into the time domain
|
||||||
|
|
||||||
|
// apply window to iFFT of bandpass
|
||||||
|
for (j = 0; j < nw; j++) { |
||||||
|
yy[j] *= ww[j]; |
||||||
|
} |
||||||
|
|
||||||
|
bk = bb + k * nw_orig; //pointer to location in output array
|
||||||
|
fcopy(bk, yy, nw_orig); //copy the filter coefficients to the output array
|
||||||
|
|
||||||
|
//print out the coefficients
|
||||||
|
//for (int i=0; i<nw; i++) { Serial.print(yy[i]*1000.0f);Serial.print(" "); }; Serial.println();
|
||||||
|
} |
||||||
|
free(be); |
||||||
|
free(ww); |
||||||
|
free(xx); |
||||||
|
free(yy); |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
|
||||||
|
#include "AudioControlTester.h" |
||||||
|
|
||||||
|
void AudioTestSignalGenerator_F32::update(void) { |
||||||
|
//receive the input audio data
|
||||||
|
audio_block_f32_t *in_block = AudioStream_F32::receiveReadOnly_f32(); |
||||||
|
if (!in_block) return; |
||||||
|
|
||||||
|
//if we're not testing, just transmit the block
|
||||||
|
if (!is_testing) { |
||||||
|
AudioStream_F32::transmit(in_block); // send the FIR output
|
||||||
|
AudioStream_F32::release(in_block); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//if we are testing, we're going to substitute a new signal,
|
||||||
|
//so we can release the block for the original signal
|
||||||
|
AudioStream_F32::release(in_block); |
||||||
|
|
||||||
|
//allocate memory for block that we'll send
|
||||||
|
audio_block_f32_t *out_block = allocate_f32(); |
||||||
|
if (!out_block) return; |
||||||
|
|
||||||
|
//now generate the new siginal
|
||||||
|
sine_gen.begin(); record_queue.begin(); //activate
|
||||||
|
sine_gen.update(); sine_gen.end(); |
||||||
|
gain_alg.update(); |
||||||
|
record_queue.update(); record_queue.end(); |
||||||
|
audio_block_f32_t *queue_block = record_queue.getAudioBlock(); |
||||||
|
|
||||||
|
//copy to out_block (why can't I just send the queue_block? I tried. It doesn't seem to work. try it again later!
|
||||||
|
for (int i=0; i < queue_block->length; i++) out_block->data[i] = queue_block->data[i]; |
||||||
|
record_queue.freeAudioBlock(); |
||||||
|
|
||||||
|
//send the data
|
||||||
|
AudioStream_F32::transmit(out_block); // send the FIR output
|
||||||
|
AudioStream_F32::release(out_block); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void AudioTestSignalMeasurement_F32::update(void) { |
||||||
|
|
||||||
|
//if we're not testing, just return
|
||||||
|
if (!is_testing) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//receive the input audio data...the baseline and the test
|
||||||
|
audio_block_f32_t *in_block_baseline = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
if (!in_block_baseline) return; |
||||||
|
audio_block_f32_t *in_block_test = AudioStream_F32::receiveReadOnly_f32(1); |
||||||
|
if (!in_block_test) { |
||||||
|
AudioStream_F32::release(in_block_baseline); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//compute the rms of both signals
|
||||||
|
float baseline_rms = computeRMS(in_block_baseline->data, in_block_baseline->length); |
||||||
|
float test_rms = computeRMS(in_block_test->data, in_block_test->length); |
||||||
|
|
||||||
|
//Release memory
|
||||||
|
AudioStream_F32::release(in_block_baseline); |
||||||
|
AudioStream_F32::release(in_block_test); |
||||||
|
|
||||||
|
//notify controller
|
||||||
|
if (testController != NULL) testController->transferRMSValues(baseline_rms, test_rms); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioTestSignalMeasurementMulti_F32::update(void) { |
||||||
|
|
||||||
|
//if we're not testing, just return
|
||||||
|
if (!is_testing) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//receive the input audio data...the baseline and the test
|
||||||
|
audio_block_f32_t *in_block_baseline = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
if (in_block_baseline==NULL) return; |
||||||
|
float baseline_rms = computeRMS(in_block_baseline->data, in_block_baseline->length); |
||||||
|
AudioStream_F32::release(in_block_baseline); |
||||||
|
|
||||||
|
//loop over each of the test data connections
|
||||||
|
float test_rms[num_test_values]; |
||||||
|
int n_with_data = 0; |
||||||
|
for (int Ichan=0; Ichan < num_test_values; Ichan++) { |
||||||
|
audio_block_f32_t *in_block_test = AudioStream_F32::receiveReadOnly_f32(1+Ichan); |
||||||
|
if (in_block_test==NULL) { |
||||||
|
//no data
|
||||||
|
test_rms[Ichan]=0.0f; |
||||||
|
} else { |
||||||
|
//process data
|
||||||
|
n_with_data = Ichan+1; |
||||||
|
test_rms[Ichan]=computeRMS(in_block_test->data, in_block_test->length); |
||||||
|
AudioStream_F32::release(in_block_test); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//notify controller
|
||||||
|
if (testController != NULL) testController->transferRMSValues(baseline_rms, test_rms, n_with_data); |
||||||
|
} |
@ -0,0 +1,538 @@ |
|||||||
|
|
||||||
|
#ifndef _AudioControlTester_h |
||||||
|
#define _AudioControlTester_h |
||||||
|
|
||||||
|
//include <Tympan_Library.h>
|
||||||
|
#include <OpenAudio_ArduinoLibrary.h> |
||||||
|
|
||||||
|
|
||||||
|
#define max_steps 64 |
||||||
|
#define max_num_chan 16 //max number of test signal inputs to the AudioTestSignalMeasurementMulti_F32
|
||||||
|
|
||||||
|
//prototypes
|
||||||
|
class AudioTestSignalGenerator_F32; |
||||||
|
class AudioTestSignalMeasurementInterface_F32; |
||||||
|
class AudioTestSignalMeasurement_F32; |
||||||
|
class AudioTestSignalMeasurementMulti_F32; |
||||||
|
class AudioControlSignalTesterInterface_F32; |
||||||
|
class AudioControlSignalTester_F32; |
||||||
|
class AudioControlTestAmpSweep_F32; |
||||||
|
class AudioControlTestFreqSweep_F32; |
||||||
|
|
||||||
|
// class definitions
|
||||||
|
class AudioTestSignalGenerator_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: testSignGen
|
||||||
|
public: |
||||||
|
AudioTestSignalGenerator_F32(void): AudioStream_F32(1,inputQueueArray) { |
||||||
|
setSampleRate_Hz(AUDIO_SAMPLE_RATE); |
||||||
|
setDefaultValues(); |
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
AudioTestSignalGenerator_F32(const AudioSettings_F32 &settings): AudioStream_F32(1,inputQueueArray) { |
||||||
|
setAudioSettings(settings); |
||||||
|
setDefaultValues(); |
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
~AudioTestSignalGenerator_F32(void) { |
||||||
|
if (patchCord1 != NULL) delete patchCord1; |
||||||
|
} |
||||||
|
void setAudioSettings(const AudioSettings_F32 &settings) { |
||||||
|
setSampleRate_Hz(settings.sample_rate_Hz); |
||||||
|
} |
||||||
|
void setSampleRate_Hz(const float _fs_Hz) { |
||||||
|
//pass this data on to its components that care
|
||||||
|
sine_gen.setSampleRate_Hz(_fs_Hz); |
||||||
|
} |
||||||
|
void makeConnections(void) { |
||||||
|
patchCord1 = new AudioConnection_F32(sine_gen, 0, gain_alg, 0); |
||||||
|
patchCord2 = new AudioConnection_F32(gain_alg, 0, record_queue, 0); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void update(void); |
||||||
|
void begin(void) { |
||||||
|
is_testing = true; |
||||||
|
//if (Serial) Serial.println("AudioTestSignalGenerator_F32: begin(): ...");
|
||||||
|
} |
||||||
|
void end(void) { is_testing = false; } |
||||||
|
|
||||||
|
AudioSynthWaveformSine_F32 sine_gen; |
||||||
|
AudioEffectGain_F32 gain_alg; |
||||||
|
AudioRecordQueue_F32 record_queue; |
||||||
|
AudioConnection_F32 *patchCord1; |
||||||
|
AudioConnection_F32 *patchCord2; |
||||||
|
|
||||||
|
void amplitude(float val) { |
||||||
|
sine_gen.amplitude(1.0); |
||||||
|
gain_alg.setGain(val); |
||||||
|
} |
||||||
|
void frequency(float val) { |
||||||
|
sine_gen.frequency(val); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setSignalAmplitude_dBFS(float val_dBFS) { |
||||||
|
amplitude(sqrtf(2.0)*sqrtf(powf(10.0f,0.1*val_dBFS))); |
||||||
|
}; |
||||||
|
virtual void setSignalFrequency_Hz(float val_Hz) { |
||||||
|
frequency(val_Hz); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
bool is_testing = false; |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
|
||||||
|
void setDefaultValues(void) { |
||||||
|
sine_gen.end(); //disable it for now
|
||||||
|
record_queue.end(); //disable it for now;
|
||||||
|
is_testing = false; |
||||||
|
frequency(1000.f); |
||||||
|
amplitude(0.0f); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||||
|
class AudioTestSignalMeasurementInterface_F32 { |
||||||
|
public: |
||||||
|
AudioTestSignalMeasurementInterface_F32 (void) {}; |
||||||
|
|
||||||
|
void setAudioSettings(const AudioSettings_F32 &settings) { |
||||||
|
setSampleRate_Hz(settings.sample_rate_Hz); |
||||||
|
} |
||||||
|
void setSampleRate_Hz(const float _fs_Hz) { |
||||||
|
//pass this data on to its components that care. None care right now.
|
||||||
|
} |
||||||
|
virtual void update(void); |
||||||
|
virtual float computeRMS(float data[], int n) { |
||||||
|
float rms_value; |
||||||
|
arm_rms_f32 (data, n, &rms_value); |
||||||
|
return rms_value; |
||||||
|
} |
||||||
|
virtual void begin(AudioControlSignalTester_F32 *p_controller) { |
||||||
|
//if (Serial) Serial.println("AudioTestSignalMeasurement_F32: begin(): ...");
|
||||||
|
testController = p_controller; |
||||||
|
is_testing = true; |
||||||
|
} |
||||||
|
virtual void end(void) { |
||||||
|
//if (Serial) Serial.println("AudioTestSignalMeasurement_F32: end(): ...");
|
||||||
|
testController = NULL; |
||||||
|
is_testing = false; |
||||||
|
} |
||||||
|
protected: |
||||||
|
bool is_testing = false; |
||||||
|
//audio_block_f32_t *inputQueueArray[2];
|
||||||
|
AudioControlSignalTester_F32 *testController = NULL; |
||||||
|
|
||||||
|
virtual void setDefaultValues(void) { |
||||||
|
is_testing = false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class AudioTestSignalMeasurement_F32 : public AudioStream_F32, public AudioTestSignalMeasurementInterface_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: testSigMeas
|
||||||
|
public: |
||||||
|
AudioTestSignalMeasurement_F32(void): AudioStream_F32(2,inputQueueArray) { |
||||||
|
setSampleRate_Hz(AUDIO_SAMPLE_RATE); |
||||||
|
setDefaultValues(); |
||||||
|
} |
||||||
|
AudioTestSignalMeasurement_F32(const AudioSettings_F32 &settings): AudioStream_F32(2,inputQueueArray) { |
||||||
|
setAudioSettings(settings); |
||||||
|
setDefaultValues(); |
||||||
|
} |
||||||
|
void update(void); |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[2]; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class AudioTestSignalMeasurementMulti_F32 : public AudioStream_F32, public AudioTestSignalMeasurementInterface_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:10, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: testSigMeas
|
||||||
|
public: |
||||||
|
AudioTestSignalMeasurementMulti_F32(void): AudioStream_F32(max_num_chan+1,inputQueueArray) { |
||||||
|
setSampleRate_Hz(AUDIO_SAMPLE_RATE); |
||||||
|
setDefaultValues(); |
||||||
|
} |
||||||
|
AudioTestSignalMeasurementMulti_F32(const AudioSettings_F32 &settings): AudioStream_F32(max_num_chan+1,inputQueueArray) { |
||||||
|
setAudioSettings(settings); |
||||||
|
setDefaultValues(); |
||||||
|
} |
||||||
|
void update(void); |
||||||
|
|
||||||
|
private: |
||||||
|
//int num_input_connections = max_num_chan+1;
|
||||||
|
int num_test_values = max_num_chan; |
||||||
|
audio_block_f32_t *inputQueueArray[max_num_chan+1]; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// ///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
class AudioControlSignalTesterInterface_F32 { |
||||||
|
public: |
||||||
|
AudioControlSignalTesterInterface_F32(void) {}; |
||||||
|
//virtual void setAudioBlockSamples(void) = 0;
|
||||||
|
//virtual void setSampleRate_hz(void) = 0;
|
||||||
|
virtual void begin(void) = 0; |
||||||
|
virtual void end(void) = 0; |
||||||
|
virtual void setStepPattern(float, float, float) = 0; |
||||||
|
virtual void transferRMSValues(float, float) = 0; |
||||||
|
virtual void transferRMSValues(float, float *, int) = 0; |
||||||
|
virtual bool available(void) = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class AudioControlSignalTester_F32 : public AudioControlSignalTesterInterface_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: sigTest(Abstract)
|
||||||
|
public:
|
||||||
|
AudioControlSignalTester_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
|
||||||
|
: AudioControlSignalTesterInterface_F32(), sig_gen(_sig_gen), sig_meas(_sig_meas) { |
||||||
|
|
||||||
|
setAudioBlockSamples(settings.audio_block_samples); |
||||||
|
setSampleRate_Hz(settings.sample_rate_Hz); |
||||||
|
resetState(); |
||||||
|
} |
||||||
|
virtual void begin(void) { |
||||||
|
Serial.println("AudioControlSignalTester_F32: begin(): ..."); |
||||||
|
recomputeTargetCountsPerStep(); //not needed, just to print some debugging messages
|
||||||
|
|
||||||
|
//activate the instrumentation
|
||||||
|
sig_gen.begin(); |
||||||
|
sig_meas.begin(this); |
||||||
|
|
||||||
|
//start the test
|
||||||
|
resetState(); |
||||||
|
gotoNextStep(); |
||||||
|
} |
||||||
|
|
||||||
|
//use this to cancel the test
|
||||||
|
virtual void end(void) { |
||||||
|
finishTest(); |
||||||
|
} |
||||||
|
|
||||||
|
void setAudioSettings(AudioSettings_F32 audio_settings) { |
||||||
|
setAudioBlockSamples(audio_settings.audio_block_samples); |
||||||
|
setSampleRate_Hz(audio_settings.sample_rate_Hz); |
||||||
|
} |
||||||
|
void setAudioBlockSamples(int block_samples) { |
||||||
|
audio_block_samples = block_samples; |
||||||
|
recomputeTargetCountsPerStep(); |
||||||
|
} |
||||||
|
void setSampleRate_Hz(float fs_Hz) { |
||||||
|
sample_rate_Hz = fs_Hz; |
||||||
|
recomputeTargetCountsPerStep(); |
||||||
|
} |
||||||
|
|
||||||
|
//define how long (seconds) to spend at each step of the test
|
||||||
|
void setTargetDurPerStep_sec(float sec) { |
||||||
|
if (sec > 0.001) { |
||||||
|
target_dur_per_step_sec = sec; |
||||||
|
recomputeTargetCountsPerStep(); |
||||||
|
} else { |
||||||
|
Serial.print(F("AudioControlSignalTester_F32: setTargetDurPerStep_sec: given duration too short: ")); |
||||||
|
Serial.print(target_dur_per_step_sec); |
||||||
|
Serial.print(F(". Ignoring...")); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
virtual void setStepPattern(float _start_val, float _end_val, float _step_val) { |
||||||
|
start_val = _start_val; end_val = _end_val; step_val = _step_val; |
||||||
|
recomputeTargetNumberOfSteps(); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void transferRMSValues(float baseline_rms, float test_rms) { |
||||||
|
transferRMSValues(baseline_rms, &test_rms, 1); |
||||||
|
} |
||||||
|
virtual void transferRMSValues(float baseline_rms, float *test_rms, int num_chan) { |
||||||
|
if (counter_ignore > 0) { |
||||||
|
//ignore this reading
|
||||||
|
counter_ignore--; |
||||||
|
return; |
||||||
|
} |
||||||
|
given_num_chan = num_chan; |
||||||
|
if (given_num_chan > max_num_chan) { |
||||||
|
Serial.println(F("AudioControlSignalTester_F32: transferRMSValues: *** ERROR ***")); |
||||||
|
Serial.print(F(" : num_chan (")); Serial.print(num_chan); Serial.print(")"); |
||||||
|
Serial.print(F(" is bigger max_num_chan (")); Serial.println(max_num_chan); |
||||||
|
Serial.println(F(" : Skipping...")); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
//add this number
|
||||||
|
sum_sig_pow_baseline[counter_step] += (baseline_rms*baseline_rms); |
||||||
|
for (int Ichan=0; Ichan < num_chan; Ichan++) { |
||||||
|
sum_sig_pow_test[counter_step][Ichan] += (test_rms[Ichan]*test_rms[Ichan]); |
||||||
|
} |
||||||
|
freq_at_each_step_Hz[counter_step] = signal_frequency_Hz; |
||||||
|
counter_sum[counter_step]++; |
||||||
|
|
||||||
|
//have all the channels checked in?
|
||||||
|
if (counter_sum[counter_step] >= target_counts_per_step) { |
||||||
|
gotoNextStep(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setSignalFrequency_Hz(float freq_Hz) { |
||||||
|
signal_frequency_Hz = freq_Hz; |
||||||
|
sig_gen.setSignalFrequency_Hz(signal_frequency_Hz); |
||||||
|
} |
||||||
|
virtual void setSignalAmplitude_dBFS(float amp_dBFS) { |
||||||
|
signal_amplitude_dBFS = amp_dBFS; |
||||||
|
sig_gen.setSignalAmplitude_dBFS(amp_dBFS); |
||||||
|
} |
||||||
|
virtual void printTableOfResults(Stream *s) { |
||||||
|
float ave1_dBFS, ave2_dBFS, ave3_dBFS, gain_dB, total_pow, total_wav, foo_pow; |
||||||
|
s->println(" : Freq (Hz), Input (dBFS), Per-Chan Output (dBFS), Total Gain (inc) (dB), Total Gain (coh) (dB)"); |
||||||
|
//s->print(" : given_num_chan = ");s->println(given_num_chan);
|
||||||
|
for (int i=0; i < target_n_steps; i++) { |
||||||
|
ave1_dBFS = 10.f*log10f(sum_sig_pow_baseline[i]/counter_sum[i]); |
||||||
|
s->print(" "); s->print(freq_at_each_step_Hz[i],0); |
||||||
|
s->print(", "); s->print(ave1_dBFS,1); |
||||||
|
|
||||||
|
total_pow = 0.0f; |
||||||
|
total_wav = 0.0f; |
||||||
|
for (int Ichan=0; Ichan < given_num_chan; Ichan++) { |
||||||
|
if (Ichan==0) { |
||||||
|
s->print(", "); |
||||||
|
} else { |
||||||
|
s->print(", "); |
||||||
|
} |
||||||
|
foo_pow = sum_sig_pow_test[i][Ichan]/counter_sum[i]; |
||||||
|
ave2_dBFS = 10.f*log10f(foo_pow); |
||||||
|
s->print(ave2_dBFS,1); |
||||||
|
|
||||||
|
total_pow += foo_pow; //sum as if it's noise being recombined incoherently
|
||||||
|
total_wav += sqrtf(foo_pow); //sum as it it's a in-phase tone being combined coherently
|
||||||
|
} |
||||||
|
ave2_dBFS = 10.f*log10f(total_pow); |
||||||
|
gain_dB = ave2_dBFS - ave1_dBFS; |
||||||
|
s->print(", "); s->print(gain_dB,2); |
||||||
|
|
||||||
|
ave3_dBFS = 20.f*log10f(total_wav); |
||||||
|
gain_dB = ave3_dBFS - ave1_dBFS; |
||||||
|
s->print(", "); s->println(gain_dB,2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool isDataAvailable = false; |
||||||
|
bool available(void) { return isDataAvailable; } |
||||||
|
|
||||||
|
protected: |
||||||
|
AudioTestSignalGenerator_F32 &sig_gen; |
||||||
|
AudioTestSignalMeasurementInterface_F32 &sig_meas; |
||||||
|
float signal_frequency_Hz = 1000.f; |
||||||
|
float signal_amplitude_dBFS = -50.0f; |
||||||
|
int counter_ignore = 0; |
||||||
|
//bool is_testing = 0;
|
||||||
|
|
||||||
|
int audio_block_samples = AUDIO_BLOCK_SAMPLES; |
||||||
|
float sample_rate_Hz = AUDIO_SAMPLE_RATE_EXACT; |
||||||
|
float target_dur_per_step_sec = 0.2; |
||||||
|
int target_counts_per_step = 1; |
||||||
|
|
||||||
|
//const int max_steps = 64;
|
||||||
|
float start_val = 0, end_val = 1.f, step_val = 1.f; |
||||||
|
int target_n_steps = 1; |
||||||
|
int given_num_chan = max_num_chan; |
||||||
|
|
||||||
|
float sum_sig_pow_baseline[max_steps]; |
||||||
|
float sum_sig_pow_test[max_steps][max_num_chan]; |
||||||
|
float freq_at_each_step_Hz[max_steps]; |
||||||
|
int counter_sum[max_steps], counter_step=-1; |
||||||
|
|
||||||
|
int recomputeTargetCountsPerStep(void) { |
||||||
|
target_counts_per_step = max(1,(int)((target_dur_per_step_sec * sample_rate_Hz / ((float)audio_block_samples))+0.5)); //round
|
||||||
|
// if (Serial) {
|
||||||
|
// Serial.println("AudioControlSignalTester_F32: recomputeTargetCountsPerStep: ");
|
||||||
|
// Serial.print(" : target_dur_per_step_sec = "); Serial.println(target_dur_per_step_sec);
|
||||||
|
// Serial.print(" : sample_rate_Hz = "); Serial.println(sample_rate_Hz);
|
||||||
|
// Serial.print(" : audio_block_samples = "); Serial.println(audio_block_samples);
|
||||||
|
// Serial.print(" : target_counts_per_step = "); Serial.println(target_counts_per_step);
|
||||||
|
// }
|
||||||
|
return target_counts_per_step;
|
||||||
|
} |
||||||
|
virtual int recomputeTargetNumberOfSteps(void) { |
||||||
|
return target_n_steps = (int)((end_val - start_val)/step_val + 0.5)+1; //round
|
||||||
|
} |
||||||
|
|
||||||
|
virtual void resetState(void) { |
||||||
|
isDataAvailable = false; |
||||||
|
for (int i=0; i<max_steps; i++) { |
||||||
|
sum_sig_pow_baseline[i]=0.0f; |
||||||
|
for (int Ichan=0; Ichan<max_num_chan; Ichan++) sum_sig_pow_test[i][Ichan]=0.0f; |
||||||
|
counter_sum[i] = 0; |
||||||
|
} |
||||||
|
counter_step = -1; |
||||||
|
} |
||||||
|
virtual void gotoNextStep(void) { |
||||||
|
counter_step++; |
||||||
|
//Serial.print("AudioControlSignalTester_F32: gotoNextStep: starting step ");
|
||||||
|
//Serial.println(counter_step);
|
||||||
|
if (counter_step >= target_n_steps) { |
||||||
|
finishTest(); |
||||||
|
return; |
||||||
|
} else { |
||||||
|
counter_ignore = 10; //ignore first 10 packets
|
||||||
|
counter_sum[counter_step]=0; |
||||||
|
sum_sig_pow_baseline[counter_step]=0.0f; |
||||||
|
for (int Ichan=0; Ichan < max_num_chan; Ichan++) sum_sig_pow_test[counter_step][Ichan]=0.0f; |
||||||
|
updateSignalGenerator(); |
||||||
|
freq_at_each_step_Hz[counter_step]=0.0f; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
virtual void updateSignalGenerator(void)
|
||||||
|
{ |
||||||
|
//if (Serial) Serial.println("AudioControlSignalTester_F32: updateSignalGenerator(): did the child version get called?");
|
||||||
|
} //override this is a child class!
|
||||||
|
|
||||||
|
virtual void finishTest(void) { |
||||||
|
//Serial.println("AudioControlSignalTester_F32: finishTest()...");
|
||||||
|
//disable the test instrumentation
|
||||||
|
sig_gen.end(); |
||||||
|
sig_meas.end(); |
||||||
|
|
||||||
|
//let listeners know that data is available
|
||||||
|
isDataAvailable = true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||||
|
class AudioControlTestAmpSweep_F32 : public AudioControlSignalTester_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: ampSweepTester
|
||||||
|
public: |
||||||
|
AudioControlTestAmpSweep_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
|
||||||
|
: AudioControlSignalTester_F32(settings, _sig_gen,_sig_meas) |
||||||
|
{ |
||||||
|
float start_amp_dB = -100.0f, end_amp_dB = 0.0f, step_amp_dB = 2.5f; |
||||||
|
setStepPattern(start_amp_dB, end_amp_dB, step_amp_dB); |
||||||
|
setTargetDurPerStep_sec(0.25); |
||||||
|
resetState(); |
||||||
|
} |
||||||
|
void begin(void) { |
||||||
|
//activate the instrumentation
|
||||||
|
sig_gen.begin(); |
||||||
|
sig_meas.begin(this); |
||||||
|
|
||||||
|
//start the test
|
||||||
|
resetState(); |
||||||
|
gotoNextStep(); |
||||||
|
} |
||||||
|
|
||||||
|
//use this to cancel the test
|
||||||
|
//void end(void) {
|
||||||
|
// finishTest();
|
||||||
|
//}
|
||||||
|
|
||||||
|
void printTableOfResults(Stream *s) { |
||||||
|
s->println("AudioControlTestAmpSweep_F32: Start Table of Results..."); |
||||||
|
AudioControlSignalTester_F32::printTableOfResults(s); |
||||||
|
s->println("AudioControlTestAmpSweep_F32: End Table of Results..."); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
|
||||||
|
virtual void updateSignalGenerator(void) { |
||||||
|
float new_amp_dB = start_val + ((float)counter_step)*step_val; //start_val and step_val are in parent class
|
||||||
|
Serial.print("AudioControlTestAmpSweep_F32: updateSignalGenerator(): setting amplitude to (dBFS) "); |
||||||
|
Serial.println(new_amp_dB); |
||||||
|
setSignalAmplitude_dBFS(new_amp_dB); |
||||||
|
} |
||||||
|
void finishTest(void) {
|
||||||
|
//disable the test instrumentation
|
||||||
|
setSignalAmplitude_dBFS(-1000.0f); //some very quiet value
|
||||||
|
|
||||||
|
//do all of the common actions
|
||||||
|
AudioControlSignalTester_F32::finishTest(); |
||||||
|
|
||||||
|
//print results
|
||||||
|
printTableOfResults(&Serial); |
||||||
|
} |
||||||
|
|
||||||
|
//void resetState(void) {
|
||||||
|
// AudioControlSignalTester_F32::resetState();
|
||||||
|
//}
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||||
|
class AudioControlTestFreqSweep_F32 : public AudioControlSignalTester_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: freqSweepTester
|
||||||
|
public: |
||||||
|
AudioControlTestFreqSweep_F32(AudioSettings_F32 &settings, AudioTestSignalGenerator_F32 &_sig_gen, AudioTestSignalMeasurementInterface_F32 &_sig_meas)
|
||||||
|
: AudioControlSignalTester_F32(settings, _sig_gen,_sig_meas) |
||||||
|
{ |
||||||
|
float start_freq_Hz = 125.f, end_freq_Hz = 16000.f, step_freq_octave = sqrtf(2.0); |
||||||
|
setStepPattern(start_freq_Hz, end_freq_Hz, step_freq_octave); |
||||||
|
setTargetDurPerStep_sec(0.25); |
||||||
|
resetState(); |
||||||
|
} |
||||||
|
void begin(void) { |
||||||
|
//activate the instrumentation
|
||||||
|
sig_gen.begin(); |
||||||
|
sig_meas.begin(this); |
||||||
|
|
||||||
|
//start the test
|
||||||
|
resetState(); |
||||||
|
recomputeTargetNumberOfSteps(); |
||||||
|
gotoNextStep(); |
||||||
|
} |
||||||
|
|
||||||
|
//use this to cancel the test
|
||||||
|
//void end(void) {
|
||||||
|
// finishTest();
|
||||||
|
//}
|
||||||
|
|
||||||
|
void printTableOfResults(Stream *s) { |
||||||
|
s->println("AudioControlTestFreqSweep_F32: Start Table of Results..."); |
||||||
|
AudioControlSignalTester_F32::printTableOfResults(s); |
||||||
|
s->println("AudioControlTestFreqSweep_F32: End Table of Results..."); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected: |
||||||
|
float signal_frequency_Hz = 1000.f; |
||||||
|
float signal_amplitude_dBFS = -50.0f; |
||||||
|
|
||||||
|
virtual int recomputeTargetNumberOfSteps(void) { |
||||||
|
return target_n_steps = (int)(log10f(end_val/start_val)/log10f(step_val)+0.5) + 1; //round
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updateSignalGenerator(void) { |
||||||
|
//logarithmically step the frequency
|
||||||
|
float new_freq_Hz = start_val * powf(step_val,counter_step); |
||||||
|
Serial.print("AudioControlTestFreqSweep_F32: updateSignalGenerator(): setting freq to "); |
||||||
|
Serial.println(new_freq_Hz); |
||||||
|
setSignalFrequency_Hz(new_freq_Hz); |
||||||
|
} |
||||||
|
void finishTest(void) { |
||||||
|
|
||||||
|
//disable the test instrumentation
|
||||||
|
setSignalAmplitude_dBFS(-1000.0f); //some very quiet value
|
||||||
|
setSignalFrequency_Hz(1000.f); |
||||||
|
|
||||||
|
//do all of the common actions
|
||||||
|
AudioControlSignalTester_F32::finishTest(); |
||||||
|
|
||||||
|
//print results
|
||||||
|
printTableOfResults(&Serial); |
||||||
|
} |
||||||
|
|
||||||
|
//void resetState(void) {
|
||||||
|
// AudioControlSignalTester_F32::resetState();
|
||||||
|
//}
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,41 @@ |
|||||||
|
|
||||||
|
/*
|
||||||
|
* AudioFilterBiquad_F32.cpp |
||||||
|
* |
||||||
|
* Chip Audette, OpenAudio, Apr 2017 |
||||||
|
* |
||||||
|
* MIT License, Use at your own risk. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include "AudioFilterBiquad_F32.h" |
||||||
|
|
||||||
|
void AudioFilterBiquad_F32::update(void) |
||||||
|
{ |
||||||
|
audio_block_f32_t *block; |
||||||
|
|
||||||
|
block = AudioStream_F32::receiveWritable_f32(); |
||||||
|
if (!block) return; |
||||||
|
|
||||||
|
// If there's no coefficient table, give up.
|
||||||
|
if (coeff_p == NULL) { |
||||||
|
AudioStream_F32::release(block); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// do passthru
|
||||||
|
if (coeff_p == IIR_F32_PASSTHRU) { |
||||||
|
// Just passthrough
|
||||||
|
AudioStream_F32::transmit(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// do IIR
|
||||||
|
arm_biquad_cascade_df1_f32(&iir_inst, block->data, block->data, block->length); |
||||||
|
|
||||||
|
//transmit the data
|
||||||
|
AudioStream_F32::transmit(block); // send the IIR output
|
||||||
|
AudioStream_F32::release(block); |
||||||
|
} |
@ -0,0 +1,204 @@ |
|||||||
|
/*
|
||||||
|
* AudioFilterBiquad_F32 |
||||||
|
*
|
||||||
|
* Created: Chip Audette (OpenAudio) Feb 2017 |
||||||
|
* |
||||||
|
* License: MIT License. Use at your own risk. |
||||||
|
*
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _filter_iir_f32 |
||||||
|
#define _filter_iir_f32 |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
// Indicates that the code should just pass through the audio
|
||||||
|
// without any filtering (as opposed to doing nothing at all)
|
||||||
|
#define IIR_F32_PASSTHRU ((const float32_t *) 1) |
||||||
|
|
||||||
|
#define IIR_MAX_STAGES 1 //meaningless right now
|
||||||
|
|
||||||
|
class AudioFilterBiquad_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName:IIR
|
||||||
|
public: |
||||||
|
AudioFilterBiquad_F32(void): AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) {
|
||||||
|
setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT);
|
||||||
|
} |
||||||
|
AudioFilterBiquad_F32(const AudioSettings_F32 &settings):
|
||||||
|
AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) { |
||||||
|
setSampleRate_Hz(settings.sample_rate_Hz);
|
||||||
|
} |
||||||
|
|
||||||
|
void begin(const float32_t *cp, int n_stages = 1) { |
||||||
|
coeff_p = cp; |
||||||
|
// Initialize FIR instance (ARM DSP Math Library)
|
||||||
|
if (coeff_p && (coeff_p != IIR_F32_PASSTHRU) && n_stages <= IIR_MAX_STAGES) { |
||||||
|
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html
|
||||||
|
arm_biquad_cascade_df1_init_f32(&iir_inst, n_stages, (float32_t *)coeff_p, &StateF32[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
void end(void) { |
||||||
|
coeff_p = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void setSampleRate_Hz(float _fs_Hz) { sampleRate_Hz = _fs_Hz; } |
||||||
|
|
||||||
|
void setBlockDC(void) { |
||||||
|
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
|
||||||
|
//Use matlab to compute the coeff for HP at 40Hz: [b,a]=butter(2,40/(44100/2),'high'); %assumes fs_Hz = 44100
|
||||||
|
float32_t b[] = {8.173653471988667e-01, -1.634730694397733e+00, 8.173653471988667e-01}; //from Matlab
|
||||||
|
float32_t a[] = { 1.000000000000000e+00, -1.601092394183619e+00, 6.683689946118476e-01}; //from Matlab
|
||||||
|
setFilterCoeff_Matlab(b, a); |
||||||
|
} |
||||||
|
|
||||||
|
void setFilterCoeff_Matlab(float32_t b[], float32_t a[]) { //one stage of N=2 IIR
|
||||||
|
//https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
|
||||||
|
//Use matlab to compute the coeff, such as: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100
|
||||||
|
coeff[0] = b[0]; coeff[1] = b[1]; coeff[2] = b[2]; //here are the matlab "b" coefficients
|
||||||
|
coeff[3] = -a[1]; coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab ;
|
||||||
|
begin(coeff); |
||||||
|
} |
||||||
|
|
||||||
|
//note: stage is currently ignored
|
||||||
|
void setCoefficients(int stage, float c[]) { |
||||||
|
if (stage > 0) { |
||||||
|
if (Serial) { |
||||||
|
Serial.println(F("AudioFilterBiquad_F32: setCoefficients: *** ERROR ***")); |
||||||
|
Serial.print(F(" : This module only accepts one stage.")); |
||||||
|
Serial.print(F(" : You are attempting to set stage "));Serial.print(stage); |
||||||
|
Serial.print(F(" : Ignoring this filter.")); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
coeff[0] = c[0]; |
||||||
|
coeff[1] = c[1]; |
||||||
|
coeff[2] = c[2]; |
||||||
|
coeff[3] = -c[3]; |
||||||
|
coeff[4] = -c[4]; |
||||||
|
begin(coeff); |
||||||
|
} |
||||||
|
|
||||||
|
// Compute common filter functions
|
||||||
|
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||||
|
//void setLowpass(uint32_t stage, float frequency, float q = 0.7071) {
|
||||||
|
void setLowpass(uint32_t stage, float frequency, float q = 0.7071) { |
||||||
|
//int coeff[5];
|
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
double alpha = sinW0 / ((double)q * 2.0); |
||||||
|
double cosW0 = cos(w0); |
||||||
|
//double scale = 1073741824.0 / (1.0 + alpha);
|
||||||
|
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
|
||||||
|
/* b0 */ coeff[0] = ((1.0 - cosW0) / 2.0) * scale; |
||||||
|
/* b1 */ coeff[1] = (1.0 - cosW0) * scale; |
||||||
|
/* b2 */ coeff[2] = coeff[0]; |
||||||
|
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale; |
||||||
|
/* a2 */ coeff[4] = (1.0 - alpha) * scale; |
||||||
|
|
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
void setHighpass(uint32_t stage, float frequency, float q = 0.7071) { |
||||||
|
//int coeff[5];
|
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
double alpha = sinW0 / ((double)q * 2.0); |
||||||
|
double cosW0 = cos(w0); |
||||||
|
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
|
||||||
|
/* b0 */ coeff[0] = ((1.0 + cosW0) / 2.0) * scale; |
||||||
|
/* b1 */ coeff[1] = -(1.0 + cosW0) * scale; |
||||||
|
/* b2 */ coeff[2] = coeff[0]; |
||||||
|
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale; |
||||||
|
/* a2 */ coeff[4] = (1.0 - alpha) * scale; |
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
void setBandpass(uint32_t stage, float frequency, float q = 1.0) { |
||||||
|
//int coeff[5];
|
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
double alpha = sinW0 / ((double)q * 2.0); |
||||||
|
double cosW0 = cos(w0); |
||||||
|
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
|
||||||
|
/* b0 */ coeff[0] = alpha * scale; |
||||||
|
/* b1 */ coeff[1] = 0; |
||||||
|
/* b2 */ coeff[2] = (-alpha) * scale; |
||||||
|
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale; |
||||||
|
/* a2 */ coeff[4] = (1.0 - alpha) * scale; |
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
void setNotch(uint32_t stage, float frequency, float q = 1.0) { |
||||||
|
//int coeff[5];
|
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
double alpha = sinW0 / ((double)q * 2.0); |
||||||
|
double cosW0 = cos(w0); |
||||||
|
double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0
|
||||||
|
/* b0 */ coeff[0] = scale; |
||||||
|
/* b1 */ coeff[1] = (-2.0 * cosW0) * scale; |
||||||
|
/* b2 */ coeff[2] = coeff[0]; |
||||||
|
/* a1 */ coeff[3] = (-2.0 * cosW0) * scale; |
||||||
|
/* a2 */ coeff[4] = (1.0 - alpha) * scale; |
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
void setLowShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) { |
||||||
|
//int coeff[5];
|
||||||
|
double a = pow(10.0, gain/40.0); |
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
//double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
|
||||||
|
double cosW0 = cos(w0); |
||||||
|
//generate three helper-values (intermediate results):
|
||||||
|
double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); |
||||||
|
double aMinus = (a-1.0)*cosW0; |
||||||
|
double aPlus = (a+1.0)*cosW0; |
||||||
|
double scale = 1.0 / ( (a+1.0) + aMinus + sinsq); |
||||||
|
/* b0 */ coeff[0] = a * ( (a+1.0) - aMinus + sinsq ) * scale; |
||||||
|
/* b1 */ coeff[1] = 2.0*a * ( (a-1.0) - aPlus ) * scale; |
||||||
|
/* b2 */ coeff[2] = a * ( (a+1.0) - aMinus - sinsq ) * scale; |
||||||
|
/* a1 */ coeff[3] = -2.0* ( (a-1.0) + aPlus ) * scale; |
||||||
|
/* a2 */ coeff[4] = ( (a+1.0) + aMinus - sinsq ) * scale; |
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
void setHighShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) { |
||||||
|
//int coeff[5];
|
||||||
|
double a = pow(10.0, gain/40.0); |
||||||
|
double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
double sinW0 = sin(w0); |
||||||
|
//double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
|
||||||
|
double cosW0 = cos(w0); |
||||||
|
//generate three helper-values (intermediate results):
|
||||||
|
double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); |
||||||
|
double aMinus = (a-1.0)*cosW0; |
||||||
|
double aPlus = (a+1.0)*cosW0; |
||||||
|
double scale = 1.0 / ( (a+1.0) - aMinus + sinsq); |
||||||
|
/* b0 */ coeff[0] = a * ( (a+1.0) + aMinus + sinsq ) * scale; |
||||||
|
/* b1 */ coeff[1] = -2.0*a * ( (a-1.0) + aPlus ) * scale; |
||||||
|
/* b2 */ coeff[2] = a * ( (a+1.0) + aMinus - sinsq ) * scale; |
||||||
|
/* a1 */ coeff[3] = 2.0* ( (a-1.0) - aPlus ) * scale; |
||||||
|
/* a2 */ coeff[4] = ( (a+1.0) - aMinus - sinsq ) * scale; |
||||||
|
setCoefficients(stage, coeff); |
||||||
|
} |
||||||
|
|
||||||
|
void update(void); |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
float32_t coeff[5 * 1] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later
|
||||||
|
float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h??
|
||||||
|
|
||||||
|
// pointer to current coefficients or NULL or FIR_PASSTHRU
|
||||||
|
const float32_t *coeff_p; |
||||||
|
|
||||||
|
// ARM DSP Math library filter instance
|
||||||
|
arm_biquad_casd_df1_inst_f32 iir_inst; |
||||||
|
float32_t StateF32[4*IIR_MAX_STAGES]; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@ |
|||||||
|
/*
|
||||||
|
* AudioFilterFIR_F32.cpp |
||||||
|
* |
||||||
|
* Chip Audette, OpenAudio, Apr 2017 |
||||||
|
* |
||||||
|
* MIT License, Use at your own risk. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "AudioFilterFIR_F32.h" |
||||||
|
|
||||||
|
|
||||||
|
void AudioFilterFIR_F32::update(void) |
||||||
|
{ |
||||||
|
audio_block_f32_t *block, *block_new; |
||||||
|
|
||||||
|
block = AudioStream_F32::receiveReadOnly_f32(); |
||||||
|
if (!block) return; |
||||||
|
|
||||||
|
// If there's no coefficient table, give up.
|
||||||
|
if (coeff_p == NULL) { |
||||||
|
AudioStream_F32::release(block); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// do passthru
|
||||||
|
if (coeff_p == FIR_F32_PASSTHRU) { |
||||||
|
// Just passthrough
|
||||||
|
AudioStream_F32::transmit(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
//Serial.println("AudioFilterFIR_F32: update(): PASSTHRU.");
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// get a block for the FIR output
|
||||||
|
block_new = AudioStream_F32::allocate_f32(); |
||||||
|
if (block_new) { |
||||||
|
|
||||||
|
//check to make sure our FIR instance has the right size
|
||||||
|
if (block->length != configured_block_size) { |
||||||
|
//doesn't match. re-initialize
|
||||||
|
Serial.println("AudioFilterFIR_F32: block size doesn't match. Re-initializing FIR."); |
||||||
|
begin(coeff_p, n_coeffs, block->length); //initialize with same coefficients, just a new block length
|
||||||
|
} |
||||||
|
|
||||||
|
//apply the FIR
|
||||||
|
arm_fir_f32(&fir_inst, block->data, block_new->data, block->length); |
||||||
|
block_new->length = block->length; |
||||||
|
|
||||||
|
//transmit the data
|
||||||
|
AudioStream_F32::transmit(block_new); // send the FIR output
|
||||||
|
AudioStream_F32::release(block_new); |
||||||
|
} |
||||||
|
AudioStream_F32::release(block); |
||||||
|
} |
@ -1,30 +0,0 @@ |
|||||||
#include "AudioMixer4_F32.h" |
|
||||||
|
|
||||||
void AudioMixer4_F32::update(void) { |
|
||||||
audio_block_f32_t *in, *out=NULL; |
|
||||||
|
|
||||||
out = receiveWritable_f32(0); |
|
||||||
if (!out) return; |
|
||||||
|
|
||||||
arm_scale_f32(out->data, multiplier[0], out->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
|
|
||||||
for (int channel=1; channel < 4; channel++) { |
|
||||||
in = receiveReadOnly_f32(channel); |
|
||||||
if (!in) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
audio_block_f32_t *tmp = allocate_f32(); |
|
||||||
|
|
||||||
arm_scale_f32(in->data, multiplier[channel], tmp->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
arm_add_f32(out->data, tmp->data, out->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
|
|
||||||
release(tmp); |
|
||||||
release(in); |
|
||||||
} |
|
||||||
|
|
||||||
if (out) { |
|
||||||
transmit(out); |
|
||||||
release(out); |
|
||||||
} |
|
||||||
} |
|
@ -1,38 +0,0 @@ |
|||||||
/*
|
|
||||||
* AudioMixer4 |
|
||||||
*
|
|
||||||
* Created: Patrick Radius, December 2016 |
|
||||||
* Purpose: Mix up to 4 audio channels with individual gain controls. |
|
||||||
* Assumes floating-point data. |
|
||||||
*
|
|
||||||
* This processes a single stream fo audio data (ie, it is mono)
|
|
||||||
*
|
|
||||||
* MIT License. use at your own risk. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef AUDIOMIXER4F32_H |
|
||||||
#define AUDIOMIXER4F32_H |
|
||||||
|
|
||||||
#include <arm_math.h> |
|
||||||
#include <AudioStream_F32.h> |
|
||||||
|
|
||||||
class AudioMixer4_F32 : public AudioStream_F32 { |
|
||||||
//GUI: inputs:4, outputs:1 //this line used for automatic generation of GUI node
|
|
||||||
public: |
|
||||||
AudioMixer4_F32() : AudioStream_F32(4, inputQueueArray) { |
|
||||||
for (int i=0; i<4; i++) multiplier[i] = 1.0; |
|
||||||
} |
|
||||||
|
|
||||||
virtual void update(void); |
|
||||||
|
|
||||||
void gain(unsigned int channel, float gain) { |
|
||||||
if (channel >= 4 || channel < 0) return; |
|
||||||
multiplier[channel] = gain; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
audio_block_f32_t *inputQueueArray[4]; |
|
||||||
float multiplier[4]; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif |
|
@ -0,0 +1,29 @@ |
|||||||
|
|
||||||
|
|
||||||
|
#include "AudioSettings_F32.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
|
||||||
|
float AudioSettings_F32::cpu_load_percent(const int n) { |
||||||
|
//n is the number of cycles
|
||||||
|
#define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) |
||||||
|
float foo1 = ((float)(F_CPU / 32))/sample_rate_Hz; |
||||||
|
foo1 *= ((float)audio_block_samples); |
||||||
|
foo1 /= 100.f; |
||||||
|
foo1 += (float)n; |
||||||
|
float foo2 = (float)(F_CPU / 16)/sample_rate_Hz; |
||||||
|
foo2 *= ((float)audio_block_samples); |
||||||
|
foo2 /= 100.f; |
||||||
|
return foo1 / foo2; |
||||||
|
|
||||||
|
//return (((n) + (F_CPU / 32 / sample_rate_Hz * audio_block_samples / 100)) / (F_CPU / 16 / sample_rate_Hz * audio_block_samples / 100));
|
||||||
|
} |
||||||
|
|
||||||
|
float AudioSettings_F32::processorUsage(void) {
|
||||||
|
return cpu_load_percent(AudioStream::cpu_cycles_total);
|
||||||
|
}; |
||||||
|
float AudioSettings_F32::processorUsageMax(void) {
|
||||||
|
return cpu_load_percent(AudioStream::cpu_cycles_total_max);
|
||||||
|
} |
||||||
|
void AudioSettings_F32::processorUsageMaxReset(void) {
|
||||||
|
AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total;
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
|
||||||
|
#ifndef _AudioSettings_F32_ |
||||||
|
#define _AudioSettings_F32_ |
||||||
|
|
||||||
|
class AudioSettings_F32 { |
||||||
|
public: |
||||||
|
AudioSettings_F32(float fs_Hz, int block_size) : |
||||||
|
sample_rate_Hz(fs_Hz), audio_block_samples(block_size) {} |
||||||
|
const float sample_rate_Hz; |
||||||
|
const int audio_block_samples; |
||||||
|
|
||||||
|
float cpu_load_percent(const int n); |
||||||
|
float processorUsage(void); |
||||||
|
float processorUsageMax(void); |
||||||
|
void processorUsageMaxReset(void); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,181 @@ |
|||||||
|
|
||||||
|
#ifndef _BTNRH_WDRC_TYPES_H |
||||||
|
#define _BTNRH_WDRC_TYPES_H |
||||||
|
|
||||||
|
namespace BTNRH_WDRC { |
||||||
|
|
||||||
|
// from CHAPRO cha_ff.h
|
||||||
|
#define DSL_MXCH 32 |
||||||
|
//class CHA_DSL {
|
||||||
|
typedef struct { |
||||||
|
//public:
|
||||||
|
//CHA_DSL(void) {};
|
||||||
|
//static const int DSL_MXCH = 32; // maximum number of channels
|
||||||
|
float attack; // attack time (ms)
|
||||||
|
float release; // release time (ms)
|
||||||
|
float maxdB; // maximum signal (dB SPL)
|
||||||
|
int ear; // 0=left, 1=right
|
||||||
|
int nchannel; // number of channels
|
||||||
|
float cross_freq[DSL_MXCH]; // cross frequencies (Hz)
|
||||||
|
float tkgain[DSL_MXCH]; // compression-start gain
|
||||||
|
float cr[DSL_MXCH]; // compression ratio
|
||||||
|
float tk[DSL_MXCH]; // compression-start kneepoint
|
||||||
|
float bolt[DSL_MXCH]; // broadband output limiting threshold
|
||||||
|
} CHA_DSL; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
//public:
|
||||||
|
//CHA_DSL(void) {};
|
||||||
|
//static const int DSL_MXCH = 32; // maximum number of channels
|
||||||
|
float attack; // attack time (ms)
|
||||||
|
float release; // release time (ms)
|
||||||
|
float maxdB; // maximum signal (dB SPL)
|
||||||
|
int ear; // 0=left, 1=right
|
||||||
|
int nchannel; // number of channels
|
||||||
|
float cross_freq[DSL_MXCH]; // cross frequencies (Hz)
|
||||||
|
float exp_cr[DSL_MXCH]; // compression ratio for low-SPL region (ie, the expander)
|
||||||
|
float exp_end_knee[DSL_MXCH]; // expansion-end kneepoint
|
||||||
|
float tkgain[DSL_MXCH]; // compression-start gain
|
||||||
|
float cr[DSL_MXCH]; // compression ratio
|
||||||
|
float tk[DSL_MXCH]; // compression-start kneepoint
|
||||||
|
float bolt[DSL_MXCH]; // broadband output limiting threshold
|
||||||
|
} CHA_DSL2; |
||||||
|
|
||||||
|
/* int parseStringIntoDSL(String &text_buffer) {
|
||||||
|
int position = 0; |
||||||
|
float foo_val; |
||||||
|
const bool print_debug = false; |
||||||
|
|
||||||
|
if (print_debug) Serial.println("parseTextAsDSL: values from file:"); |
||||||
|
|
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
attack = foo_val; |
||||||
|
if (print_debug) { Serial.print(" attack: "); Serial.println(attack); } |
||||||
|
|
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
release = foo_val; |
||||||
|
if (print_debug) { Serial.print(" release: "); Serial.println(release); } |
||||||
|
|
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
maxdB = foo_val; |
||||||
|
if (print_debug) { Serial.print(" maxdB: "); Serial.println(maxdB); } |
||||||
|
|
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
ear = int(foo_val + 0.5); //round
|
||||||
|
if (print_debug) { Serial.print(" ear: "); Serial.println(ear); } |
||||||
|
|
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
nchannel = int(foo_val + 0.5); //round
|
||||||
|
if (print_debug) { Serial.print(" nchannel: "); Serial.println(nchannel); } |
||||||
|
|
||||||
|
//check to see if the number of channels is acceptable.
|
||||||
|
if ((nchannel < 0) || (nchannel > DSL_MXCH)) { |
||||||
|
if (print_debug) Serial.print(" : channel number is too big (or negative). stopping.");
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
//read the cross-over frequencies. There should be nchan-1 of them (0 and Nyquist are assumed)
|
||||||
|
if (print_debug) Serial.print(" cross_freq: "); |
||||||
|
for (int i=0; i < (nchannel-1); i++) { |
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
cross_freq[i] = foo_val; |
||||||
|
if (print_debug) { Serial.print(cross_freq[i]); Serial.print(", ");} |
||||||
|
} |
||||||
|
if (print_debug) Serial.println(); |
||||||
|
|
||||||
|
//read the tkgain values. There should be nchan of them
|
||||||
|
if (print_debug) Serial.print(" tkgain: "); |
||||||
|
for (int i=0; i < nchannel; i++) { |
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
tkgain[i] = foo_val; |
||||||
|
if (print_debug) { Serial.print(tkgain[i]); Serial.print(", ");} |
||||||
|
} |
||||||
|
if (print_debug) Serial.println(); |
||||||
|
|
||||||
|
//read the cr values. There should be nchan of them
|
||||||
|
if (print_debug) Serial.print(" cr: "); |
||||||
|
for (int i=0; i < nchannel; i++) { |
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
cr[i] = foo_val; |
||||||
|
if (print_debug) { Serial.print(cr[i]); Serial.print(", ");} |
||||||
|
} |
||||||
|
if (print_debug) Serial.println(); |
||||||
|
|
||||||
|
//read the tk values. There should be nchan of them
|
||||||
|
if (print_debug) Serial.print(" tk: "); |
||||||
|
for (int i=0; i < nchannel; i++) { |
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
tk[i] = foo_val; |
||||||
|
if (print_debug) { Serial.print(tk[i]); Serial.print(", ");} |
||||||
|
} |
||||||
|
if (print_debug) Serial.println(); |
||||||
|
|
||||||
|
//read the bolt values. There should be nchan of them
|
||||||
|
if (print_debug) Serial.print(" bolt: "); |
||||||
|
for (int i=0; i < nchannel; i++) { |
||||||
|
position = parseNextNumberFromString(text_buffer, position, foo_val); |
||||||
|
bolt[i] = foo_val; |
||||||
|
if (print_debug) { Serial.print(bolt[i]); Serial.print(", ");} |
||||||
|
} |
||||||
|
if (print_debug) Serial.println(); |
||||||
|
|
||||||
|
return 0; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void printToStream(Stream *s) { |
||||||
|
s->print("CHA_DSL: attack (ms) = "); s->println(attack); |
||||||
|
s->print(" : release (ms) = "); s->println(release); |
||||||
|
s->print(" : maxdB (dB SPL) = "); s->println(maxdB); |
||||||
|
s->print(" : ear (0 = left, 1 = right) "); s->println(ear); |
||||||
|
s->print(" : nchannel = "); s->println(nchannel); |
||||||
|
s->print(" : cross_freq (Hz) = "); |
||||||
|
for (int i=0; i<nchannel-1;i++) { s->print(cross_freq[i]); s->print(", ");}; s->println(); |
||||||
|
s->print(" : tkgain = "); |
||||||
|
for (int i=0; i<nchannel;i++) { s->print(tkgain[i]); s->print(", ");}; s->println(); |
||||||
|
s->print(" : cr = "); |
||||||
|
for (int i=0; i<nchannel;i++) { s->print(cr[i]); s->print(", ");}; s->println(); |
||||||
|
s->print(" : tk = "); |
||||||
|
for (int i=0; i<nchannel;i++) { s->print(tk[i]); s->print(", ");}; s->println(); |
||||||
|
s->print(" : bolt = "); |
||||||
|
for (int i=0; i<nchannel;i++) { s->print(bolt[i]); s->print(", ");}; s->println(); |
||||||
|
} |
||||||
|
} ; */ |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
float alfa; // attack constant (not time)
|
||||||
|
float beta; // release constant (not time
|
||||||
|
float fs; // sampling rate (Hz)
|
||||||
|
float maxdB; // maximum signal (dB SPL)
|
||||||
|
float tkgain; // compression-start gain
|
||||||
|
float tk; // compression-start kneepoint
|
||||||
|
float cr; // compression ratio
|
||||||
|
float bolt; // broadband output limiting threshold
|
||||||
|
} CHA_DVAR_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
float attack; // attack time (ms), unused in this class
|
||||||
|
float release; // release time (ms), unused in this class
|
||||||
|
float fs; // sampling rate (Hz), set through other means in this class
|
||||||
|
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
|
||||||
|
float tkgain; // compression-start gain
|
||||||
|
float tk; // compression-start kneepoint
|
||||||
|
float cr; // compression ratio
|
||||||
|
float bolt; // broadband output limiting threshold
|
||||||
|
} CHA_WDRC; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
float attack; // attack time (ms), unused in this class
|
||||||
|
float release; // release time (ms), unused in this class
|
||||||
|
float fs; // sampling rate (Hz), set through other means in this class
|
||||||
|
float maxdB; // maximum signal (dB SPL)...I think this is the SPL corresponding to signal with rms of 1.0
|
||||||
|
float exp_cr; // compression ratio for low-SPL region (ie, the expander)
|
||||||
|
float exp_end_knee; // expansion-end kneepoint
|
||||||
|
float tkgain; // compression-start gain
|
||||||
|
float tk; // compression-start kneepoint
|
||||||
|
float cr; // compression ratio
|
||||||
|
float bolt; // broadband output limiting threshold
|
||||||
|
} CHA_WDRC2; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,146 @@ |
|||||||
|
/*
|
||||||
|
* USB_Audio_F32 |
||||||
|
* |
||||||
|
* Created: Chip Audette (OpenAudio), Mar 2017 |
||||||
|
* Float32 wrapper for the Audio USB classes from the Teensy Audio Library |
||||||
|
* |
||||||
|
* License: MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef usb_audio_f32_h_ |
||||||
|
#define usb_audio_f32_h_ |
||||||
|
//#include "Arduino.h"
|
||||||
|
#include <AudioStream_F32.h> |
||||||
|
#include <Audio.h> |
||||||
|
|
||||||
|
class AudioInputUSB_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName:usbAudioIn //this line used for automatic generation of GUI node
|
||||||
|
public: |
||||||
|
AudioInputUSB_F32() : AudioStream_F32(0, NULL) { |
||||||
|
//i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h
|
||||||
|
//output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h
|
||||||
|
|
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
AudioInputUSB_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) { |
||||||
|
//i16_to_f32.disconnectFromUpdateAll(); //requires modification to AudioStream.h
|
||||||
|
//output_queue.disconnectFromUpdateAll(); //requires modification to AudioStream.h
|
||||||
|
|
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
|
||||||
|
void makeConnections(void) { |
||||||
|
//make the audio connections
|
||||||
|
patchCord100_L = new AudioConnection(usb_in, 0, i16_to_f32_L, 0); //usb_in is an Int16 audio object. So, convert it!
|
||||||
|
patchCord100_R = new AudioConnection(usb_in, 1, i16_to_f32_R, 0); //usb_in is an Int16 audio object. So, convert it!
|
||||||
|
patchCord101_L = new AudioConnection_F32(i16_to_f32_L, 0, output_queue_L, 0); |
||||||
|
patchCord101_R = new AudioConnection_F32(i16_to_f32_R, 0, output_queue_R, 0); |
||||||
|
} |
||||||
|
|
||||||
|
//define audio processing blocks.
|
||||||
|
AudioInputUSB usb_in; //from the original Teensy Audio Library, expects Int16 audio data
|
||||||
|
AudioConvert_I16toF32 i16_to_f32_L, i16_to_f32_R;
|
||||||
|
AudioRecordQueue_F32 output_queue_L,output_queue_R;
|
||||||
|
|
||||||
|
//define the audio connections
|
||||||
|
AudioConnection *patchCord100_L, *patchCord100_R; |
||||||
|
AudioConnection_F32 *patchCord101_L, *patchCord101_R; |
||||||
|
|
||||||
|
void update(void) { |
||||||
|
//Serial.println("AudioSynthNoiseWhite_F32: update().");
|
||||||
|
output_queue_L.begin(); |
||||||
|
output_queue_R.begin(); |
||||||
|
|
||||||
|
//manually update audio blocks in the desired order
|
||||||
|
usb_in.update(); //the output should be routed directly via the AudioConnection
|
||||||
|
i16_to_f32_L.update(); // output is routed via the AudioConnection
|
||||||
|
i16_to_f32_R.update(); // output is routed via the AudioConnection
|
||||||
|
output_queue_L.update(); |
||||||
|
output_queue_R.update(); |
||||||
|
|
||||||
|
//handle the output for the left channel
|
||||||
|
audio_block_f32_t *block; |
||||||
|
block = output_queue_L.getAudioBlock(); |
||||||
|
if (block == NULL) return; |
||||||
|
AudioStream_F32::transmit(block,0); |
||||||
|
output_queue_L.freeAudioBlock(); |
||||||
|
output_queue_L.end(); |
||||||
|
|
||||||
|
//handle the output for the left channel
|
||||||
|
block = output_queue_R.getAudioBlock(); |
||||||
|
if (block == NULL) return; |
||||||
|
AudioStream_F32::transmit(block,1); |
||||||
|
output_queue_R.freeAudioBlock(); |
||||||
|
output_queue_R.end(); |
||||||
|
} |
||||||
|
private:
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class AudioOutputUSB_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName:usbAudioOut //this line used for automatic generation of GUI node
|
||||||
|
public: |
||||||
|
AudioOutputUSB_F32() : AudioStream_F32(2, inputQueueArray_f32) { |
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
|
||||||
|
AudioOutputUSB_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) { |
||||||
|
makeConnections(); |
||||||
|
} |
||||||
|
|
||||||
|
void makeConnections(void) { |
||||||
|
//make the audio connections
|
||||||
|
patchCord100_L = new AudioConnection_F32(queue_L, 0, f32_to_i16_L, 0); //noise is an Int16 audio object. So, convert it!
|
||||||
|
patchCord100_R = new AudioConnection_F32(queue_R, 0, f32_to_i16_R, 0); //noise is an Int16 audio object. So, convert it!
|
||||||
|
patchCord101_L = new AudioConnection(f32_to_i16_L, 0, usb_out, 0); //Int16 audio connection
|
||||||
|
patchCord101_R = new AudioConnection(f32_to_i16_R, 0, usb_out, 1); //Int16 audio connection
|
||||||
|
} |
||||||
|
|
||||||
|
//define audio processing blocks.
|
||||||
|
AudioPlayQueue_F32 queue_L,queue_R;
|
||||||
|
AudioConvert_F32toI16 f32_to_i16_L, f32_to_i16_R;
|
||||||
|
AudioOutputUSB usb_out; //from the original Teensy Audio Library, expects Int16 audio data
|
||||||
|
|
||||||
|
//define the audio connections
|
||||||
|
AudioConnection_F32 *patchCord100_L, *patchCord100_R; |
||||||
|
AudioConnection *patchCord101_L, *patchCord101_R; |
||||||
|
|
||||||
|
void update(void) { |
||||||
|
//Serial.println("AudioSynthNoiseWhite_F32: update().");
|
||||||
|
//queue_L.begin();
|
||||||
|
//queue_R.begin();
|
||||||
|
|
||||||
|
//is there audio waiting for us for the left channel?
|
||||||
|
audio_block_f32_t *block; |
||||||
|
block = receiveReadOnly_f32(0);
|
||||||
|
if (!block) return; //if no audio, return now.
|
||||||
|
|
||||||
|
//there is some audio, so execute the processing chain for the left channel
|
||||||
|
queue_L.playAudioBlock(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
queue_L.update(); |
||||||
|
f32_to_i16_L.update(); |
||||||
|
|
||||||
|
//see if there is a right channel
|
||||||
|
block = receiveReadOnly_f32(1);
|
||||||
|
if (block) { |
||||||
|
//there is a right channel. process it now
|
||||||
|
queue_R.playAudioBlock(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
queue_R.update(); |
||||||
|
f32_to_i16_R.update(); |
||||||
|
} |
||||||
|
|
||||||
|
//whether or not there was right-channel audio, update the usb_out
|
||||||
|
usb_out.update(); |
||||||
|
return; |
||||||
|
} |
||||||
|
private:
|
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,393 @@ |
|||||||
|
|
||||||
|
//ifndef _BTNRH_FFT_H
|
||||||
|
//define _BTNRH_FFT_H
|
||||||
|
|
||||||
|
#include "BTNRH_rfft.h" |
||||||
|
#include <math.h> |
||||||
|
//#include "chapro.h"
|
||||||
|
//#include "cha_ff.h"
|
||||||
|
|
||||||
|
/***********************************************************/ |
||||||
|
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
|
||||||
|
// In IEEE Acoustics, Speech, and Signal Processing Society.
|
||||||
|
// "Programs for Digital Signal Processing," IEEE Press, New York,
|
||||||
|
|
||||||
|
namespace BTNRH_FFT { |
||||||
|
|
||||||
|
static __inline int |
||||||
|
ilog2(int n) |
||||||
|
{ |
||||||
|
int m; |
||||||
|
|
||||||
|
for (m = 1; m < 32; m++) |
||||||
|
if (n == (1 << m)) |
||||||
|
return (m); |
||||||
|
return (-1); |
||||||
|
} |
||||||
|
|
||||||
|
static __inline int |
||||||
|
bitrev(int ii, int m) |
||||||
|
{ |
||||||
|
register int jj; |
||||||
|
|
||||||
|
jj = ii & 1; |
||||||
|
--m; |
||||||
|
while (--m > 0) { |
||||||
|
ii >>= 1; |
||||||
|
jj <<= 1; |
||||||
|
jj |= ii & 1; |
||||||
|
} |
||||||
|
return (jj); |
||||||
|
} |
||||||
|
|
||||||
|
static __inline void |
||||||
|
rad2(int ii, float *x0, float *x1) |
||||||
|
{ |
||||||
|
int k; |
||||||
|
float t; |
||||||
|
|
||||||
|
for (k = 0; k < ii; k++) { |
||||||
|
t = x0[k] + x1[k]; |
||||||
|
x1[k] = x0[k] - x1[k]; |
||||||
|
x0[k] = t; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static __inline void |
||||||
|
reorder1(int m, float *x) |
||||||
|
{ |
||||||
|
int j, k, kl, n; |
||||||
|
float t; |
||||||
|
|
||||||
|
k = 4; |
||||||
|
kl = 2; |
||||||
|
n = 1 << m; |
||||||
|
for (j = 4; j <= n; j += 2) { |
||||||
|
if (k > j) { |
||||||
|
t = x[j - 1]; |
||||||
|
x[j - 1] = x[k - 1]; |
||||||
|
x[k - 1] = t; |
||||||
|
} |
||||||
|
k -= 2; |
||||||
|
if (k <= kl) { |
||||||
|
k = 2 * j; |
||||||
|
kl = j; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static __inline void |
||||||
|
reorder2(int m, float *x) |
||||||
|
{ |
||||||
|
int ji, ij, n; |
||||||
|
float t; |
||||||
|
|
||||||
|
n = 1 << m; |
||||||
|
for (ij = 0; ij <= (n - 2); ij += 2) { |
||||||
|
ji = bitrev(ij >> 1, m) << 1; |
||||||
|
if (ij < ji) { |
||||||
|
t = x[ij]; |
||||||
|
x[ij] = x[ji]; |
||||||
|
x[ji] = t; |
||||||
|
t = x[ij + 1]; |
||||||
|
x[ij + 1] = x[ji + 1]; |
||||||
|
x[ji + 1] = t; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/***********************************************************/ |
||||||
|
|
||||||
|
// rcfft
|
||||||
|
|
||||||
|
static void |
||||||
|
rcrad4(int ii, int nn, |
||||||
|
float *x0, float *x1, float *x2, float *x3, |
||||||
|
float *x4, float *x5, float *x6, float *x7) |
||||||
|
{ |
||||||
|
double arg, tpiovn; |
||||||
|
float c1, c2, c3, s1, s2, s3, pr, pi, r1, r5; |
||||||
|
float t0, t1, t2, t3, t4, t5, t6, t7; |
||||||
|
int i0, i4, j, j0, ji, jl, jr, jlast, k, k0, kl, m, n, ni; |
||||||
|
|
||||||
|
n = nn / 4; |
||||||
|
for (m = 1; (1 << m) < n; m++) |
||||||
|
continue; |
||||||
|
tpiovn = 2 * M_PI / nn; |
||||||
|
ji = 3; |
||||||
|
jl = 2; |
||||||
|
jr = 2; |
||||||
|
ni = (n + 1) / 2; |
||||||
|
for (i0 = 0; i0 < ni; i0++) { |
||||||
|
if (i0 == 0) { |
||||||
|
for (k = 0; k < ii; k++) { |
||||||
|
t0 = x0[k] + x2[k]; |
||||||
|
t1 = x1[k] + x3[k]; |
||||||
|
x2[k] = x0[k] - x2[k]; |
||||||
|
x3[k] = x1[k] - x3[k]; |
||||||
|
x0[k] = t0 + t1; |
||||||
|
x1[k] = t0 - t1; |
||||||
|
} |
||||||
|
if (nn > 4) { |
||||||
|
k0 = ii * 4; |
||||||
|
kl = k0 + ii; |
||||||
|
for (k = k0; k < kl; k++) { |
||||||
|
pr = (float) (M_SQRT1_2 * (x1[k] - x3[k])); |
||||||
|
pi = (float) (M_SQRT1_2 * (x1[k] + x3[k])); |
||||||
|
x3[k] = x2[k] + pi; |
||||||
|
x1[k] = pi - x2[k]; |
||||||
|
x2[k] = x0[k] - pr; |
||||||
|
x0[k] += pr; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
arg = tpiovn * bitrev(i0, m); |
||||||
|
c1 = cosf(arg); |
||||||
|
s1 = sinf(arg); |
||||||
|
c2 = c1 * c1 - s1 * s1; |
||||||
|
s2 = c1 * s1 + c1 * s1; |
||||||
|
c3 = c1 * c2 - s1 * s2; |
||||||
|
s3 = c2 * s1 + s2 * c1; |
||||||
|
i4 = ii * 4; |
||||||
|
j0 = jr * i4; |
||||||
|
k0 = ji * i4; |
||||||
|
jlast = j0 + ii; |
||||||
|
for (j = j0; j < jlast; j++) { |
||||||
|
k = k0 + j - j0; |
||||||
|
r1 = x1[j] * c1 - x5[k] * s1; |
||||||
|
r5 = x1[j] * s1 + x5[k] * c1; |
||||||
|
t2 = x2[j] * c2 - x6[k] * s2; |
||||||
|
t6 = x2[j] * s2 + x6[k] * c2; |
||||||
|
t3 = x3[j] * c3 - x7[k] * s3; |
||||||
|
t7 = x3[j] * s3 + x7[k] * c3; |
||||||
|
t0 = x0[j] + t2; |
||||||
|
t4 = x4[k] + t6; |
||||||
|
t2 = x0[j] - t2; |
||||||
|
t6 = x4[k] - t6; |
||||||
|
t1 = r1 + t3; |
||||||
|
t5 = r5 + t7; |
||||||
|
t3 = r1 - t3; |
||||||
|
t7 = r5 - t7; |
||||||
|
x0[j] = t0 + t1; |
||||||
|
x7[k] = t4 + t5; |
||||||
|
x6[k] = t0 - t1; |
||||||
|
x1[j] = t5 - t4; |
||||||
|
x2[j] = t2 - t7; |
||||||
|
x5[k] = t6 + t3; |
||||||
|
x4[k] = t2 + t7; |
||||||
|
x3[j] = t3 - t6; |
||||||
|
} |
||||||
|
jr += 2; |
||||||
|
ji -= 2; |
||||||
|
if (ji <= jl) { |
||||||
|
ji = 2 * jr - 1; |
||||||
|
jl = jr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
|
||||||
|
static int |
||||||
|
rcfft2(float *x, int m) |
||||||
|
{ |
||||||
|
int ii, nn, m2, it, n; |
||||||
|
|
||||||
|
n = 1 << m;; |
||||||
|
m2 = m / 2; |
||||||
|
|
||||||
|
// radix 2
|
||||||
|
|
||||||
|
if (m <= m2 * 2) { |
||||||
|
nn = 1; |
||||||
|
} else { |
||||||
|
nn = 2; |
||||||
|
ii = n / nn; |
||||||
|
rad2(ii, x, x + ii); |
||||||
|
} |
||||||
|
|
||||||
|
// radix 4
|
||||||
|
|
||||||
|
if (m2 != 0) { |
||||||
|
for (it = 0; it < m2; it++) { |
||||||
|
nn = nn * 4; |
||||||
|
ii = n / nn; |
||||||
|
rcrad4(ii, nn, x, x + ii, x + 2 * ii, x + 3 * ii, |
||||||
|
x, x + ii, x + 2 * ii, x + 3 * ii); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// re-order
|
||||||
|
|
||||||
|
reorder1(m, x); |
||||||
|
reorder2(m, x); |
||||||
|
for (it = 3; it < n; it += 2) |
||||||
|
x[it] = -x[it]; |
||||||
|
x[n] = x[1]; |
||||||
|
x[1] = 0.0; |
||||||
|
x[n + 1] = 0.0; |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
/***********************************************************/ |
||||||
|
|
||||||
|
// rcfft
|
||||||
|
|
||||||
|
static void |
||||||
|
crrad4(int jj, int nn, |
||||||
|
float *x0, float *x1, float *x2, float *x3, |
||||||
|
float *x4, float *x5, float *x6, float *x7) |
||||||
|
{ |
||||||
|
double arg, tpiovn; |
||||||
|
float c1, c2, c3, s1, s2, s3; |
||||||
|
float t0, t1, t2, t3, t4, t5, t6, t7; |
||||||
|
int ii, j, j0, ji, jr, jl, jlast, j4, k, k0, kl, m, n, ni; |
||||||
|
|
||||||
|
tpiovn = 2 * M_PI / nn; |
||||||
|
ji = 3; |
||||||
|
jl = 2; |
||||||
|
jr = 2; |
||||||
|
n = nn / 4; |
||||||
|
for (m = 1; (1 << m) < n; m++) |
||||||
|
continue; |
||||||
|
ni = (n + 1) / 2; |
||||||
|
for (ii = 0; ii < ni; ii++) { |
||||||
|
if (ii == 0) { |
||||||
|
for (k = 0; k < jj; k++) { |
||||||
|
t0 = x0[k] + x1[k]; |
||||||
|
t1 = x0[k] - x1[k]; |
||||||
|
t2 = x2[k] * 2; |
||||||
|
t3 = x3[k] * 2; |
||||||
|
x0[k] = t0 + t2; |
||||||
|
x2[k] = t0 - t2; |
||||||
|
x1[k] = t1 + t3; |
||||||
|
x3[k] = t1 - t3; |
||||||
|
} |
||||||
|
if (nn > 4) { |
||||||
|
k0 = jj * 4; |
||||||
|
kl = k0 + jj; |
||||||
|
for (k = k0; k < kl; k++) { |
||||||
|
t2 = x0[k] - x2[k]; |
||||||
|
t3 = x1[k] + x3[k]; |
||||||
|
x0[k] = (x0[k] + x2[k]) * 2; |
||||||
|
x2[k] = (x3[k] - x1[k]) * 2; |
||||||
|
x1[k] = (float) ((t2 + t3) * M_SQRT2); |
||||||
|
x3[k] = (float) ((t3 - t2) * M_SQRT2); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
arg = tpiovn * bitrev(ii, m); |
||||||
|
c1 = cosf(arg); |
||||||
|
s1 = -sinf(arg); |
||||||
|
c2 = c1 * c1 - s1 * s1; |
||||||
|
s2 = c1 * s1 + c1 * s1; |
||||||
|
c3 = c1 * c2 - s1 * s2; |
||||||
|
s3 = c2 * s1 + s2 * c1; |
||||||
|
j4 = jj * 4; |
||||||
|
j0 = jr * j4; |
||||||
|
k0 = ji * j4; |
||||||
|
jlast = j0 + jj; |
||||||
|
for (j = j0; j < jlast; j++) { |
||||||
|
k = k0 + j - j0; |
||||||
|
t0 = x0[j] + x6[k]; |
||||||
|
t1 = x7[k] - x1[j]; |
||||||
|
t2 = x0[j] - x6[k]; |
||||||
|
t3 = x7[k] + x1[j]; |
||||||
|
t4 = x2[j] + x4[k]; |
||||||
|
t5 = x5[k] - x3[j]; |
||||||
|
t6 = x5[k] + x3[j]; |
||||||
|
t7 = x4[k] - x2[j]; |
||||||
|
x0[j] = t0 + t4; |
||||||
|
x4[k] = t1 + t5; |
||||||
|
x1[j] = (t2 + t6) * c1 - (t3 + t7) * s1; |
||||||
|
x5[k] = (t2 + t6) * s1 + (t3 + t7) * c1; |
||||||
|
x2[j] = (t0 - t4) * c2 - (t1 - t5) * s2; |
||||||
|
x6[k] = (t0 - t4) * s2 + (t1 - t5) * c2; |
||||||
|
x3[j] = (t2 - t6) * c3 - (t3 - t7) * s3; |
||||||
|
x7[k] = (t2 - t6) * s3 + (t3 - t7) * c3; |
||||||
|
} |
||||||
|
jr += 2; |
||||||
|
ji -= 2; |
||||||
|
if (ji <= jl) { |
||||||
|
ji = 2 * jr - 1; |
||||||
|
jl = jr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
|
||||||
|
static int |
||||||
|
crfft2(float *x, int m) |
||||||
|
{ |
||||||
|
int n, i, it, nn, jj, m2; |
||||||
|
|
||||||
|
n = 1 << m; |
||||||
|
x[1] = x[n]; |
||||||
|
m2 = m / 2; |
||||||
|
|
||||||
|
// re-order
|
||||||
|
|
||||||
|
for (i = 3; i < n; i += 2) |
||||||
|
x[i] = -x[i]; |
||||||
|
reorder2(m, x); |
||||||
|
reorder1(m, x); |
||||||
|
|
||||||
|
// radix 4
|
||||||
|
|
||||||
|
if (m2 != 0) { |
||||||
|
nn = 4 * n; |
||||||
|
for (it = 0; it < m2; it++) { |
||||||
|
nn = nn / 4; |
||||||
|
jj = n / nn; |
||||||
|
crrad4(jj, nn, x, x + jj, x + 2 * jj, x + 3 * jj, |
||||||
|
x, x + jj, x + 2 * jj, x + 3 * jj); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// radix 2
|
||||||
|
|
||||||
|
if (m > m2 * 2) { |
||||||
|
jj = n / 2; |
||||||
|
rad2(jj, x, x + jj); |
||||||
|
} |
||||||
|
|
||||||
|
return (0); |
||||||
|
} |
||||||
|
|
||||||
|
/***********************************************************/ |
||||||
|
|
||||||
|
// real-to-complex FFT
|
||||||
|
|
||||||
|
//FUNC(void)
|
||||||
|
void cha_fft_rc(float *x, int n) |
||||||
|
{ |
||||||
|
int m; |
||||||
|
|
||||||
|
// assume n is a power of two
|
||||||
|
m = ilog2(n); |
||||||
|
rcfft2(x, m); |
||||||
|
} |
||||||
|
|
||||||
|
// complex-to-real inverse FFT
|
||||||
|
|
||||||
|
//FUNC(void)
|
||||||
|
void cha_fft_cr(float *x, int n) |
||||||
|
{ |
||||||
|
int i, m; |
||||||
|
|
||||||
|
// assume n is a power of two
|
||||||
|
m = ilog2(n); |
||||||
|
crfft2(x, m); |
||||||
|
// scale inverse by 1/n
|
||||||
|
for (i = 0; i < n; i++) { |
||||||
|
x[i] /= n; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//endif
|
@ -0,0 +1,19 @@ |
|||||||
|
|
||||||
|
#ifndef _BTNRH_FFT_H |
||||||
|
#define _BTNRH_FFT_H |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
//#include "chapro.h"
|
||||||
|
//#include "cha_ff.h"
|
||||||
|
|
||||||
|
/***********************************************************/ |
||||||
|
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
|
||||||
|
// In IEEE Acoustics, Speech, and Signal Processing Society.
|
||||||
|
// "Programs for Digital Signal Processing," IEEE Press, New York,
|
||||||
|
|
||||||
|
namespace BTNRH_FFT { |
||||||
|
void cha_fft_cr(float *, int); |
||||||
|
void cha_fft_rc(float *, int); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -1,384 +0,0 @@ |
|||||||
|
|
||||||
#include <math.h> |
|
||||||
//#include "chapro.h"
|
|
||||||
//#include "cha_ff.h"
|
|
||||||
|
|
||||||
/***********************************************************/ |
|
||||||
// FFT functions adapted from G. D. Bergland, "Subroutines FAST and FSST," (1979).
|
|
||||||
// In IEEE Acoustics, Speech, and Signal Processing Society.
|
|
||||||
// "Programs for Digital Signal Processing," IEEE Press, New York,
|
|
||||||
|
|
||||||
static __inline int |
|
||||||
ilog2(int n) |
|
||||||
{ |
|
||||||
int m; |
|
||||||
|
|
||||||
for (m = 1; m < 32; m++) |
|
||||||
if (n == (1 << m)) |
|
||||||
return (m); |
|
||||||
return (-1); |
|
||||||
} |
|
||||||
|
|
||||||
static __inline int |
|
||||||
bitrev(int ii, int m) |
|
||||||
{ |
|
||||||
register int jj; |
|
||||||
|
|
||||||
jj = ii & 1; |
|
||||||
--m; |
|
||||||
while (--m > 0) { |
|
||||||
ii >>= 1; |
|
||||||
jj <<= 1; |
|
||||||
jj |= ii & 1; |
|
||||||
} |
|
||||||
return (jj); |
|
||||||
} |
|
||||||
|
|
||||||
static __inline void |
|
||||||
rad2(int ii, float *x0, float *x1) |
|
||||||
{ |
|
||||||
int k; |
|
||||||
float t; |
|
||||||
|
|
||||||
for (k = 0; k < ii; k++) { |
|
||||||
t = x0[k] + x1[k]; |
|
||||||
x1[k] = x0[k] - x1[k]; |
|
||||||
x0[k] = t; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static __inline void |
|
||||||
reorder1(int m, float *x) |
|
||||||
{ |
|
||||||
int j, k, kl, n; |
|
||||||
float t; |
|
||||||
|
|
||||||
k = 4; |
|
||||||
kl = 2; |
|
||||||
n = 1 << m; |
|
||||||
for (j = 4; j <= n; j += 2) { |
|
||||||
if (k > j) { |
|
||||||
t = x[j - 1]; |
|
||||||
x[j - 1] = x[k - 1]; |
|
||||||
x[k - 1] = t; |
|
||||||
} |
|
||||||
k -= 2; |
|
||||||
if (k <= kl) { |
|
||||||
k = 2 * j; |
|
||||||
kl = j; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static __inline void |
|
||||||
reorder2(int m, float *x) |
|
||||||
{ |
|
||||||
int ji, ij, n; |
|
||||||
float t; |
|
||||||
|
|
||||||
n = 1 << m; |
|
||||||
for (ij = 0; ij <= (n - 2); ij += 2) { |
|
||||||
ji = bitrev(ij >> 1, m) << 1; |
|
||||||
if (ij < ji) { |
|
||||||
t = x[ij]; |
|
||||||
x[ij] = x[ji]; |
|
||||||
x[ji] = t; |
|
||||||
t = x[ij + 1]; |
|
||||||
x[ij + 1] = x[ji + 1]; |
|
||||||
x[ji + 1] = t; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/***********************************************************/ |
|
||||||
|
|
||||||
// rcfft
|
|
||||||
|
|
||||||
static void |
|
||||||
rcrad4(int ii, int nn, |
|
||||||
float *x0, float *x1, float *x2, float *x3, |
|
||||||
float *x4, float *x5, float *x6, float *x7) |
|
||||||
{ |
|
||||||
double arg, tpiovn; |
|
||||||
float c1, c2, c3, s1, s2, s3, pr, pi, r1, r5; |
|
||||||
float t0, t1, t2, t3, t4, t5, t6, t7; |
|
||||||
int i0, i4, j, j0, ji, jl, jr, jlast, k, k0, kl, m, n, ni; |
|
||||||
|
|
||||||
n = nn / 4; |
|
||||||
for (m = 1; (1 << m) < n; m++) |
|
||||||
continue; |
|
||||||
tpiovn = 2 * M_PI / nn; |
|
||||||
ji = 3; |
|
||||||
jl = 2; |
|
||||||
jr = 2; |
|
||||||
ni = (n + 1) / 2; |
|
||||||
for (i0 = 0; i0 < ni; i0++) { |
|
||||||
if (i0 == 0) { |
|
||||||
for (k = 0; k < ii; k++) { |
|
||||||
t0 = x0[k] + x2[k]; |
|
||||||
t1 = x1[k] + x3[k]; |
|
||||||
x2[k] = x0[k] - x2[k]; |
|
||||||
x3[k] = x1[k] - x3[k]; |
|
||||||
x0[k] = t0 + t1; |
|
||||||
x1[k] = t0 - t1; |
|
||||||
} |
|
||||||
if (nn > 4) { |
|
||||||
k0 = ii * 4; |
|
||||||
kl = k0 + ii; |
|
||||||
for (k = k0; k < kl; k++) { |
|
||||||
pr = (float) (M_SQRT1_2 * (x1[k] - x3[k])); |
|
||||||
pi = (float) (M_SQRT1_2 * (x1[k] + x3[k])); |
|
||||||
x3[k] = x2[k] + pi; |
|
||||||
x1[k] = pi - x2[k]; |
|
||||||
x2[k] = x0[k] - pr; |
|
||||||
x0[k] += pr; |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
arg = tpiovn * bitrev(i0, m); |
|
||||||
c1 = cosf(arg); |
|
||||||
s1 = sinf(arg); |
|
||||||
c2 = c1 * c1 - s1 * s1; |
|
||||||
s2 = c1 * s1 + c1 * s1; |
|
||||||
c3 = c1 * c2 - s1 * s2; |
|
||||||
s3 = c2 * s1 + s2 * c1; |
|
||||||
i4 = ii * 4; |
|
||||||
j0 = jr * i4; |
|
||||||
k0 = ji * i4; |
|
||||||
jlast = j0 + ii; |
|
||||||
for (j = j0; j < jlast; j++) { |
|
||||||
k = k0 + j - j0; |
|
||||||
r1 = x1[j] * c1 - x5[k] * s1; |
|
||||||
r5 = x1[j] * s1 + x5[k] * c1; |
|
||||||
t2 = x2[j] * c2 - x6[k] * s2; |
|
||||||
t6 = x2[j] * s2 + x6[k] * c2; |
|
||||||
t3 = x3[j] * c3 - x7[k] * s3; |
|
||||||
t7 = x3[j] * s3 + x7[k] * c3; |
|
||||||
t0 = x0[j] + t2; |
|
||||||
t4 = x4[k] + t6; |
|
||||||
t2 = x0[j] - t2; |
|
||||||
t6 = x4[k] - t6; |
|
||||||
t1 = r1 + t3; |
|
||||||
t5 = r5 + t7; |
|
||||||
t3 = r1 - t3; |
|
||||||
t7 = r5 - t7; |
|
||||||
x0[j] = t0 + t1; |
|
||||||
x7[k] = t4 + t5; |
|
||||||
x6[k] = t0 - t1; |
|
||||||
x1[j] = t5 - t4; |
|
||||||
x2[j] = t2 - t7; |
|
||||||
x5[k] = t6 + t3; |
|
||||||
x4[k] = t2 + t7; |
|
||||||
x3[j] = t3 - t6; |
|
||||||
} |
|
||||||
jr += 2; |
|
||||||
ji -= 2; |
|
||||||
if (ji <= jl) { |
|
||||||
ji = 2 * jr - 1; |
|
||||||
jl = jr; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//-----------------------------------------------------------
|
|
||||||
|
|
||||||
static int |
|
||||||
rcfft2(float *x, int m) |
|
||||||
{ |
|
||||||
int ii, nn, m2, it, n; |
|
||||||
|
|
||||||
n = 1 << m;; |
|
||||||
m2 = m / 2; |
|
||||||
|
|
||||||
// radix 2
|
|
||||||
|
|
||||||
if (m <= m2 * 2) { |
|
||||||
nn = 1; |
|
||||||
} else { |
|
||||||
nn = 2; |
|
||||||
ii = n / nn; |
|
||||||
rad2(ii, x, x + ii); |
|
||||||
} |
|
||||||
|
|
||||||
// radix 4
|
|
||||||
|
|
||||||
if (m2 != 0) { |
|
||||||
for (it = 0; it < m2; it++) { |
|
||||||
nn = nn * 4; |
|
||||||
ii = n / nn; |
|
||||||
rcrad4(ii, nn, x, x + ii, x + 2 * ii, x + 3 * ii, |
|
||||||
x, x + ii, x + 2 * ii, x + 3 * ii); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// re-order
|
|
||||||
|
|
||||||
reorder1(m, x); |
|
||||||
reorder2(m, x); |
|
||||||
for (it = 3; it < n; it += 2) |
|
||||||
x[it] = -x[it]; |
|
||||||
x[n] = x[1]; |
|
||||||
x[1] = 0.0; |
|
||||||
x[n + 1] = 0.0; |
|
||||||
|
|
||||||
return (0); |
|
||||||
} |
|
||||||
|
|
||||||
/***********************************************************/ |
|
||||||
|
|
||||||
// rcfft
|
|
||||||
|
|
||||||
static void |
|
||||||
crrad4(int jj, int nn, |
|
||||||
float *x0, float *x1, float *x2, float *x3, |
|
||||||
float *x4, float *x5, float *x6, float *x7) |
|
||||||
{ |
|
||||||
double arg, tpiovn; |
|
||||||
float c1, c2, c3, s1, s2, s3; |
|
||||||
float t0, t1, t2, t3, t4, t5, t6, t7; |
|
||||||
int ii, j, j0, ji, jr, jl, jlast, j4, k, k0, kl, m, n, ni; |
|
||||||
|
|
||||||
tpiovn = 2 * M_PI / nn; |
|
||||||
ji = 3; |
|
||||||
jl = 2; |
|
||||||
jr = 2; |
|
||||||
n = nn / 4; |
|
||||||
for (m = 1; (1 << m) < n; m++) |
|
||||||
continue; |
|
||||||
ni = (n + 1) / 2; |
|
||||||
for (ii = 0; ii < ni; ii++) { |
|
||||||
if (ii == 0) { |
|
||||||
for (k = 0; k < jj; k++) { |
|
||||||
t0 = x0[k] + x1[k]; |
|
||||||
t1 = x0[k] - x1[k]; |
|
||||||
t2 = x2[k] * 2; |
|
||||||
t3 = x3[k] * 2; |
|
||||||
x0[k] = t0 + t2; |
|
||||||
x2[k] = t0 - t2; |
|
||||||
x1[k] = t1 + t3; |
|
||||||
x3[k] = t1 - t3; |
|
||||||
} |
|
||||||
if (nn > 4) { |
|
||||||
k0 = jj * 4; |
|
||||||
kl = k0 + jj; |
|
||||||
for (k = k0; k < kl; k++) { |
|
||||||
t2 = x0[k] - x2[k]; |
|
||||||
t3 = x1[k] + x3[k]; |
|
||||||
x0[k] = (x0[k] + x2[k]) * 2; |
|
||||||
x2[k] = (x3[k] - x1[k]) * 2; |
|
||||||
x1[k] = (float) ((t2 + t3) * M_SQRT2); |
|
||||||
x3[k] = (float) ((t3 - t2) * M_SQRT2); |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
arg = tpiovn * bitrev(ii, m); |
|
||||||
c1 = cosf(arg); |
|
||||||
s1 = -sinf(arg); |
|
||||||
c2 = c1 * c1 - s1 * s1; |
|
||||||
s2 = c1 * s1 + c1 * s1; |
|
||||||
c3 = c1 * c2 - s1 * s2; |
|
||||||
s3 = c2 * s1 + s2 * c1; |
|
||||||
j4 = jj * 4; |
|
||||||
j0 = jr * j4; |
|
||||||
k0 = ji * j4; |
|
||||||
jlast = j0 + jj; |
|
||||||
for (j = j0; j < jlast; j++) { |
|
||||||
k = k0 + j - j0; |
|
||||||
t0 = x0[j] + x6[k]; |
|
||||||
t1 = x7[k] - x1[j]; |
|
||||||
t2 = x0[j] - x6[k]; |
|
||||||
t3 = x7[k] + x1[j]; |
|
||||||
t4 = x2[j] + x4[k]; |
|
||||||
t5 = x5[k] - x3[j]; |
|
||||||
t6 = x5[k] + x3[j]; |
|
||||||
t7 = x4[k] - x2[j]; |
|
||||||
x0[j] = t0 + t4; |
|
||||||
x4[k] = t1 + t5; |
|
||||||
x1[j] = (t2 + t6) * c1 - (t3 + t7) * s1; |
|
||||||
x5[k] = (t2 + t6) * s1 + (t3 + t7) * c1; |
|
||||||
x2[j] = (t0 - t4) * c2 - (t1 - t5) * s2; |
|
||||||
x6[k] = (t0 - t4) * s2 + (t1 - t5) * c2; |
|
||||||
x3[j] = (t2 - t6) * c3 - (t3 - t7) * s3; |
|
||||||
x7[k] = (t2 - t6) * s3 + (t3 - t7) * c3; |
|
||||||
} |
|
||||||
jr += 2; |
|
||||||
ji -= 2; |
|
||||||
if (ji <= jl) { |
|
||||||
ji = 2 * jr - 1; |
|
||||||
jl = jr; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//-----------------------------------------------------------
|
|
||||||
|
|
||||||
static int |
|
||||||
crfft2(float *x, int m) |
|
||||||
{ |
|
||||||
int n, i, it, nn, jj, m2; |
|
||||||
|
|
||||||
n = 1 << m; |
|
||||||
x[1] = x[n]; |
|
||||||
m2 = m / 2; |
|
||||||
|
|
||||||
// re-order
|
|
||||||
|
|
||||||
for (i = 3; i < n; i += 2) |
|
||||||
x[i] = -x[i]; |
|
||||||
reorder2(m, x); |
|
||||||
reorder1(m, x); |
|
||||||
|
|
||||||
// radix 4
|
|
||||||
|
|
||||||
if (m2 != 0) { |
|
||||||
nn = 4 * n; |
|
||||||
for (it = 0; it < m2; it++) { |
|
||||||
nn = nn / 4; |
|
||||||
jj = n / nn; |
|
||||||
crrad4(jj, nn, x, x + jj, x + 2 * jj, x + 3 * jj, |
|
||||||
x, x + jj, x + 2 * jj, x + 3 * jj); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// radix 2
|
|
||||||
|
|
||||||
if (m > m2 * 2) { |
|
||||||
jj = n / 2; |
|
||||||
rad2(jj, x, x + jj); |
|
||||||
} |
|
||||||
|
|
||||||
return (0); |
|
||||||
} |
|
||||||
|
|
||||||
/***********************************************************/ |
|
||||||
|
|
||||||
// real-to-complex FFT
|
|
||||||
|
|
||||||
//FUNC(void)
|
|
||||||
void cha_fft_rc(float *x, int n) |
|
||||||
{ |
|
||||||
int m; |
|
||||||
|
|
||||||
// assume n is a power of two
|
|
||||||
m = ilog2(n); |
|
||||||
rcfft2(x, m); |
|
||||||
} |
|
||||||
|
|
||||||
// complex-to-real inverse FFT
|
|
||||||
|
|
||||||
//FUNC(void)
|
|
||||||
void cha_fft_cr(float *x, int n) |
|
||||||
{ |
|
||||||
int i, m; |
|
||||||
|
|
||||||
// assume n is a power of two
|
|
||||||
m = ilog2(n); |
|
||||||
crfft2(x, m); |
|
||||||
// scale inverse by 1/n
|
|
||||||
for (i = 0; i < n; i++) { |
|
||||||
x[i] /= n; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
Loading…
Reference in new issue