initial commit

This commit is contained in:
2026-02-16 19:47:03 +03:00
commit 1e06bb4dc7
27 changed files with 1786 additions and 0 deletions

95
src/harmonica.hpp Normal file
View File

@@ -0,0 +1,95 @@
#pragma once
#include <sndfile.h>
#include <hack/logger/logger.hpp>
#include "utils/var.hpp" // IWYU pragma: keep
#include "utils/using.hpp"
#include "utils/workers/setup.hpp"
#include "utils/workers/result.hpp"
#include "adapter/adapter.hpp"
#include "plugins/magnitude/magnitude.hpp" // IWYU pragma: keep
namespace hr
{
/**
* @brief Запуск на чтение аудиофайла и обработка его через плагин
* @tparam Plugin Тип плагина для обработки аудиоданных
* @param setup Настройки обработки (путь к файлу, параметры и т.д.)
* @return Результат обработки аудио
*/
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)
{
// Обработка ошибки открытия файла
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.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;
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 };
do
{
// Определение длины читаемого блока (защита от выхода за границы)
auto length = hack::math::min(setup.m_step_size, in.size());
auto read_samples = sf_read_float(file, read_data.data(), read_data.size());
uint_t read_length = read_samples / setup.m_channels; // Перевод в кадры
read_length = hack::math::min(length, read_length); // Ограничение длиной буфера
// Де-интерливирование и down-mixing (преобразование многоканального в моно)
for (std::size_t i = 0; i < read_length; ++i)
{
in[i] = 0.0;
// Суммирование всех каналов
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);
}
// Подготовка к следующей итерации
read = hack::math::min(length, static_cast<uint_t>(floorf(read_length + .5)));
// Дополнение буфера нулями если считано неполный блок (конец файла)
if (in.size() > read) std::fill(in.begin() + read, in.end(), 0.0);
// Вычисление временной метки и обработка данных через адаптер
real_time timestamp = real_time::frame2rt(read, sf_info.samplerate);
ad.process(in, timestamp);
}
while (read == setup.m_step_size); // Продолжать пока читаются полные блоки
// Закрытие файла и возврат результата
sf_close(file);
return ad.get_result();
}
}