From f21a17b6c9a33280a0a0b343b6a5c231fa87e412 Mon Sep 17 00:00:00 2001 From: chatlanin Date: Fri, 20 Mar 2026 10:42:25 +0300 Subject: [PATCH] add fft plugin --- bin/main.cpp | 10 +++----- src/harmonica.hpp | 1 + src/meson.build | 2 ++ src/plugins/fft/fft.cpp | 50 ++++++++++++++++++++++++++++++++++++ src/plugins/fft/fft.hpp | 22 ++++++++++++++++ src/utils/math.hpp | 23 +++++++++++++++++ src/utils/workers/result.hpp | 9 +++++++ src/utils/workers/setup.hpp | 2 ++ 8 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 src/plugins/fft/fft.cpp create mode 100644 src/plugins/fft/fft.hpp create mode 100644 src/utils/math.hpp diff --git a/bin/main.cpp b/bin/main.cpp index 539ded4..9822b1a 100644 --- a/bin/main.cpp +++ b/bin/main.cpp @@ -8,18 +8,16 @@ auto main() -> int // данных для чтения m_block_size; см. установки по умолчанию. // Передается по ссылке и заполняется необходимыми данными hr::setup setup; - // setup.m_domain = hr::DOMAIN_PLUGIN::FREQUENSY; + setup.m_domain = hr::DOMAIN_PLUGIN::FREQUENSY; setup.m_file = "./sin.wav"; - auto r = hr::run(setup); + auto r = hr::run(setup); hack::log()("size:", r.size()); + hack::log()("grad:", r.m_grad); if (!r.empty()) { - std::vector res; for (auto& p : r.m_data) - for (auto s : p) - res.push_back(s.m_value); - hack::log()(res); + hack::log()(p[0].m_values); } } diff --git a/src/harmonica.hpp b/src/harmonica.hpp index 30bcfbd..3b1f77b 100644 --- a/src/harmonica.hpp +++ b/src/harmonica.hpp @@ -11,6 +11,7 @@ #include "plugins/raw_data/raw_data.hpp" // IWYU pragma: keep #include "plugins/magnitude/magnitude.hpp" // IWYU pragma: keep +#include "plugins/fft/fft.hpp" // IWYU pragma: keep namespace hr { diff --git a/src/meson.build b/src/meson.build index 8f8b600..b90a1ce 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,6 +16,7 @@ headers = [ # plugins 'plugins/raw_data/raw_data.hpp', 'plugins/magnitude/magnitude.hpp', + 'plugins/fft/fft.hpp', 'harmonica.hpp' ] @@ -30,6 +31,7 @@ sources = [ # plugins 'plugins/raw_data/raw_data.cpp', 'plugins/magnitude/magnitude.cpp', + 'plugins/fft/fft.cpp', ] lib = library( diff --git a/src/plugins/fft/fft.cpp b/src/plugins/fft/fft.cpp new file mode 100644 index 0000000..3db8f16 --- /dev/null +++ b/src/plugins/fft/fft.cpp @@ -0,0 +1,50 @@ +#include "fft.hpp" +#include "utils/var.hpp" +#include "utils/math.hpp" + +namespace hr::plugins +{ + fft::fft(const setup& st) : plugin{ st } + { + m_setup.m_plugin_name = "FFT"; + m_setup.m_plugin_description = "Вычисляет FFT и прокидывает данные дальше в вашу программу."; + GUARD_DOMAIN(FREQUENSY); + + // Данные - амплитуда частот + m_result.init(1); + + // заполняем градацию по частотам + // тут не нужно делить на 2. получаем = 513 исходя из базового m_setup + // т.к. реализация FFT (rdft) уже возвращает только уникальную часть спектра, а не полный симметричный массив из 1024 элементов. + m_frames = m_setup.m_step_size + 1; + m_result.m_grad.reserve(m_frames); + for (size_t i = 0; i < m_frames; ++i) + m_result.m_grad.push_back(static_cast(i) * m_setup.m_sample_rate / m_setup.m_block_size); + } + + void fft::process(fvec_t& base, real_time timestamp) + { + } + + void fft::process(cvec_t& fft, fvec_t& base, real_time timestamp) + { + result::bit b; + b.m_name = "Amplitudes"; + b.m_duration = timestamp; + b.m_values.reserve(m_frames); + for (size_t i = 0; i < m_frames; ++i) + { + // Конвертация в децибелы + auto a = fft.m_norm[i]; + if (a > 0.000001f) a = 20.0f * log10(a); + else a = -120.0f; // Минимальное значение для логарифмической шкалы + b.m_values.push_back(a); + } + m_result.set_bit(0, b); + } + + result fft::get_result() + { + return m_result; + } +} diff --git a/src/plugins/fft/fft.hpp b/src/plugins/fft/fft.hpp new file mode 100644 index 0000000..2ab2f76 --- /dev/null +++ b/src/plugins/fft/fft.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "utils/workers/plugin.hpp" + +namespace hr::plugins +{ + class fft : public plugin + { + public: + fft(const setup& st); + virtual ~fft() = default; + + private: + result m_result; + std::size_t m_frames; + + public: + void process(fvec_t& base, real_time timestamp) override; + void process(cvec_t& fft, fvec_t& base, real_time timestamp) override; + result get_result() override; + }; +} diff --git a/src/utils/math.hpp b/src/utils/math.hpp new file mode 100644 index 0000000..c3f7729 --- /dev/null +++ b/src/utils/math.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace hr +{ + template + T log10(T value) + { + if constexpr (std::is_same_v) + return std::log10f(value); + else if constexpr (std::is_same_v) + return std::log10(value); + else if constexpr (std::is_same_v) + return std::log10l(value); + else + { + static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Unsupported type for log10"); + return std::log10(value); // fallback + } + } +} diff --git a/src/utils/workers/result.hpp b/src/utils/workers/result.hpp index 44024d5..81eb0f3 100644 --- a/src/utils/workers/result.hpp +++ b/src/utils/workers/result.hpp @@ -13,7 +13,13 @@ namespace hr { std::string m_name; real_time m_duration; + // когда в расчете только одно значение. да может быть и нужно это использовать как + // массив с индексом 0 типа m_values[0], но как-то вот так. Потому как отрисовка графиков тот еще праздник... + // конечно в боевой задаче это можно и нужно оптимизировать, но в данном случае для вывода на экран и понимания процесса + // это можно опустить и использовать так как есть... base_t m_value; + // когда у тебя получается на один бин большоймассив данных, типа расчет fft (см. комент выше) + std::vector m_values; }; void set_bit(std::size_t index, bit& b) @@ -53,5 +59,8 @@ namespace hr // если захотелось увидеть их // Второй вектор - данные для каждой линии, т.е. именно сими биты std::vector> m_data; + + // иногда нужна градуировка одна и тажа для всех бинов + std::vector m_grad; }; } diff --git a/src/utils/workers/setup.hpp b/src/utils/workers/setup.hpp index 74d3a97..1988497 100644 --- a/src/utils/workers/setup.hpp +++ b/src/utils/workers/setup.hpp @@ -18,7 +18,9 @@ namespace hr int m_channels; std::filesystem::path m_file; + // Количество семплов, которые обрабатываются за один раз std::size_t m_block_size = 1'024; + // На сколько семплов сдвигается окно при следующей обработке std::size_t m_step_size = 512; std::string m_plugin_name;