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