109 lines
4.5 KiB
C++
109 lines
4.5 KiB
C++
#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
|
||
#include "plugins/signal_generator/signal_generator.hpp" // IWYU pragma: keep
|
||
|
||
namespace hr
|
||
{
|
||
/**
|
||
* @brief Запуск на чтение аудиофайла и обработка его через плагин
|
||
* @tparam Plugin Тип плагина для обработки аудиоданных
|
||
* @param setup Настройки обработки (путь к файлу, параметры и т.д.)
|
||
* @return Результат обработки аудио
|
||
*/
|
||
template<typename Plugin>
|
||
inline result run(setup& setup)
|
||
{
|
||
if (setup.m_signal_type.m_type != hr::signal_type::type::FILE)
|
||
{
|
||
Plugin pl { setup };
|
||
adapter ad { pl };
|
||
// просто заглушки для process
|
||
fvec_t v;
|
||
real_time t;
|
||
ad.process(v, t);
|
||
return ad.get_result();
|
||
}
|
||
else
|
||
{
|
||
// Инициализация структуры для libsndfile и открытие файла
|
||
SF_INFO sf_info;
|
||
SNDFIoLE* 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();
|
||
}
|
||
}
|
||
}
|