initial commit
This commit is contained in:
95
src/harmonica.hpp
Normal file
95
src/harmonica.hpp
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user