@@ -0,0 +1,18 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net> | |||
// | |||
// 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" | |||
@@ -19,13 +19,14 @@ | |||
// | |||
#include <cmath> // std::floor | |||
#include "../math/constants.h" // F_PI | |||
#include <functional> // std::function | |||
#include <limits> // std::numeric_limits | |||
#include <memory> // std::shared_ptr | |||
#include <type_traits> // std::is_same_v | |||
#include <unordered_set> // 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<float, lanczos_size> 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<stream<T>> m_in; | |||
interp::lanczos<T> m_lanczos; | |||
std::vector<T> m_cache; | |||
size_t m_pos; | |||
@@ -0,0 +1,63 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net> | |||
// | |||
// 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 <cmath> // std::sin | |||
#include "../math/constants.h" // F_PI | |||
namespace lol::interp | |||
{ | |||
template<typename T, size_t SIZE = 16, size_t PRECISION = 64> | |||
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<T, SIZE * PRECISION> m_lut; | |||
}; | |||
} // namespace lol::interp |