Compare commits

...

7 Commits

Author SHA1 Message Date
69e8f5e75b fix fft grad 2026-04-22 17:08:09 +03:00
45215c1529 add min max in the bin 2026-04-15 14:47:10 +03:00
ec1bd2f376 fix some code 2026-04-15 14:10:14 +03:00
39b62d6ccb fix fft size 2026-04-15 13:51:50 +03:00
3afccda759 fix adapter 2026-04-07 14:35:20 +03:00
83ae62ce19 remove init setup 2026-04-07 14:10:47 +03:00
8d860ce409 add init to hr::setup 2026-04-07 13:51:44 +03:00
8 changed files with 59 additions and 31 deletions

View File

@@ -9,15 +9,16 @@ auto main() -> int
// Передается по ссылке и заполняется необходимыми данными
hr::setup setup;
setup.m_domain = hr::DOMAIN_PLUGIN::FREQUENSY;
setup.m_file = "./sin.wav";
setup.m_file = "/mnt/raid/projects/dsp/songs/base/MakSim: Знаешь ли ты?.mp3";
auto r = hr::run<hr::plugins::fft>(setup);
hack::log()("grad:", r.m_grad);
hack::log()("grad:", r.m_grad.size());
hack::log()("min:", r.m_min, "max:", r.m_max);
hack::log()("size:", r.m_size);
if (!r.empty())
{
for (auto& p : r.m_data)
hack::log()(p[0].m_values);
hack::log()(p[10].m_values.size(), p[10].m_min, p[10].m_max);
}
}

View File

@@ -1,6 +1,6 @@
executable(
meson.project_name(),
'main.raw_data.cpp',
'main.fft.cpp',
dependencies : deps,
cpp_args: args,
include_directories : inc

View File

@@ -57,7 +57,7 @@ namespace hr
private:
void swap_buffer(fvec_t& in)
{
size_t i = 0;
std::size_t i = 0;
for (i = 0; i < m_end; ++i) m_data[i] = m_data_old[i];
for (i = 0; i < m_plugin.m_setup.m_step_size; ++i) m_data[m_end + i] = in[i];
for (i = 0; i < m_end; ++i) m_data_old[i] = m_data[i + m_plugin.m_setup.m_step_size];

View File

@@ -24,36 +24,32 @@ namespace hr
template<typename Plugin>
inline result run(setup& setup)
{
// Инициализация структуры для libsndfile и открытие файла
SF_INFO sf_info;
SNDFILE* file = sf_open(setup.m_file.c_str(), SFM_READ, &sf_info);
if (!file)
auto deleter = [&](SNDFILE* f) { if (f) sf_close(f); };
std::unique_ptr<SNDFILE, decltype(deleter)> sf_file(sf_open(setup.m_file.c_str(), SFM_READ, &sf_info), deleter);
if (!sf_file)
{
// Обработка ошибки открытия файла
hack::exception ex;
hack::log().on_file();
hack::log().on_func();
hack::log().on_row();
ex.title("Error of open file");
ex.description(sf_strerror(file));
ex.description(sf_strerror(sf_file.get()));
ex.set("file", setup.m_file);
hack::error()(ex);
throw ex;
}
// Сохранение информации о файле в настройки
setup.m_sample_rate = sf_info.samplerate;
setup.m_frames = sf_info.frames;
setup.m_channels = sf_info.channels;
setup.check();
if (setup.m_channels == 0) throw std::runtime_error("Нет каналов в аудио файле");
// Инициализация переменных для чтения
std::size_t read = 0; // Количество обработанных кадров
fvec_t read_data(setup.m_channels * setup.m_step_size, .0); // Буфер для чтения (интерливированные данные)
fvec_t in(setup.m_step_size, .0); // Буфер для моно-данных
// Создание плагина и адаптера для обработки
Plugin pl { setup };
adapter ad { pl };
@@ -61,7 +57,7 @@ namespace hr
{
// Определение длины читаемого блока (защита от выхода за границы)
auto length = hack::math::min(setup.m_step_size, in.size());
auto read_samples = sf_read_float(file, read_data.data(), read_data.size());
auto read_samples = sf_read_float(sf_file.get(), read_data.data(), read_data.size());
uint_t read_length = read_samples / setup.m_channels; // Перевод в кадры
read_length = hack::math::min(length, read_length); // Ограничение длиной буфера
@@ -71,8 +67,7 @@ namespace hr
{
in[i] = 0.0;
// Суммирование всех каналов
for (int c = 0; c < setup.m_channels; ++c)
in[i] += read_data[setup.m_channels * i + c];
for (int c = 0; c < setup.m_channels; ++c) in[i] += read_data[setup.m_channels * i + c];
// Усреднение для получения моно-сигнала
in[i] /= static_cast<base_t>(setup.m_channels);
}
@@ -84,12 +79,11 @@ namespace hr
if (in.size() > read) std::fill(in.begin() + read, in.end(), 0.0);
// Вычисление временной метки и обработка данных через адаптер
real_time timestamp = real_time::frame2rt(read, sf_info.samplerate);
real_time timestamp = real_time::frame2rt(read, setup.m_sample_rate);
ad.process(in, timestamp);
}
while (read == setup.m_step_size);
sf_close(file);
return ad.get_result();
}
}

View File

@@ -16,11 +16,10 @@ namespace hr::plugins
// заполняем градацию по частотам
// тут не нужно делить на 2. получаем = 513 исходя из базового m_setup
// т.к. реализация FFT (rdft) уже возвращает только уникальную часть спектра, а не полный симметричный массив из 1024 элементов.
m_frames = m_setup.m_step_size + 1;
m_result.m_grad.reserve(m_frames);
m_result.m_size = m_frames;
for (size_t i = 0; i < m_frames; ++i)
m_result.m_grad.push_back(static_cast<float>(i) * m_setup.m_sample_rate / m_setup.m_block_size);
auto step = m_setup.m_step_size + 1;
m_result.m_grad.reserve(step);
for (size_t i = 0; i < step; ++i)
m_result.m_grad.push_back(static_cast<float>(i) * m_setup.m_sample_rate / m_setup.m_block_size / 1'000);
}
void fft::process(fvec_t& base, real_time timestamp)
@@ -29,20 +28,24 @@ namespace hr::plugins
void fft::process(cvec_t& fft, fvec_t& base, real_time timestamp)
{
auto step = fft.size();
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)
b.m_values.reserve(step);
for (size_t i = 0; i < step; ++i)
{
// Конвертация в децибелы
auto v = fft.m_norm[i];
if (v > 0.000001f) v = 20.0f * log10(v);
else v = -120.0f; // Минимальное значение для логарифмической шкалы
b.m_values.push_back(v);
b.set_min_max(v);
m_result.set_min_max(v);
}
m_result.set_bit(0, b);
++m_result.m_size;
}
result fft::get_result()

