diff --git a/include/lol/private/audio/stream.h b/include/lol/private/audio/stream.h index 4262ec69..676f0f16 100644 --- a/include/lol/private/audio/stream.h +++ b/include/lol/private/audio/stream.h @@ -19,6 +19,7 @@ // #include // std::floor +#include "../math/constants.h" // F_PI #include // std::function #include // std::numeric_limits #include // std::shared_ptr @@ -337,10 +338,33 @@ 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::sinf(dist) * std::sinf(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 + 2) + while (m_cache.size() / channels < m_pos / out_rate + window_size) { // Remove obsolete frames on the left size_t todelete = std::min(m_pos / out_rate, m_cache.size() / channels); @@ -358,8 +382,15 @@ public: for (size_t ch = 0; ch < channels; ++ch) { - *buf++ = m_cache[n0 * channels + ch] * (1.f - alpha) - + m_cache[(n0 + 1) * channels + ch] * alpha; + // 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; } }