Browse Source

audio: make the Lanczos interpolator a separate class

wip/interp
Sam Hocevar 4 months ago
parent
commit
52e589974d
3 changed files with 87 additions and 35 deletions
  1. +18
    -0
      include/lol/interp
  2. +6
    -35
      include/lol/private/audio/stream.h
  3. +63
    -0
      include/lol/private/math/interp.h

+ 18
- 0
include/lol/interp View File

@@ -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"


+ 6
- 35
include/lol/private/audio/stream.h View File

@@ -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;


+ 63
- 0
include/lol/private/math/interp.h View File

@@ -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

Loading…
Cancel
Save