4#include "AudioTools/CoreAudio/AudioStreams.h"
5#include "AudioTools/CoreAudio/MusicalNotes.h"
17static MusicalNotes AudioFFTNotes;
25 float magnitude = 0.0f;
26 float frequency = 0.0f;
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);
76 void multiply(
float f) {
81 void conjugate() { img = -img; }
83 void clear() { real = img = 0.0f; }
100 for (
int j = 0; j < data.size(); j++) {
107 float add_value = value;
108 if (window_function !=
nullptr) {
109 add_value = value * window_function->
factor(pos);
112 data[pos] += add_value;
116 void getStepData(
float *result,
int stride,
float maxResult) {
117 for (
int j = 0; j < stride; j++) {
119 if (data[j] > rfft_max) rfft_max = data[j];
121 for (
int j = 0; j < stride; j++) {
122 result[j] = data[j] / rfft_max * maxResult;
124 if (result[j] > maxResult) {
125 result[j] = maxResult;
127 if (result[j] < -maxResult) {
128 result[j] = -maxResult;
132 for (
int j = 0; j < len - stride; j++) {
133 data[j] = data[j + stride];
136 for (
int j = len - stride; j < len; j++) {
142 int size() {
return data.size(); }
158 virtual bool begin(
int len) = 0;
159 virtual void end() = 0;
168 virtual bool isValid() = 0;
172 virtual void rfft() { LOGE(
"Not implemented"); }
176 virtual bool setBin(
int idx,
float real,
float img) {
return false; }
214 bins = cfg.length / 2;
221 if (cfg.stride == 0) cfg.stride = cfg.length;
223 if (!isPowerOfTwo(cfg.length)) {
224 LOGE(
"Len must be of the power of 2: %d", cfg.length);
227 if (!p_driver->begin(cfg.length)) {
228 LOGE(
"Not enough memory");
239 bool is_valid_rxtx =
false;
242 stride_buffer.
resize((cfg.length) * bytesPerSample());
243 is_valid_rxtx =
true;
247 rfft_add.
resize(cfg.length);
248 step_data.resize(cfg.stride);
249 is_valid_rxtx =
true;
252 if (!is_valid_rxtx) {
253 LOGE(
"Invalid rxtx_mode");
258 return p_driver->isValid();
272 operator bool()
override {
273 return p_driver !=
nullptr && p_driver->isValid();
287 l_magnitudes.resize(0);
294 size_t write(
const uint8_t *data,
size_t len)
override {
296 if (p_driver->isValid()) {
300 processSamples<int8_t>(data, len);
303 processSamples<int16_t>(data, len / 2);
306 processSamples<int24_t>(data, len / 3);
309 processSamples<int32_t>(data, len / 4);
322 if (rfft_data.
size() == 0)
return 0;
331 if (has_rfft_data && rfft_data.
available() == 0) {
339 return cfg.length * cfg.
channels * bytesPerSample();
344 assert(cfg.stride != 0);
345 return cfg.stride * cfg.
channels * bytesPerSample();
363 ret_value.magnitude = 0.0f;
366 for (
int j = 0; j <
size(); j++) {
368 if (m > ret_value.magnitude) {
369 ret_value.magnitude = m;
373 ret_value.frequency =
frequency(ret_value.bin);
381 for (
int j = 0; j < N; j++) {
382 result[j].magnitude = -1000000;
386 for (
int j = 0; j <
size(); j++) {
390 insertSorted<N>(
result, act);
395 float *
toMEL(
int n_bins,
float min_freq = 0.0f,
float max_freq = 0.0f) {
397 if (n_bins <= 0) n_bins =
size();
398 if (min_freq <= 0.0f) min_freq =
frequency(0);
400 mel_bins.resize(n_bins);
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));
408 mel_points.resize(n_bins + 2);
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;
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);
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);
428 if (bin_indices[i] >= bins) bin_indices[i] = bins - 1;
429 if (bin_indices[i] < 0) bin_indices[i] = 0;
433 for (
int i = 0; i < n_bins; i++) {
434 float mel_sum = 0.0f;
436 int start_bin = bin_indices[i];
437 int mid_bin = bin_indices[i + 1];
438 int end_bin = bin_indices[i + 2];
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);
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);
454 mel_bins[i] = mel_sum;
457 return mel_bins.data();
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;
472 if (min_freq <= 0.0f) min_freq =
frequency(0);
476 for (
int i = 0; i < bins; i++) {
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));
488 mel_points.resize(n_bins + 2);
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;
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);
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);
508 if (bin_indices[i] >= bins) bin_indices[i] = bins - 1;
509 if (bin_indices[i] < 0) bin_indices[i] = 0;
514 linear_magnitudes.resize(bins);
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];
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;
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;
537 for (
int i = 0; i < bins; i++) {
538 if (linear_magnitudes[i] > 0) {
540 bin.real = linear_magnitudes[i];
556 LOGE(
"Invalid bin %d", bin);
559 return static_cast<float>(bin) * cfg.
sample_rate / cfg.length;
565 return map(freq, 0, max_freq, 0,
size());
572 LOGE(
"Invalid bin %d", bin);
578 float magnitudeFast(
int bin) {
580 LOGE(
"Invalid bin %d", bin);
590 return atan2(fft_bin.img, fft_bin.real);
596 if (l_magnitudes.size() == 0) {
597 l_magnitudes.resize(
size());
599 for (
int j = 0; j <
size(); j++) {
602 return l_magnitudes.data();
608 if (l_magnitudes.size() == 0) {
609 l_magnitudes.resize(
size());
611 for (
int j = 0; j <
size(); j++) {
612 l_magnitudes[j] = magnitudeFast(j);
614 return l_magnitudes.data();
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;
633 for (
int j = 0; j <
size(); j++) {
645 unsigned long timestamp_begin = 0l;
646 unsigned long timestamp = 0l;
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;
657 template <
typename T>
658 void processSamples(
const void *data,
size_t count) {
659 T *dataT = (T *)data;
661 for (
int j = 0; j < count; j += cfg.
channels) {
663 if (writeStrideBuffer((uint8_t *)&sample,
sizeof(T))) {
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);
679 stride_buffer.
clearArray(cfg.stride *
sizeof(T));
682 if (cfg.stride == cfg.length) assert(stride_buffer.
available() == 0);
687 template <
typename T>
688 T windowedSample(T sample,
int pos) {
696 template <
typename T>
698 timestamp_begin =
millis();
700 has_rfft_data =
true;
712 has_rfft_data =
false;
714 for (
int j = 0; j < cfg.length; j++) {
715 float value = p_driver->
getValue(j);
728 rfft_add.getStepData(step_data.data(), cfg.stride,
733 writeIFFT<int8_t>(step_data.data(), cfg.stride);
736 writeIFFT<int16_t>(step_data.data(), cfg.stride);
739 writeIFFT<int24_t>(step_data.data(), cfg.stride);
742 writeIFFT<int32_t>(step_data.data(), cfg.stride);
749 template <
typename T>
750 void writeIFFT(
float *data,
int len) {
751 for (
int j = 0; j < len; j++) {
754 for (
int ch = 0; ch < cfg.
channels; ch++) {
755 out_data[ch] = sample;
758 assert(
result ==
sizeof(out_data));
768 for (
int j = 0; j < N; j++) {
770 if (tmp.magnitude >
result[j].magnitude) {
772 for (
int i = N - 2; i >= j; i--) {
784 bool writeStrideBuffer(uint8_t *buffer,
size_t len) {
787 return stride_buffer.
isFull();
790 bool isPowerOfTwo(uint16_t x) {
return (x & (x - 1)) == 0; }
Different Window functions that can be used by FFT.
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28