View File

@@ -12,7 +12,6 @@ namespace hr::plugins
private:
result m_result;
std::size_t m_frames;
public:
void process(fvec_t& base, real_time timestamp) override;

View File

@@ -14,14 +14,27 @@ namespace hr
{
std::string m_name;
real_time m_duration;
// когда в расчете только одно значение. да может быть и нужно это использовать как
// массив с индексом 0 типа m_values[0], но как-то вот так.
// Потому как отрисовка графиков тот еще праздник...
// конечно в боевой задаче это можно и нужно оптимизировать, но в данном случае для вывода на экран и понимания процесса
// это можно опустить и использовать так как есть...
base_t m_value;
// когда у тебя получается на один бин большоймассив данных, типа расчет fft (см. комент выше)
// когда у тебя получается на один бин большой массив данных, типа расчет fft (см. комент выше)
std::vector<base_t> m_values;
// максимальные и минимальные элементы в конкретном бине
// в основном нужны для графической реализации
// соответственно метод ниже в попощь
base_t m_max = std::numeric_limits<base_t>::min();
base_t m_min = std::numeric_limits<base_t>::max();
void set_min_max(base_t v)
{
m_max = hack::math::max(m_max, v);
m_min = hack::math::min(m_min, v);
}
};
void set_bit(std::size_t index, bit& b)
@@ -69,7 +82,7 @@ namespace hr
// в данном случае вы сами решаете, что для вас значит размер
// это может быть размер всего массива m_data или размер массива данных хранящихся в каждом бине
// или этот же массив но помноженный на размер m_data
// или этот же массив, но помноженный на размер m_data
std::size_t m_size = 0;
// иногда нужна градуировка одна и тажа для всех бинов

View File

@@ -1,6 +1,9 @@
#pragma once
#include <filesystem>
#include <sndfile.h>
#include <hack/exception/exception.hpp>
#include <hack/logger/logger.hpp>
namespace hr
{
@@ -27,5 +30,20 @@ namespace hr
std::string m_plugin_description;
DOMAIN_PLUGIN m_domain = DOMAIN_PLUGIN::TIME;
void check()
{
if (m_channels == 0)
{
hack::exception ex;
hack::log().on_file();
hack::log().on_func();
hack::log().on_row();
ex.title("Нет каналов в аудиофайле");
ex.set("file", m_file);
hack::error()(ex);
throw ex;
}
}
};
}