From 52e589974d3bb03e47c28d2d92112b981aefbb76 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Thu, 25 Jul 2024 11:04:57 +0200 Subject: [PATCH] audio: make the Lanczos interpolator a separate class --- include/lol/interp | 18 +++++++++ include/lol/private/audio/stream.h | 41 +++---------------- include/lol/private/math/interp.h | 63 ++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 include/lol/interp create mode 100644 include/lol/private/math/interp.h diff --git a/include/lol/interp b/include/lol/interp new file mode 100644 index 00000000..09546f32 --- /dev/null +++ b/include/lol/interp @@ -0,0 +1,18 @@ +// +// Lol Engine +// +// Copyright © 2010–2024 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. +// + +#pragma once + +#include "private/push_macros.h" +#include "private/math/interp.h" +#include "private/pop_macros.h" + diff --git a/include/lol/private/audio/stream.h b/include/lol/private/audio/stream.h index 33c021d0..0085c102 100644 --- a/include/lol/private/audio/stream.h +++ b/include/lol/private/audio/stream.h @@ -19,13 +19,14 @@ // #include // std::floor -#include "../math/constants.h" // F_PI #include // std::function #include // std::numeric_limits #include // std::shared_ptr #include // std::is_same_v #include // std::unordered_set +#include "../math/interp.h" // lol::interp::lanczos + namespace lol::audio { @@ -338,32 +339,10 @@ public: if (in_rate == out_rate) return m_in->get(buf, frames); - // 16 taps lanczos filter - const size_t window_size = 16; - const float window_center = 8.0f; - - const size_t constexpr lanczos_size = window_size * 64; - const float lanczos_lut_scale = (lanczos_size - 1) / (window_center + 1.0f); - - static auto build_lanczos = [&]() - { - std::array ret; - for (int k = 0; k < lanczos_size; ++k) - { - float dist = float(k) * F_PI / lanczos_lut_scale; - float lanczos = 1.0f; - if (dist > 0.0f) lanczos = window_center * std::sin(dist) * std::sin(dist / window_center) / (dist * dist); - ret[k] = lanczos; - } - return ret; - }; - - static auto const lanczos_lut = build_lanczos(); - for (size_t n = 0; n < frames; ++n, m_pos += in_rate) { // Fill internal buffer if we don’t have enough data - while (m_cache.size() / channels < m_pos / out_rate + window_size) + while (m_cache.size() / channels < m_pos / out_rate + m_lanczos.size()) { // Remove obsolete frames on the left size_t todelete = std::min(m_pos / out_rate, m_cache.size() / channels); @@ -380,17 +359,7 @@ public: float alpha = float(m_pos % out_rate) / out_rate; for (size_t ch = 0; ch < channels; ++ch) - { - // lanczos upsampling - T value = T(); - for (size_t wi = 0; wi < window_size; ++wi) - { - float dist = std::abs(wi - window_center - alpha); - float lanczos = lanczos_lut[int(dist * lanczos_lut_scale)]; - value += m_cache[(n0 + wi) * channels + ch] * lanczos; - } - *buf++ = value; - } + *buf++ = m_lanczos.get(&m_cache[n0 * channels + ch], channels, alpha); } return frames; @@ -399,6 +368,8 @@ public: protected: std::shared_ptr> m_in; + interp::lanczos m_lanczos; + std::vector m_cache; size_t m_pos; diff --git a/include/lol/private/math/interp.h b/include/lol/private/math/interp.h new file mode 100644 index 00000000..07a8843f --- /dev/null +++ b/include/lol/private/math/interp.h @@ -0,0 +1,63 @@ +// +// Lol Engine +// +// Copyright © 2010–2024 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. +// + +#pragma once + +// +// Interpolation classes +// ————————————————————— +// + +#include // std::sin +#include "../math/constants.h" // F_PI + +namespace lol::interp +{ + +template +class lanczos +{ +public: + lanczos() + { + T const window_center = SIZE / 2; + T const lut_scale = (SIZE * PRECISION - 1) / (window_center + 1); + for (size_t k = 0; k < SIZE * PRECISION; ++k) + { + T dist = T(k) * F_PI / lut_scale; + m_lut[k] = dist ? window_center * std::sin(dist) * std::sin(dist / window_center) / (dist * dist) : T(1); + } + } + + T get(T *data, size_t stride, T alpha) + { + T const window_center = SIZE / 2; + T const lut_scale = (SIZE * PRECISION - 1) / (window_center + 1); + T ret(0); + for (size_t k = 0; k < SIZE; ++k) + { + float dist = std::abs(k - window_center - alpha); + float lanczos = m_lut[size_t(dist * lut_scale)]; + ret += data[k * stride] * lanczos; + } + return ret; + } + + size_t const size() { return SIZE; } + +private: + static size_t const center = T(SIZE) / 2; + + std::array m_lut; +}; + +} // namespace lol::interp