Compare commits
7 Commits
a18cefb671
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 69e8f5e75b | |||
| 45215c1529 | |||
| ec1bd2f376 | |||
| 39b62d6ccb | |||
| 3afccda759 | |||
| 83ae62ce19 | |||
| 8d860ce409 |
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
executable(
|
||||
meson.project_name(),
|
||||
'main.raw_data.cpp',
|
||||
'main.fft.cpp',
|
||||
dependencies : deps,
|
||||
cpp_args: args,
|
||||
include_directories : inc
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
// иногда нужна градуировка одна и тажа для всех бинов
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user