Skip to content

Commit d44d0f5

Browse files
committed
FFT: new stride logic
1 parent d9b9399 commit d44d0f5

File tree

1 file changed

+71
-60
lines changed

1 file changed

+71
-60
lines changed

src/AudioTools/AudioLibs/AudioFFT.h

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ struct AudioFFTConfig : public AudioInfo {
4949
uint8_t channel_used = 0;
5050
int length = 8192;
5151
int stride = 0;
52-
/// Optional window function
52+
/// Optional window function for both fft and ifft
5353
WindowFunction *window_function = nullptr;
54+
/// Optional window function for fft only
55+
WindowFunction *window_function_fft = nullptr;
56+
/// Optional window function for ifft only
57+
WindowFunction *window_function_ifft = nullptr;
5458
/// TX_MODE = FFT, RX_MODE = IFFT
5559
RxTxMode rxtx_mode = TX_MODE;
5660
};
@@ -96,18 +100,19 @@ class FFTInverseOverlapAdder {
96100
// adds the values to the array (by applying the window function)
97101
void add(float value, int pos, WindowFunction *window_function) {
98102
float add_value = value;
99-
if (window_function != nullptr)
103+
if (window_function != nullptr) {
100104
add_value = value * window_function->factor(pos);
105+
}
101106
data[pos] += add_value;
102107
}
103108

104109
// gets the scaled audio data as result
105-
void getStepData(float *result, int step_size, float maxResult) {
106-
for (int j = 0; j < step_size; j++) {
110+
void getStepData(float *result, int stride, float maxResult) {
111+
for (int j = 0; j < stride; j++) {
107112
// determine max value to scale
108-
if (rfft_max < data[j]) rfft_max = data[j];
113+
if (data[j] > rfft_max) rfft_max = data[j];
109114
}
110-
for (int j = 0; j < step_size; j++) {
115+
for (int j = 0; j < stride; j++) {
111116
result[j] = data[j] / rfft_max * maxResult;
112117
// clip
113118
if (result[j] > maxResult) {
@@ -118,17 +123,21 @@ class FFTInverseOverlapAdder {
118123
}
119124
}
120125
// copy data to head
121-
for (int j = 0; j < len - step_size; j++) {
122-
data[j] = data[j + step_size];
126+
for (int j = 0; j < len - stride; j++) {
127+
data[j] = data[j + stride];
123128
}
124129
// clear tail
125-
for (int j = len - step_size; j < len; j++) {
130+
for (int j = len - stride; j < len; j++) {
126131
data[j] = 0.0;
127132
}
128133
}
129134

130135
int size() { return data.size(); }
131136

137+
void begin(){
138+
rfft_max = 0;
139+
}
140+
132141
protected:
133142
Vector<float> data{0};
134143
int len = 0;
@@ -200,24 +209,32 @@ class AudioFFTBase : public AudioStream {
200209
/// starts the processing
201210
bool begin() override {
202211
bins = cfg.length / 2;
212+
// define window functions
213+
if (cfg.window_function_fft==nullptr) cfg.window_function_fft = cfg.window_function;
214+
if (cfg.window_function_ifft==nullptr) cfg.window_function_ifft = cfg.window_function;
215+
// define default stride value if not defined
216+
if (cfg.stride == 0) cfg.stride = cfg.length;
217+
203218
if (!isPowerOfTwo(cfg.length)) {
204219
LOGE("Len must be of the power of 2: %d", cfg.length);
205220
return false;
206221
}
207222
if (!p_driver->begin(cfg.length)) {
208223
LOGE("Not enough memory");
209224
}
210-
int step_size = cfg.stride > 0 ? cfg.stride : cfg.length;
211-
if (cfg.window_function != nullptr) {
212-
cfg.window_function->begin(step_size);
225+
226+
if (cfg.window_function_fft != nullptr) {
227+
cfg.window_function_fft->begin(cfg.length);
228+
}
229+
if (cfg.window_function_ifft != nullptr) {
230+
cfg.window_function_ifft->begin(cfg.length);
213231
}
214232

233+
int step_size = cfg.stride > 0 ? cfg.stride : cfg.length;
215234
bool is_valid_rxtx = false;
216235
if (cfg.rxtx_mode == TX_MODE || cfg.rxtx_mode == RXTX_MODE) {
217-
if (cfg.stride > 0 && cfg.stride < cfg.length) {
218-
// holds last N bytes that need to be reprocessed
219-
stride_buffer.resize((cfg.length - cfg.stride) * bytesPerSample());
220-
}
236+
// holds last N bytes that need to be reprocessed
237+
stride_buffer.resize((cfg.length) * bytesPerSample());
221238
is_valid_rxtx = true;
222239
}
223240
if (cfg.rxtx_mode == RX_MODE || cfg.rxtx_mode == RXTX_MODE) {
@@ -238,8 +255,11 @@ class AudioFFTBase : public AudioStream {
238255
/// Just resets the current_pos e.g. to start a new cycle
239256
void reset() {
240257
current_pos = 0;
241-
if (cfg.window_function != nullptr) {
242-
cfg.window_function->begin(length());
258+
if (cfg.window_function_fft != nullptr) {
259+
cfg.window_function_fft->begin(cfg.length);
260+
}
261+
if (cfg.window_function_ifft != nullptr) {
262+
cfg.window_function_ifft->begin(cfg.length);
243263
}
244264
}
245265

@@ -290,7 +310,7 @@ class AudioFFTBase : public AudioStream {
290310

291311
/// Provides the result of a reverse FFT
292312
size_t readBytes(uint8_t *data, size_t len) override {
293-
LOGD("setup ifft data");
313+
TRACED();
294314
if (rfft_data.size() == 0) return 0;
295315
// execute rfft when we consumed all data
296316
if (has_rfft_data && rfft_data.available() == 0) {
@@ -432,7 +452,7 @@ class AudioFFTBase : public AudioStream {
432452
AudioFFTConfig cfg;
433453
unsigned long timestamp_begin = 0l;
434454
unsigned long timestamp = 0l;
435-
RingBuffer<uint8_t> stride_buffer{0};
455+
SingleBuffer<uint8_t> stride_buffer{0};
436456
Vector<float> l_magnitudes{0};
437457
Vector<float> step_data{0};
438458
int bins = 0;
@@ -445,35 +465,35 @@ class AudioFFTBase : public AudioStream {
445465
void processSamples(const void *data, size_t samples) {
446466
T *dataT = (T *)data;
447467
T sample;
448-
float sample_windowed;
449468
for (int j = 0; j < samples; j += cfg.channels) {
450469
sample = dataT[j + cfg.channel_used];
451-
p_driver->setValue(current_pos, windowedSample(sample, current_pos));
452-
writeStrideBuffer((uint8_t *)&sample, sizeof(T));
453-
if (++current_pos >= cfg.length) {
454-
fft<T>();
455-
current_pos = 0;
456-
457-
// reprocess data in stride buffer
458-
if (stride_buffer.size() > 0) {
459-
// reload data from stride buffer
460-
while (stride_buffer.available()) {
461-
T sample;
462-
stride_buffer.readArray((uint8_t *)&sample, sizeof(T));
463-
p_driver->setValue(current_pos,
464-
windowedSample(sample, current_pos));
465-
current_pos++;
466-
}
470+
if (writeStrideBuffer((uint8_t *)&sample, sizeof(T))){
471+
// process data if buffer is full
472+
T* samples = (T*) stride_buffer.data();
473+
int sample_count = stride_buffer.size() / sizeof(T);
474+
assert(sample_count == cfg.length);
475+
for (int j=0; j< sample_count; j++){
476+
T out_sample = samples[j];
477+
p_driver->setValue(j, windowedSample(out_sample, j));
467478
}
479+
480+
fft<T>();
481+
482+
// remove stride samples
483+
stride_buffer.clearArray(cfg.stride * sizeof(T));
484+
485+
// validate available data in stride buffer
486+
if (cfg.stride == cfg.length) assert(stride_buffer.available()==0);
487+
468488
}
469489
}
470490
}
471491

472492
template <typename T>
473493
T windowedSample(T sample, int pos) {
474494
T result = sample;
475-
if (cfg.window_function != nullptr) {
476-
result = cfg.window_function->factor(pos) * sample;
495+
if (cfg.window_function_fft != nullptr) {
496+
result = cfg.window_function_fft->factor(pos) * sample;
477497
}
478498
return result;
479499
}
@@ -498,35 +518,34 @@ class AudioFFTBase : public AudioStream {
498518
// add data to sum buffer
499519
for (int j = 0; j < cfg.length; j++) {
500520
float value = p_driver->getValue(j);
501-
rfft_add.add(value, j, cfg.window_function);
521+
rfft_add.add(value, j, cfg.window_function_ifft);
502522
}
503523
// get result data from sum buffer
504524
rfftWriteData(rfft_data);
505525
}
506526

507527
/// write reverse fft result to buffer to make it available for readBytes
508528
void rfftWriteData(BaseBuffer<uint8_t> &data) {
509-
int step_size = cfg.stride > 0 ? cfg.stride : cfg.length;
510529
// get data to result buffer
511-
step_data.resize(step_size);
512-
for (int j = 0; j < step_size; j++) {
530+
step_data.resize(cfg.stride);
531+
for (int j = 0; j < cfg.stride; j++) {
513532
step_data[j] = 0.0;
514533
}
515-
rfft_add.getStepData(step_data.data(), step_size,
534+
rfft_add.getStepData(step_data.data(), cfg.stride,
516535
NumberConverter::maxValue(cfg.bits_per_sample));
517536

518537
switch (cfg.bits_per_sample) {
519538
case 8:
520-
writeIFFT<int8_t>(step_data.data(), step_size);
539+
writeIFFT<int8_t>(step_data.data(), cfg.stride);
521540
break;
522541
case 16:
523-
writeIFFT<int16_t>(step_data.data(), step_size);
542+
writeIFFT<int16_t>(step_data.data(), cfg.stride);
524543
break;
525544
case 24:
526-
writeIFFT<int24_t>(step_data.data(), step_size);
545+
writeIFFT<int24_t>(step_data.data(), cfg.stride);
527546
break;
528547
case 32:
529-
writeIFFT<int32_t>(step_data.data(), step_size);
548+
writeIFFT<int32_t>(step_data.data(), cfg.stride);
530549
break;
531550
default:
532551
LOGE("Unsupported bits: %d", cfg.bits_per_sample);
@@ -567,18 +586,10 @@ class AudioFFTBase : public AudioStream {
567586
}
568587
}
569588

570-
void writeStrideBuffer(uint8_t *buffer, size_t len) {
571-
if (stride_buffer.size() > 0) {
572-
int available = stride_buffer.availableForWrite();
573-
if (len > available) {
574-
// clear oldest values to make space
575-
int diff = len - available;
576-
for (int j = 0; j < diff; j++) {
577-
stride_buffer.read();
578-
}
579-
}
580-
stride_buffer.writeArray(buffer, len);
581-
}
589+
// adds samples to stride buffer, returns true if the buffer is full
590+
bool writeStrideBuffer(uint8_t *buffer, size_t len) {
591+
stride_buffer.writeArray(buffer, len);
592+
return stride_buffer.isFull();
582593
}
583594

584595
bool isPowerOfTwo(uint16_t x) { return (x & (x - 1)) == 0; }

0 commit comments

Comments
 (0)