arduino-audio-tools
Loading...
Searching...
No Matches
AudioFFT.h
1#pragma once
2
4#include "AudioTools/CoreAudio/AudioStreams.h"
5#include "AudioTools/CoreAudio/MusicalNotes.h"
6
13namespace audio_tools {
14
15// forward declaration
16class AudioFFTBase;
17static MusicalNotes AudioFFTNotes;
18
24 int bin = 0;
25 float magnitude = 0.0f;
26 float frequency = 0.0f;
27
28 int frequencyAsInt() { return round(frequency); }
29 const char *frequencyAsNote() { return AudioFFTNotes.note(frequency); }
30 const char *frequencyAsNote(float &diff) {
31 return AudioFFTNotes.note(frequency, diff);
32 }
33};
34
40struct AudioFFTConfig : public AudioInfo {
42 channels = 2;
43 bits_per_sample = 16;
44 sample_rate = 44100;
45 }
47 void (*callback)(AudioFFTBase &fft) = nullptr;
49 uint8_t channel_used = 0;
50 int length = 8192;
51 int stride = 0;
61 void *ref = nullptr;
62};
63
65struct FFTBin {
66 float real;
67 float img;
68
69 FFTBin() = default;
70
71 FFTBin(float r, float i) {
72 real = r;
73 img = i;
74 }
75
76 void multiply(float f) {
77 real *= f;
78 img *= f;
79 }
80
81 void conjugate() { img = -img; }
82
83 void clear() { real = img = 0.0f; }
84};
85
88 public:
90 if (size > 0) resize(size);
91 }
92
94 void resize(int size) {
95 // reset max for new scaling
96 rfft_max = 0.0;
97 // define new size
98 len = size;
99 data.resize(size);
100 for (int j = 0; j < data.size(); j++) {
101 data[j] = 0.0;
102 }
103 }
104
105 // adds the values to the array (by applying the window function)
106 void add(float value, int pos, WindowFunction *window_function) {
107 float add_value = value;
108 if (window_function != nullptr) {
109 add_value = value * window_function->factor(pos);
110 }
111 assert(pos < len);
112 data[pos] += add_value;
113 }
114
115 // gets the scaled audio data as result
116 void getStepData(float *result, int stride, float maxResult) {
117 for (int j = 0; j < stride; j++) {
118 // determine max value to scale
119 if (data[j] > rfft_max) rfft_max = data[j];
120 }
121 for (int j = 0; j < stride; j++) {
122 result[j] = data[j] / rfft_max * maxResult;
123 // clip
124 if (result[j] > maxResult) {
125 result[j] = maxResult;
126 }
127 if (result[j] < -maxResult) {
128 result[j] = -maxResult;
129 }
130 }
131 // copy data to head
132 for (int j = 0; j < len - stride; j++) {
133 data[j] = data[j + stride];
134 }
135 // clear tail
136 for (int j = len - stride; j < len; j++) {
137 data[j] = 0.0;
138 }
139 }
140
142 int size() { return data.size(); }
143
144 protected:
145 Vector<float> data{0};
146 int len = 0;
147 float rfft_max = 0;
148};
149
157 public:
158 virtual bool begin(int len) = 0;
159 virtual void end() = 0;
161 virtual void setValue(int pos, float value) = 0;
163 virtual void fft() = 0;
165 virtual float magnitude(int idx) = 0;
167 virtual float magnitudeFast(int idx) = 0;
168 virtual bool isValid() = 0;
170 virtual bool isReverseFFT() { return false; }
172 virtual void rfft() { LOGE("Not implemented"); }
174 virtual float getValue(int pos) = 0;
176 virtual bool setBin(int idx, float real, float img) { return false; }
178 bool setBin(int pos, FFTBin &bin) { return setBin(pos, bin.real, bin.img); }
180 virtual bool getBin(int pos, FFTBin &bin) { return false; }
181};
182
191class AudioFFTBase : public AudioStream {
192 public:
196
197 ~AudioFFTBase() { end(); }
198
201 AudioFFTConfig info;
202 info.rxtx_mode = mode;
203 return info;
204 }
205
208 cfg = info;
209 return begin();
210 }
211
213 bool begin() override {
214 bins = cfg.length / 2;
215 // define window functions
216 if (cfg.window_function_fft == nullptr)
218 if (cfg.window_function_ifft == nullptr)
220 // define default stride value if not defined
221 if (cfg.stride == 0) cfg.stride = cfg.length;
222
223 if (!isPowerOfTwo(cfg.length)) {
224 LOGE("Len must be of the power of 2: %d", cfg.length);
225 return false;
226 }
227 if (!p_driver->begin(cfg.length)) {
228 LOGE("Not enough memory");
229 }
230
231 if (cfg.window_function_fft != nullptr) {
232 cfg.window_function_fft->begin(cfg.length);
233 }
234 if (cfg.window_function_ifft != nullptr &&
236 cfg.window_function_ifft->begin(cfg.length);
237 }
238
239 bool is_valid_rxtx = false;
240 if (cfg.rxtx_mode == TX_MODE || cfg.rxtx_mode == RXTX_MODE) {
241 // holds last N bytes that need to be reprocessed
242 stride_buffer.resize((cfg.length) * bytesPerSample());
243 is_valid_rxtx = true;
244 }
245 if (cfg.rxtx_mode == RX_MODE || cfg.rxtx_mode == RXTX_MODE) {
246 rfft_data.resize(cfg.channels * bytesPerSample() * cfg.stride);
247 rfft_add.resize(cfg.length);
248 step_data.resize(cfg.stride);
249 is_valid_rxtx = true;
250 }
251
252 if (!is_valid_rxtx) {
253 LOGE("Invalid rxtx_mode");
254 return false;
255 }
256
257 current_pos = 0;
258 return p_driver->isValid();
259 }
260
262 void reset() {
263 current_pos = 0;
264 if (cfg.window_function_fft != nullptr) {
265 cfg.window_function_fft->begin(cfg.length);
266 }
267 if (cfg.window_function_ifft != nullptr) {
268 cfg.window_function_ifft->begin(cfg.length);
269 }
270 }
271
272 operator bool() override {
273 return p_driver != nullptr && p_driver->isValid();
274 }
275
277 void setAudioInfo(AudioInfo info) override {
279 cfg.sample_rate = info.sample_rate;
280 cfg.channels = info.channels;
281 begin(cfg);
282 }
283
285 void end() override {
286 p_driver->end();
287 l_magnitudes.resize(0);
288 rfft_data.resize(0);
289 rfft_add.resize(0);
290 step_data.resize(0);
291 }
292
294 size_t write(const uint8_t *data, size_t len) override {
295 size_t result = 0;
296 if (p_driver->isValid()) {
297 result = len;
298 switch (cfg.bits_per_sample) {
299 case 8:
300 processSamples<int8_t>(data, len);
301 break;
302 case 16:
303 processSamples<int16_t>(data, len / 2);
304 break;
305 case 24:
306 processSamples<int24_t>(data, len / 3);
307 break;
308 case 32:
309 processSamples<int32_t>(data, len / 4);
310 break;
311 default:
312 LOGE("Unsupported bits_per_sample: %d", cfg.bits_per_sample);
313 break;
314 }
315 }
316 return result;
317 }
318
320 size_t readBytes(uint8_t *data, size_t len) override {
321 TRACED();
322 if (rfft_data.size() == 0) return 0;
323
324 // get data via callback if there is no more data
325 if (cfg.rxtx_mode == RX_MODE && cfg.callback != nullptr &&
326 rfft_data.available() == 0) {
327 cfg.callback(*this);
328 }
329
330 // execute rfft when we consumed all data
331 if (has_rfft_data && rfft_data.available() == 0) {
332 rfft();
333 }
334 return rfft_data.readArray(data, len);
335 }
336
338 int availableForWrite() override {
339 return cfg.length * cfg.channels * bytesPerSample();
340 }
341
343 int available() override {
344 assert(cfg.stride != 0);
345 return cfg.stride * cfg.channels * bytesPerSample();
346 }
347
349 int size() { return bins; }
350
352 int length() { return cfg.length; }
353
356 unsigned long resultTime() { return timestamp; }
358 unsigned long resultTimeBegin() { return timestamp_begin; }
359
362 AudioFFTResult ret_value;
363 ret_value.magnitude = 0.0f;
364 ret_value.bin = 0;
365 // find max value and index
366 for (int j = 0; j < size(); j++) {
367 float m = magnitude(j);
368 if (m > ret_value.magnitude) {
369 ret_value.magnitude = m;
370 ret_value.bin = j;
371 }
372 }
373 ret_value.frequency = frequency(ret_value.bin);
374 return ret_value;
375 }
376
378 template <int N>
380 // initialize to negative value
381 for (int j = 0; j < N; j++) {
382 result[j].magnitude = -1000000;
383 }
384 // find top n values
385 AudioFFTResult act;
386 for (int j = 0; j < size(); j++) {
387 act.magnitude = magnitude(j);
388 act.bin = j;
389 act.frequency = frequency(j);
390 insertSorted<N>(result, act);
391 }
392 }
393
395 float *toMEL(int n_bins, float min_freq = 0.0f, float max_freq = 0.0f) {
396 // calculate mel bins
397 if (n_bins <= 0) n_bins = size();
398 if (min_freq <= 0.0f) min_freq = frequency(0);
399 if (max_freq <= 0.0f) max_freq = frequency(size() - 1);
400 mel_bins.resize(n_bins);
401
402 // Convert min and max frequencies to MEL scale
403 float min_mel = 2595.0f * log10(1.0f + (min_freq / 700.0f));
404 float max_mel = 2595.0f * log10(1.0f + (max_freq / 700.0f));
405
406 // Create equally spaced points in the MEL scale
407 Vector<float> mel_points;
408 mel_points.resize(n_bins + 2); // +2 for the endpoints
409
410 float mel_step = (max_mel - min_mel) / (n_bins + 1);
411 for (int i = 0; i < n_bins + 2; i++) {
412 mel_points[i] = min_mel + i * mel_step;
413 }
414
415 // Convert MEL points back to frequency
416 Vector<float> freq_points;
417 freq_points.resize(n_bins + 2);
418 for (int i = 0; i < n_bins + 2; i++) {
419 freq_points[i] = 700.0f * (pow(10.0f, mel_points[i] / 2595.0f) - 1.0f);
420 }
421
422 // Convert frequency points to FFT bin indices
423 Vector<int> bin_indices;
424 bin_indices.resize(n_bins + 2);
425 for (int i = 0; i < n_bins + 2; i++) {
426 bin_indices[i] = round(freq_points[i] * cfg.length / cfg.sample_rate);
427 // Ensure bin index is within valid range
428 if (bin_indices[i] >= bins) bin_indices[i] = bins - 1;
429 if (bin_indices[i] < 0) bin_indices[i] = 0;
430 }
431
432 // Create and apply triangular filters
433 for (int i = 0; i < n_bins; i++) {
434 float mel_sum = 0.0f;
435
436 int start_bin = bin_indices[i];
437 int mid_bin = bin_indices[i + 1];
438 int end_bin = bin_indices[i + 2];
439
440 // Apply first half of triangle filter (ascending)
441 for (int j = start_bin; j < mid_bin; j++) {
442 if (j >= bins) break;
443 float weight = (j - start_bin) / float(mid_bin - start_bin);
444 mel_sum += magnitude(j) * weight;
445 }
446
447 // Apply second half of triangle filter (descending)
448 for (int j = mid_bin; j < end_bin; j++) {
449 if (j >= bins) break;
450 float weight = (end_bin - j) / float(end_bin - mid_bin);
451 mel_sum += magnitude(j) * weight;
452 }
453
454 mel_bins[i] = mel_sum;
455 }
456
457 return mel_bins.data();
458 }
459
467 bool fromMEL(float *values, int n_bins, float min_freq = 0.0f,
468 float max_freq = 0.0f) {
469 if (n_bins <= 0 || values == nullptr) return false;
470
471 // Use default frequency range if not specified
472 if (min_freq <= 0.0f) min_freq = frequency(0);
473 if (max_freq <= 0.0f) max_freq = frequency(size() - 1);
474
475 // Clear the current magnitude array
476 for (int i = 0; i < bins; i++) {
477 FFTBin bin;
478 bin.clear();
479 setBin(i, bin);
480 }
481
482 // Convert min and max frequencies to MEL scale
483 float min_mel = 2595.0f * log10(1.0f + (min_freq / 700.0f));
484 float max_mel = 2595.0f * log10(1.0f + (max_freq / 700.0f));
485
486 // Create equally spaced points in the MEL scale
487 Vector<float> mel_points;
488 mel_points.resize(n_bins + 2); // +2 for the endpoints
489
490 float mel_step = (max_mel - min_mel) / (n_bins + 1);
491 for (int i = 0; i < n_bins + 2; i++) {
492 mel_points[i] = min_mel + i * mel_step;
493 }
494
495 // Convert MEL points back to frequency
496 Vector<float> freq_points;
497 freq_points.resize(n_bins + 2);
498 for (int i = 0; i < n_bins + 2; i++) {
499 freq_points[i] = 700.0f * (pow(10.0f, mel_points[i] / 2595.0f) - 1.0f);
500 }
501
502 // Convert frequency points to FFT bin indices
503 Vector<int> bin_indices;
504 bin_indices.resize(n_bins + 2);
505 for (int i = 0; i < n_bins + 2; i++) {
506 bin_indices[i] = round(freq_points[i] * cfg.length / cfg.sample_rate);
507 // Ensure bin index is within valid range
508 if (bin_indices[i] >= bins) bin_indices[i] = bins - 1;
509 if (bin_indices[i] < 0) bin_indices[i] = 0;
510 }
511
512 // Distribute MEL energy back to linear frequency bins
513 Vector<float> linear_magnitudes;
514 linear_magnitudes.resize(bins);
515
516 for (int i = 0; i < n_bins; i++) {
517 int start_bin = bin_indices[i];
518 int mid_bin = bin_indices[i + 1];
519 int end_bin = bin_indices[i + 2];
520
521 // Apply first half of triangle (ascending)
522 for (int j = start_bin; j < mid_bin; j++) {
523 if (j >= bins) break;
524 float weight = (j - start_bin) / float(mid_bin - start_bin);
525 linear_magnitudes[j] += values[i] * weight;
526 }
527
528 // Apply second half of triangle (descending)
529 for (int j = mid_bin; j < end_bin; j++) {
530 if (j >= bins) break;
531 float weight = (end_bin - j) / float(end_bin - mid_bin);
532 linear_magnitudes[j] += values[i] * weight;
533 }
534 }
535
536 // Set magnitude values and create simple phase (all zeros)
537 for (int i = 0; i < bins; i++) {
538 if (linear_magnitudes[i] > 0) {
539 FFTBin bin;
540 bin.real = linear_magnitudes[i];
541 bin.img = 0.0f;
542 setBin(i, bin);
543 }
544 }
545
546 return true;
547 }
548
551 FFTDriver *driver() { return p_driver; }
552
554 float frequency(int bin) {
555 if (bin >= bins) {
556 LOGE("Invalid bin %d", bin);
557 return 0;
558 }
559 return static_cast<float>(bin) * cfg.sample_rate / cfg.length;
560 }
561
563 int frequencyToBin(int freq) {
564 int max_freq = cfg.sample_rate / 2;
565 return map(freq, 0, max_freq, 0, size());
566 }
567
570 float magnitude(int bin) {
571 if (bin >= bins) {
572 LOGE("Invalid bin %d", bin);
573 return 0;
574 }
575 return p_driver->magnitude(bin);
576 }
577
578 float magnitudeFast(int bin) {
579 if (bin >= bins) {
580 LOGE("Invalid bin %d", bin);
581 return 0;
582 }
583 return p_driver->magnitudeFast(bin);
584 }
585
587 float phase(int bin) {
588 FFTBin fft_bin;
589 getBin(bin, fft_bin);
590 return atan2(fft_bin.img, fft_bin.real);
591 }
592
595 float *magnitudes() {
596 if (l_magnitudes.size() == 0) {
597 l_magnitudes.resize(size());
598 }
599 for (int j = 0; j < size(); j++) {
600 l_magnitudes[j] = magnitude(j);
601 }
602 return l_magnitudes.data();
603 }
604
607 float *magnitudesFast() {
608 if (l_magnitudes.size() == 0) {
609 l_magnitudes.resize(size());
610 }
611 for (int j = 0; j < size(); j++) {
612 l_magnitudes[j] = magnitudeFast(j);
613 }
614 return l_magnitudes.data();
615 }
616
618 bool setBin(int idx, float real, float img) {
619 has_rfft_data = true;
620 if (idx < 0 || idx >= size()) return false;
621 bool rc_first_half = p_driver->setBin(idx, real, img);
622 bool rc_2nd_half = p_driver->setBin(cfg.length - idx, real, img);
623 return rc_first_half && rc_2nd_half;
624 }
626 bool setBin(int pos, FFTBin &bin) { return setBin(pos, bin.real, bin.img); }
628 bool getBin(int pos, FFTBin &bin) { return p_driver->getBin(pos, bin); }
629
631 void clearBins() {
632 FFTBin empty{0, 0};
633 for (int j = 0; j < size(); j++) {
634 setBin(j, empty);
635 }
636 }
637
639 AudioFFTConfig &config() { return cfg; }
640
641 protected:
642 FFTDriver *p_driver = nullptr;
643 int current_pos = 0;
644 int bins = 0;
645 unsigned long timestamp_begin = 0l;
646 unsigned long timestamp = 0l;
647 AudioFFTConfig cfg;
648 FFTInverseOverlapAdder rfft_add{0};
649 Vector<float> l_magnitudes{0};
650 Vector<float> step_data{0};
651 Vector<float> mel_bins{0};
652 SingleBuffer<uint8_t> stride_buffer{0};
653 RingBuffer<uint8_t> rfft_data{0};
654 bool has_rfft_data = false;
655
656 // Add samples to input data p_x - and process them if full
657 template <typename T>
658 void processSamples(const void *data, size_t count) {
659 T *dataT = (T *)data;
660 T sample;
661 for (int j = 0; j < count; j += cfg.channels) {
662 sample = dataT[j + cfg.channel_used];
663 if (writeStrideBuffer((uint8_t *)&sample, sizeof(T))) {
664 // process data if buffer is full
665 T *samples = (T *)stride_buffer.data();
666 int sample_count = stride_buffer.size() / sizeof(T);
667 assert(sample_count == cfg.length);
668 for (int j = 0; j < sample_count; j++) {
669 T out_sample = samples[j];
670 T windowed_sample = windowedSample(out_sample, j);
671 float scaled_sample =
672 1.0f / NumberConverter::maxValueT<T>() * windowed_sample;
673 p_driver->setValue(j, scaled_sample);
674 }
675
676 fft<T>();
677
678 // remove stride samples
679 stride_buffer.clearArray(cfg.stride * sizeof(T));
680
681 // validate available data in stride buffer
682 if (cfg.stride == cfg.length) assert(stride_buffer.available() == 0);
683 }
684 }
685 }
686
687 template <typename T>
688 T windowedSample(T sample, int pos) {
689 T result = sample;
690 if (cfg.window_function_fft != nullptr) {
691 result = cfg.window_function_fft->factor(pos) * sample;
692 }
693 return result;
694 }
695
696 template <typename T>
697 void fft() {
698 timestamp_begin = millis();
699 p_driver->fft();
700 has_rfft_data = true;
701 timestamp = millis();
702 if (cfg.callback != nullptr) {
703 cfg.callback(*this);
704 }
705 }
706
708 void rfft() {
709 TRACED();
710 // execute reverse fft
711 p_driver->rfft();
712 has_rfft_data = false;
713 // add data to sum buffer
714 for (int j = 0; j < cfg.length; j++) {
715 float value = p_driver->getValue(j);
716 rfft_add.add(value, j, cfg.window_function_ifft);
717 }
718 // get result data from sum buffer
719 rfftWriteData(rfft_data);
720 }
721
724 // get data to result buffer
725 // for (int j = 0; j < cfg.stride; j++) {
726 // step_data[j] = 0.0;
727 // }
728 rfft_add.getStepData(step_data.data(), cfg.stride,
730
731 switch (cfg.bits_per_sample) {
732 case 8:
733 writeIFFT<int8_t>(step_data.data(), cfg.stride);
734 break;
735 case 16:
736 writeIFFT<int16_t>(step_data.data(), cfg.stride);
737 break;
738 case 24:
739 writeIFFT<int24_t>(step_data.data(), cfg.stride);
740 break;
741 case 32:
742 writeIFFT<int32_t>(step_data.data(), cfg.stride);
743 break;
744 default:
745 LOGE("Unsupported bits: %d", cfg.bits_per_sample);
746 }
747 }
748
749 template <typename T>
750 void writeIFFT(float *data, int len) {
751 for (int j = 0; j < len; j++) {
752 T sample = data[j];
753 T out_data[cfg.channels];
754 for (int ch = 0; ch < cfg.channels; ch++) {
755 out_data[ch] = sample;
756 }
757 int result = rfft_data.writeArray((uint8_t *)out_data, sizeof(out_data));
758 assert(result == sizeof(out_data));
759 }
760 }
761
762 inline int bytesPerSample() { return cfg.bits_per_sample / 8; }
763
765 template <int N>
767 // find place where we need to insert new record
768 for (int j = 0; j < N; j++) {
769 // insert when biggen then current record
770 if (tmp.magnitude > result[j].magnitude) {
771 // shift existing values right
772 for (int i = N - 2; i >= j; i--) {
773 result[i + 1] = result[i];
774 }
775 // insert new value
776 result[j] = tmp;
777 // stop after we found the correct index
778 break;
779 }
780 }
781 }
782
783 // adds samples to stride buffer, returns true if the buffer is full
784 bool writeStrideBuffer(uint8_t *buffer, size_t len) {
785 assert(stride_buffer.availableForWrite() >= len);
786 stride_buffer.writeArray(buffer, len);
787 return stride_buffer.isFull();
788 }
789
790 bool isPowerOfTwo(uint16_t x) { return (x & (x - 1)) == 0; }
791};
792
793} // namespace audio_tools
Different Window functions that can be used by FFT.
Executes FFT using audio data privded by write() and/or an inverse FFT where the samples are made ava...
Definition AudioFFT.h:191
unsigned long resultTimeBegin()
time before the fft
Definition AudioFFT.h:358
void clearBins()
clears the fft data
Definition AudioFFT.h:631
float magnitude(int bin)
Definition AudioFFT.h:570
float * magnitudesFast()
Definition AudioFFT.h:607
unsigned long resultTime()
Definition AudioFFT.h:356
int length()
The number of samples.
Definition AudioFFT.h:352
size_t readBytes(uint8_t *data, size_t len) override
Provides the result of a reverse FFT.
Definition AudioFFT.h:320
void end() override
Release the allocated memory.
Definition AudioFFT.h:285
int available() override
Data available for reverse fft.
Definition AudioFFT.h:343
AudioFFTBase(FFTDriver *driver)
Definition AudioFFT.h:195
AudioFFTConfig & config()
Provides the actual configuration.
Definition AudioFFT.h:639
size_t write(const uint8_t *data, size_t len) override
Provide the audio data as FFT input.
Definition AudioFFT.h:294
FFTDriver * driver()
Definition AudioFFT.h:551
int frequencyToBin(int freq)
Determine the bin number from the frequency.
Definition AudioFFT.h:563
bool setBin(int idx, float real, float img)
sets the value of a bin
Definition AudioFFT.h:618
int availableForWrite() override
We try to fill the buffer at once.
Definition AudioFFT.h:338
float phase(int bin)
calculates the phase
Definition AudioFFT.h:587
AudioFFTConfig defaultConfig(RxTxMode mode=TX_MODE)
Provides the default configuration.
Definition AudioFFT.h:200
bool fromMEL(float *values, int n_bins, float min_freq=0.0f, float max_freq=0.0f)
Convert MEL spectrum back to linear frequency spectrum.
Definition AudioFFT.h:467
bool begin(AudioFFTConfig info)
starts the processing
Definition AudioFFT.h:207
float * magnitudes()
Definition AudioFFT.h:595
bool getBin(int pos, FFTBin &bin)
gets the value of a bin
Definition AudioFFT.h:628
AudioFFTResult result()
Determines the result values in the max magnitude bin.
Definition AudioFFT.h:361
float frequency(int bin)
Determines the frequency of the indicated bin.
Definition AudioFFT.h:554
void rfft()
reverse fft
Definition AudioFFT.h:708
bool setBin(int pos, FFTBin &bin)
sets the value of a bin
Definition AudioFFT.h:626
void insertSorted(AudioFFTResult(&result)[N], AudioFFTResult tmp)
make sure that we do not reuse already found results
Definition AudioFFT.h:766
bool begin() override
starts the processing
Definition AudioFFT.h:213
void setAudioInfo(AudioInfo info) override
Notify change of audio information.
Definition AudioFFT.h:277
void reset()
Just resets the current_pos e.g. to start a new cycle.
Definition AudioFFT.h:262
void resultArray(AudioFFTResult(&result)[N])
Determines the N biggest result values.
Definition AudioFFT.h:379
float * toMEL(int n_bins, float min_freq=0.0f, float max_freq=0.0f)
Convert the FFT result to MEL spectrum.
Definition AudioFFT.h:395
int size()
The number of bins used by the FFT which are relevant for the result.
Definition AudioFFT.h:349
void rfftWriteData(BaseBuffer< uint8_t > &data)
write reverse fft result to buffer to make it available for readBytes
Definition AudioFFT.h:723
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:122
Shared functionality of all buffers.
Definition Buffers.h:22
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:33
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
Abstract Class which defines the basic FFT functionality.
Definition AudioFFT.h:156
virtual float magnitude(int idx)=0
Calculate the magnitude (fft result) at index (sqr(i² + r²))
virtual bool setBin(int idx, float real, float img)
sets the value of a bin
Definition AudioFFT.h:176
virtual void fft()=0
Perform FFT.
virtual float magnitudeFast(int idx)=0
Calculate the magnitude w/o sqare root.
virtual bool isReverseFFT()
Returns true if reverse FFT is supported.
Definition AudioFFT.h:170
virtual void rfft()
Calculate reverse FFT.
Definition AudioFFT.h:172
bool setBin(int pos, FFTBin &bin)
sets the value of a bin
Definition AudioFFT.h:178
virtual void setValue(int pos, float value)=0
Sets the real value.
virtual bool getBin(int pos, FFTBin &bin)
gets the value of a bin
Definition AudioFFT.h:180
virtual float getValue(int pos)=0
Get result value from Reverse FFT.
Inverse FFT Overlapp Add.
Definition AudioFFT.h:87
void resize(int size)
Initilze data by defining new size.
Definition AudioFFT.h:94
int size()
provides the actual size
Definition AudioFFT.h:142
const char * note(float frequency, float &diff) const
Determines the closes note for a frequency. We also return the frequency difference.
Definition MusicalNotes.h:169
static int64_t maxValue(int value_bits_per_sample)
provides the biggest number for the indicated number of bits
Definition AudioTypes.h:299
virtual size_t size() override
Returns the maximum capacity of the buffer.
Definition Buffers.h:423
virtual bool resize(int len)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:413
virtual int available() override
provides the number of entries that are available to read
Definition Buffers.h:405
int available() override
provides the number of entries that are available to read
Definition Buffers.h:229
int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:234
bool isFull() override
checks if the buffer is full
Definition Buffers.h:236
bool resize(int size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:301
int writeArray(const T data[], int len) override
Fills the buffer data.
Definition Buffers.h:197
T * data()
Provides address of actual data.
Definition Buffers.h:280
int clearArray(int len) override
consumes len bytes and moves current data to the beginning
Definition Buffers.h:248
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
FFT Window Function.
Definition FFTWindows.h:24
virtual void begin(int samples)
Setup the window function providing the fft length.
Definition FFTWindows.h:29
float factor(int idx)
Definition FFTWindows.h:37
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
long map(long x, long in_min, long in_max, long out_min, long out_max)
Maps input to output values.
Definition NoArduino.h:189
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
Configuration for AudioFFT. If there are more then 1 channel the channel_used is defining which chann...
Definition AudioFFT.h:40
void * ref
caller
Definition AudioFFT.h:61
WindowFunction * window_function_ifft
Optional window function for ifft only.
Definition AudioFFT.h:57
uint8_t channel_used
Channel which is used as input.
Definition AudioFFT.h:49
WindowFunction * window_function_fft
Optional window function for fft only.
Definition AudioFFT.h:55
WindowFunction * window_function
Optional window function for both fft and ifft.
Definition AudioFFT.h:53
void(* callback)(AudioFFTBase &fft)
Callback method which is called after we got a new result.
Definition AudioFFT.h:47
RxTxMode rxtx_mode
TX_MODE = FFT, RX_MODE = IFFT.
Definition AudioFFT.h:59
Result of the FFT.
Definition AudioFFT.h:23
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59
And individual FFT Bin.
Definition AudioFFT.h:65