aboutsummaryrefslogtreecommitdiffstats
path: root/lib/resample_func.h
blob: 9e894d40cc8f9504e04f18a9ccec1da5d88ecbdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#pragma once

#include <array>
#include <algorithm>
#include <cmath>
#include <type_traits>
#include <limits>
#include "resample.h"
#include "filter.h"

namespace {
	template<typename result_type, typename type>
	constexpr result_type dither_truncate(type value, unsigned dither)
	{
		using limits = std::numeric_limits<result_type>;
		using dither_limits = std::numeric_limits<decltype(dither)>;
		if constexpr(std::is_floating_point_v<type>)
		{
			value += dither * type{1} / type{1ul << dither_limits::digits};
			value = std::floor(value);
		}
		else
		{
			static_assert(std::is_integral_v<type>, "Unsupported acc_type");

			value += dither >> (dither_limits::digits - limits::digits); // 17 for short
			value >>= limits::digits; // 15 for short
		}

		return std::clamp<type>(value, limits::min(), limits::max());
	}
}

template<typename sample_type_in, typename sample_type_out, typename filter_type, typename acc_type, size_t channels = 1, size_t INTERP_BIT = 14>
void lfr_resample(lfr_fixed_t& pos, const lfr_fixed_t inv_ratio, unsigned& dither, void* out, std::size_t outlen, const void* in, std::size_t inlen, const lfr_filter& filter)
{
	static_assert(channels > 0, "0 channels do not make any sense");

	auto inp = reinterpret_cast<const sample_type_in*>(in);
	auto outp = reinterpret_cast<sample_type_out*>(out);
	auto fd = reinterpret_cast<const filter_type*>(filter.data.data());

	const auto flen = filter.nsamp;
	const auto log2nfilt = filter.log2nfilt;
	auto x = pos;
	std::array<unsigned, channels> ds{dither};
	for(std::size_t i = 1; i < channels; ++i)
	{
		ds[i] = LCG_A * ds[i - 1] + LCG_C;
	}

	for(std::size_t i = 0; i < outlen; ++i)
	{
		/* fn: filter number
			ff0: filter factor for filter fn
			ff1: filter factor for filter fn+1 */

		const auto fn = (static_cast<unsigned>(x >> 1) >> (31 - log2nfilt)) & ((1u << log2nfilt) - 1);
		int ff1 = (static_cast<unsigned>(x >> (32 - log2nfilt - INTERP_BIT))) & ((1u << INTERP_BIT) - 1);
		int ff0 = (1u << INTERP_BIT) - ff1;

		/* off: offset in input corresponding to first sample in filter */
		auto off = static_cast<int>(x >> 32);
		/* fidx0, fidx1: start, end indexes in FIR data */
		int fidx0 = std::max(-off, 0);
		int fidx1 = std::min<int>(inlen - off, flen);

		/* acc: FIR accumulator */
		std::array<acc_type, channels> acc{0};

		for(int j = fidx0; j < fidx1; ++j)
		{
			acc_type f = (fd[(fn + 0) * flen + j] * ff0 + fd[(fn + 1) * flen + j] * ff1) / (1 << INTERP_BIT);
			for(std::size_t c = 0; c < channels; ++c)
			{
				acc[c] += inp[(j + off) * channels + c] * f;
			}
		}

		for(std::size_t c = 0; c < channels; ++c)
		{
			outp[i * channels + c] = dither_truncate<sample_type_out>(acc[c], ds[c]);

			ds[c] = LCG_A2 * ds[c] + LCG_C2;
		}

		x += inv_ratio;
	}

	pos = x;
	dither = ds[0];
}