OXYGENHUB
◆ main openmsx / src / sound / ResampleHQ.cc
22.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
// Based on libsamplerate-0.1.2 (aka Secret Rabbit Code)
2
//
3
//  simplified code in several ways:
4
//   - resample algorithm is no longer switchable, we took this variant:
5
//        Band limited sinc interpolation, fastest, 97dB SNR, 80% BW
6
//   - don't allow to change sample rate on-the-fly
7
//   - assume input (and thus also output) signals have infinite length, so
8
//     there is no special code to handle the ending of the signal
9
//   - changed/simplified API to better match openmsx use model
10
//     (e.g. remove all error checking)
11
12
#include "ResampleHQ.hh"
13
14
#include "ResampledSoundDevice.hh"
15
16
#include "FixedPoint.hh"
17
#include "Math.hh"
18
#include "MemBuffer.hh"
19
#include "aligned.hh"
20
#include "narrow.hh"
21
#include "ranges.hh"
22
#include "small_buffer.hh"
23
#include "stl.hh"
24
#include "xrange.hh"
25
26
#include <algorithm>
27
#include <array>
28
#include <bit>
29
#include <cassert>
30
#include <cmath>
31
#include <cstddef>
32
#include <iterator>
33
#include <vector>
34
#ifdef __SSE2__
35
#include <emmintrin.h>
36
#endif
37
38
namespace openmsx {
39
40
// Letting the compiler deduce the (type and) size of the std::array works fine
41
// with gcc, msvc and clang-11 but is broken from clang-12 onwards. More
42
// specifically it only works for sizes upto 256. For more details see:
43
//   https://www.mail-archive.com/[email protected]/msg50598.html
44
//   https://reviews.llvm.org/D86936
45
// As a workaround we do hardcode the (type and) size here:
46
//static constexpr std::array coeffs = {
47
static constexpr std::array<float, 2463 + 1> coeffs = {
48
	#include "ResampleCoeffs.ii"
49
};
50
51
using FilterIndex = FixedPoint<16>;
52
53
static constexpr int INDEX_INC = 128;
54
static constexpr int COEFF_LEN = int(std::size(coeffs) - 1);
55
static constexpr int COEFF_HALF_LEN = COEFF_LEN - 1;
56
static constexpr size_t TAB_LEN      = ResampleHQ<1>::TAB_LEN;
57
static constexpr size_t HALF_TAB_LEN = ResampleHQ<1>::HALF_TAB_LEN;
58
59
class ResampleCoeffs
60
{
61
public:
62
	ResampleCoeffs(const ResampleCoeffs&) = delete;
63
	ResampleCoeffs(ResampleCoeffs&&) = delete;
64
	ResampleCoeffs& operator=(const ResampleCoeffs&) = delete;
65
	ResampleCoeffs& operator=(ResampleCoeffs&&) = delete;
66
67
	static ResampleCoeffs& instance();
68
	void getCoeffs(double ratio, std::span<const int16_t, HALF_TAB_LEN>& permute, float*& table, unsigned& filterLen);
69
	void releaseCoeffs(double ratio);
70
71
private:
72
	using Table = MemBuffer<float, SSE_ALIGNMENT>;
73
	using PermuteTable = MemBuffer<int16_t>; // array<int16_t, HALF_TAB_LEN>
74
75
	ResampleCoeffs() = default;
76
	~ResampleCoeffs();
77
78
	static Table calcTable(double ratio, std::span<int16_t, HALF_TAB_LEN> permute, unsigned& filterLen);
79
80
	struct Element {
81
		double ratio;
82
		PermuteTable permute; // need stable address (can't directly use std::array)
83
		Table table;
84
		unsigned filterLen;
85
		unsigned count;
86
	};
87
	std::vector<Element> cache; // typically 1-4 entries -> unsorted vector
88
};
89
90
ResampleCoeffs::~ResampleCoeffs()
91
{
92
	assert(cache.empty());
93
}
94
95
ResampleCoeffs& ResampleCoeffs::instance()
96
{
97
	static ResampleCoeffs resampleCoeffs;
98
	return resampleCoeffs;
99
}
100
101
void ResampleCoeffs::getCoeffs(
102
	double ratio, std::span<const int16_t, HALF_TAB_LEN>& permute, float*& table, unsigned& filterLen)
103
{
104
	if (auto it = std::ranges::find(cache, ratio, &Element::ratio);
105
	    it != end(cache)) {
106
		permute   = std::span<int16_t, HALF_TAB_LEN>{it->permute};
107
		table     = it->table.data();
108
		filterLen = it->filterLen;
109
		it->count++;
110
		return;
111
	}
112
	Element elem;
113
	elem.ratio = ratio;
114
	elem.count = 1;
115
	elem.permute = PermuteTable(HALF_TAB_LEN);
116
	auto perm = std::span<int16_t, HALF_TAB_LEN>{elem.permute};
117
	elem.table = calcTable(ratio, perm, elem.filterLen);
118
	permute   = perm;
119
	table     = elem.table.data();
120
	filterLen = elem.filterLen;
121
	cache.push_back(std::move(elem));
122
}
123
124
void ResampleCoeffs::releaseCoeffs(double ratio)
125
{
126
	auto it = rfind_unguarded(cache, ratio, &Element::ratio);
127
	it->count--;
128
	if (it->count == 0) {
129
		move_pop_back(cache, it);
130
	}
131
}
132
133
// -- Permutation stuff --
134
//
135
// The rows in the resample coefficient table are not visited sequentially.
136
// Instead, depending on the resample-ratio, we take fixed non-integer jumps
137
// from one row to the next.
138
//
139
// In reality the table has 4096 rows (of which only 2048 are actually stored).
140
// But for simplicity I'll here work out examples for a table with only 16 rows
141
// (of which 8 are stored).
142
//
143
// Let's first assume a jump of '5.2'. This means that after we've used row
144
// 'r', the next row we need is 'r + 5.2'. Of course row numbers must be
145
// integers, so a jump of 5.2 actually means that 80% of the time we advance 5
146
// rows and 20% of the time we advance 6 rows.
147
//
148
// The rows in the (full) table are circular. This means that once we're past
149
// row 15 (in this example) we restart at row 0. So rows 'wrap' past the end
150
// (modulo arithmetic). We also only store the 1st half of the table, the
151
// entries for the 2nd half are 'folded' back to the 1st half according to the
152
// formula: y = 15 - x.
153
//
154
// Let's now calculate the possible transitions. If we're currently on row '0',
155
// the next row will be either '5' (80% chance) or row '6' (20% chance). When
156
// we're on row '5' the next most likely row will be '10', but after folding
157
// '10' becomes '15-10 = 5' (so 5 goes to itself (80% chance)). Row '10' most
158
// likely goes to '15', after folding we get that '5' goes to '0'. Row '15'
159
// most likely goes to '20', and after wrapping and folding that becomes '0'
160
// goes to '4'. Calculating this for all rows gives:
161
//   0 -> 5 or 4 (80%)   0 -> 6 or 5 (20%)
162
//   1 -> 6 or 3         1 -> 7 or 4
163
//   2 -> 7 or 2         2 -> 7 or 3
164
//   3 -> 7 or 1         3 -> 6 or 2
165
//   4 -> 6 or 0         4 -> 5 or 1
166
//   5 -> 5 or 0         5 -> 4 or 0
167
//   6 -> 4 or 1         6 -> 3 or 0
168
//   7 -> 3 or 2         7 -> 2 or 1
169
// So every row has 4 possible successors (2 more and 2 less likely). Possibly
170
// some of these 4 are the same, or even the same as the starting row. Note
171
// that if row x goes to row y (x->y) then also y->x, this turns out to be true
172
// in general.
173
//
174
// For cache efficiency it's best if rows that are needed after each other in
175
// time are also stored sequentially in memory (both before or after is fine).
176
// Clearly storing the rows in numeric order will not read the memory
177
// sequentially. For this specific example we could stores the rows in the
178
// order:
179
//    2, 7, 3, 1, 6, 4, 0, 5
180
// With this order all likely transitions are sequential. The less likely
181
// transitions are not. But I don't believe there exists an order that's good
182
// for both the likely and the unlikely transitions. Do let me know if I'm
183
// wrong.
184
//
185
// In this example the transitions form a single chain (it turns out this is
186
// often the case). But for example for a step-size of 4.3 we get
187
//   0 -> 4 or 3 (70%)   0 -> 5 or 4 (30%)
188
//   1 -> 5 or 2         1 -> 6 or 3
189
//   2 -> 6 or 1         2 -> 7 or 2
190
//   3 -> 7 or 0         3 -> 7 or 1
191
//   4 -> 7 or 0         4 -> 6 or 0
192
//   5 -> 6 or 1         5 -> 5 or 0
193
//   6 -> 5 or 2         6 -> 4 or 1
194
//   7 -> 4 or 3         7 -> 3 or 2
195
// Only looking at the more likely transitions, we get 2 cycles of length 4:
196
//   0, 4, 7, 3
197
//   1, 5, 6, 2
198
//
199
// So the previous example gave a single chain with 2 clear end-points. Now we
200
// have 2 separate cycles. It turns out that for any possible step-size we
201
// either get a single chain or k cycles of size N/k. (So e.g. a chain of
202
// length 5 plus a cycle of length 3 is impossible. Also 1 cycle of length 4
203
// plus 2 cycles of length 2 is impossible). To be honest I've only partially
204
// mathematically proven this, but at least I've verified it for N=16 and
205
// N=4096 for all possible step-sizes.
206
//
207
// To linearize a chain in memory there are only 2 (good) possibilities: start
208
// at either end-point. But to store a cycle any point is as good as any other.
209
// Also the order in which to store the cycles themselves can still be chosen.
210
//
211
// Let's come back to the example with step-size 4.3. If we linearize this as
212
//   | 0, 4, 7, 3 | 1, 5, 6, 2 |
213
// then most of the more likely transitions are sequential. The exceptions are
214
//     0 <-> 3   and   1 <-> 2
215
// but those are unavoidable with cycles. In return 2 of the less likely
216
// transitions '3 <-> 1' are now sequential. I believe this is the best
217
// possible linearization (better said: there are other linearizations that are
218
// equally good, but none is better). But do let me know if you find a better
219
// one!
220
//
221
// For step-size '8.4' an optimal(?) linearization seems to be
222
//   | 0, 7 | 1, 6 | 2, 5 | 3, 4 |
223
// For step-size '7.9' the order is:
224
//   | 7, 0 | 6, 1 | 5, 2 | 4, 3 |
225
// And for step-size '3.8':
226
//   | 7, 4, 0, 3 | 6, 5, 1, 2 |
227
//
228
// I've again not (fully) mathematically proven it, but it seems we can
229
// optimally(?) linearize cycles by:
230
// * if likely step < unlikely step:
231
//    pick unassigned rows from 0 to N/2-1, and complete each cycle
232
// * if likely step > unlikely step:
233
//    pick unassigned rows from N/2-1 to 0, and complete each cycle
234
//
235
// The routine calcPermute() below calculates these optimal(?) linearizations.
236
// More in detail it calculates a permutation table: the i-th element in this
237
// table tells where in memory the i-th logical row of the original (half)
238
// resample coefficient table is physically stored.
239
240
static constexpr unsigned N = TAB_LEN;
241
static constexpr unsigned N1 = N - 1;
242
static constexpr unsigned N2 = N / 2;
243
244
static constexpr unsigned mapIdx(unsigned x)
245
{
246
	unsigned t = x & N1; // first wrap
247
	return (t < N2) ? t : N1 - t; // then fold
248
}
249
250
static constexpr std::pair<unsigned, unsigned> next(unsigned x, unsigned step)
251
{
252
	return {mapIdx(x + step), mapIdx(N1 - x + step)};
253
}
254
255
static void calcPermute(double ratio, std::span<int16_t, HALF_TAB_LEN> permute)
256
{
257
	double r2 = ratio * N;
258
	double fract = Math::fract(r2);
259
	auto step = narrow_cast<unsigned>(floor(r2));
260
	bool incr = [&] {
261
		if (fract > 0.5) {
262
			// mostly (> 50%) take steps of 'floor(r2) + 1'
263
			step += 1;
264
			return false; // assign from high to low
265
		} else {
266
			// mostly take steps of 'floor(r2)'
267
			return true; // assign from low to high
268
		}
269
	}();
270
271
	// initially set all as unassigned
272
	std::ranges::fill(permute, -1);
273
274
	unsigned restart = incr ? 0 : N2 - 1;
275
	unsigned curr = restart;
276
	// check for chain (instead of cycles)
277
	if (incr) {
278
		for (auto i : xrange(N2)) {
279
			auto [nxt1, nxt2] = next(i, step);
280
			if ((nxt1 == i) || (nxt2 == i)) { curr = i; break; }
281
		}
282
	} else {
283
		for (unsigned i = N2 - 1; int(i) >= 0; --i) {
284
			auto [nxt1, nxt2] = next(i, step);
285
			if ((nxt1 == i) || (nxt2 == i)) { curr = i; break; }
286
		}
287
	}
288
289
	// assign all rows (in chain of cycle(s))
290
	unsigned cnt = 0;
291
	while (true) {
292
		assert(permute[curr] == -1);
293
		assert(cnt < N2);
294
		permute[curr] = narrow<int16_t>(cnt++);
295
296
		auto [nxt1, nxt2] = next(curr, step);
297
		if (permute[nxt1] == -1) {
298
			curr = nxt1;
299
			continue;
300
		} else if (permute[nxt2] == -1) {
301
			curr = nxt2;
302
			continue;
303
		}
304
305
		// finished chain or cycle
306
		if (cnt == N2) break; // done
307
308
		// continue with next cycle
309
		while (permute[restart] != -1) {
310
			if (incr) {
311
				++restart;
312
				assert(restart != N2);
313
			} else {
314
				assert(restart != 0);
315
				--restart;
316
			}
317
		}
318
		curr = restart;
319
	}
320
321
#ifdef DEBUG
322
	std::array<int16_t, N2> testPerm;
323
	ranges::iota(testPerm, int16_t(0));
324
	assert(std::is_permutation(permute.begin(), permute.end(), testPerm.begin()));
325
#endif
326
}
327
328
static constexpr double getCoeff(FilterIndex index)
329
{
330
	double fraction = index.fractionAsDouble();
331
	int indx = index.toInt();
332
	return double(coeffs[indx]) +
333
	       fraction * (double(coeffs[indx + 1]) - double(coeffs[indx]));
334
}
335
336
ResampleCoeffs::Table ResampleCoeffs::calcTable(
337
	double ratio, std::span<int16_t, HALF_TAB_LEN> permute, unsigned& filterLen)
338
{
339
	calcPermute(ratio, permute);
340
341
	double floatIncr = (ratio > 1.0) ? INDEX_INC / ratio : INDEX_INC;
342
	double normFactor = floatIncr / INDEX_INC;
343
	auto increment = FilterIndex(floatIncr);
344
	FilterIndex maxFilterIndex(COEFF_HALF_LEN);
345
346
	int min_idx = -maxFilterIndex.divAsInt(increment);
347
	int max_idx = 1 + (maxFilterIndex - (increment - FilterIndex(floatIncr))).divAsInt(increment);
348
	int idx_cnt = max_idx - min_idx + 1;
349
	filterLen = (idx_cnt + 3) & ~3; // round up to multiple of 4
350
	min_idx -= (narrow<int>(filterLen) - idx_cnt) / 2;
351
	Table table(HALF_TAB_LEN * filterLen);
352
	std::ranges::fill(std::span{table}, 0.0f);
353
354
	for (auto t : xrange(HALF_TAB_LEN)) {
355
		float* tab = &table[permute[t] * filterLen];
356
		double lastPos = (double(t) + 0.5) / TAB_LEN;
357
		FilterIndex startFilterIndex(lastPos * floatIncr);
358
359
		FilterIndex filterIndex(startFilterIndex);
360
		int coeffCount = (maxFilterIndex - filterIndex).divAsInt(increment);
361
		filterIndex += increment * coeffCount;
362
		int bufIndex = -coeffCount;
363
		do {
364
			tab[bufIndex - min_idx] =
365
				float(getCoeff(filterIndex) * normFactor);
366
			filterIndex -= increment;
367
			bufIndex += 1;
368
		} while (filterIndex >= FilterIndex(0));
369
370
		filterIndex = increment - startFilterIndex;
371
		coeffCount = (maxFilterIndex - filterIndex).divAsInt(increment);
372
		filterIndex += increment * coeffCount;
373
		bufIndex = 1 + coeffCount;
374
		do {
375
			tab[bufIndex - min_idx] =
376
				float(getCoeff(filterIndex) * normFactor);
377
			filterIndex -= increment;
378
			bufIndex -= 1;
379
		} while (filterIndex > FilterIndex(0));
380
	}
381
	return table;
382
}
383
384
static const std::array<int16_t, HALF_TAB_LEN> dummyPermute = {};
385
386
template<unsigned CHANNELS>
387
ResampleHQ<CHANNELS>::ResampleHQ(
388
		ResampledSoundDevice& input_, const DynamicClock& hostClock_)
389
	: ResampleAlgo(input_)
390
	, hostClock(hostClock_)
391
	, ratio(float(hostClock.getPeriod().toDouble() / getEmuClock().getPeriod().toDouble()))
392
	, permute(dummyPermute) // Any better way to do this? (that also works with debug-STL)
393
{
394
	ResampleCoeffs::instance().getCoeffs(double(ratio), permute, table, filterLen);
395
396
	// fill buffer with 'enough' zero's
397
	unsigned extra = filterLen + 1 + narrow_cast<int>(ratio) + 1;
398
	bufStart = 0;
399
	bufEnd   = extra;
400
	size_t initialSize = 4000; // buffer grows dynamically if this is too small
401
	buffer.resize((initialSize + extra) * CHANNELS); // zero-initialized
402
}
403
404
template<unsigned CHANNELS>
405
ResampleHQ<CHANNELS>::~ResampleHQ()
406
{
407
	ResampleCoeffs::instance().releaseCoeffs(double(ratio));
408
}
409
410
#ifdef __SSE2__
411
412
static inline __m128 reverse(__m128 x)
413
{
414
	return _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3));
415
}
416
417
template<bool REVERSE>
418
static inline void calcSseMono(const float* buf_, const float* tab_, size_t len, float* out)
419
{
420
	assert((len % 4) == 0);
421
	assert((uintptr_t(tab_) % 16) == 0);
422
423
	auto x = narrow<ptrdiff_t>((len & ~7) * sizeof(float));
424
	assert((x % 32) == 0);
425
	const char* buf = std::bit_cast<const char*>(buf_) + x;
426
	const char* tab = std::bit_cast<const char*>(tab_) + (REVERSE ? -x : x);
427
	x = -x;
428
429
	__m128 a0 = _mm_setzero_ps();
430
	__m128 a1 = _mm_setzero_ps();
431
	do {
432
		__m128 b0 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x +  0));
433
		__m128 b1 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x + 16));
434
		__m128 t0, t1;
435
		if constexpr (REVERSE) {
436
			t0 = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - x - 16)));
437
			t1 = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - x - 32)));
438
		} else {
439
			t0 = _mm_loadu_ps (std::bit_cast<const float*>(tab + x +  0));
440
			t1 = _mm_loadu_ps (std::bit_cast<const float*>(tab + x + 16));
441
		}
442
		__m128 m0 = _mm_mul_ps(b0, t0);
443
		__m128 m1 = _mm_mul_ps(b1, t1);
444
		a0 = _mm_add_ps(a0, m0);
445
		a1 = _mm_add_ps(a1, m1);
446
		x += 2 * sizeof(__m128);
447
	} while (x < 0);
448
	if (len & 4) {
449
		__m128 b0 = _mm_loadu_ps(std::bit_cast<const float*>(buf));
450
		__m128 t0;
451
		if constexpr (REVERSE) {
452
			t0 = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - 16)));
453
		} else {
454
			t0 = _mm_loadu_ps (std::bit_cast<const float*>(tab));
455
		}
456
		__m128 m0 = _mm_mul_ps(b0, t0);
457
		a0 = _mm_add_ps(a0, m0);
458
	}
459
460
	__m128 a = _mm_add_ps(a0, a1);
461
	// The following can be _slightly_ faster by using the SSE3 _mm_hadd_ps()
462
	// intrinsic, but not worth the trouble.
463
	__m128 t = _mm_add_ps(a, _mm_movehl_ps(a, a));
464
	__m128 s = _mm_add_ss(t, _mm_shuffle_ps(t, t, 1));
465
466
	_mm_store_ss(out, s);
467
}
468
469
template<int N> static inline __m128 shuffle(__m128 x)
470
{
471
	return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x), N));
472
}
473
template<bool REVERSE>
474
static inline void calcSseStereo(const float* buf_, const float* tab_, size_t len, float* out)
475
{
476
	assert((len % 4) == 0);
477
	assert((uintptr_t(tab_) % 16) == 0);
478
479
	auto x = narrow<ptrdiff_t>(2 * (len & ~7) * sizeof(float));
480
	const auto* buf = std::bit_cast<const char*>(buf_) + x;
481
	const auto* tab = std::bit_cast<const char*>(tab_);
482
	x = -x;
483
484
	__m128 a0 = _mm_setzero_ps();
485
	__m128 a1 = _mm_setzero_ps();
486
	__m128 a2 = _mm_setzero_ps();
487
	__m128 a3 = _mm_setzero_ps();
488
	do {
489
		__m128 b0 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x +  0));
490
		__m128 b1 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x + 16));
491
		__m128 b2 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x + 32));
492
		__m128 b3 = _mm_loadu_ps(std::bit_cast<const float*>(buf + x + 48));
493
		__m128 ta, tb;
494
		if constexpr (REVERSE) {
495
			ta = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - 16)));
496
			tb = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - 32)));
497
			tab -= 2 * sizeof(__m128);
498
		} else {
499
			ta = _mm_loadu_ps (std::bit_cast<const float*>(tab +  0));
500
			tb = _mm_loadu_ps (std::bit_cast<const float*>(tab + 16));
501
			tab += 2 * sizeof(__m128);
502
		}
503
		__m128 t0 = shuffle<0x50>(ta);
504
		__m128 t1 = shuffle<0xFA>(ta);
505
		__m128 t2 = shuffle<0x50>(tb);
506
		__m128 t3 = shuffle<0xFA>(tb);
507
		__m128 m0 = _mm_mul_ps(b0, t0);
508
		__m128 m1 = _mm_mul_ps(b1, t1);
509
		__m128 m2 = _mm_mul_ps(b2, t2);
510
		__m128 m3 = _mm_mul_ps(b3, t3);
511
		a0 = _mm_add_ps(a0, m0);
512
		a1 = _mm_add_ps(a1, m1);
513
		a2 = _mm_add_ps(a2, m2);
514
		a3 = _mm_add_ps(a3, m3);
515
		x += 4 * sizeof(__m128);
516
	} while (x < 0);
517
	if (len & 4) {
518
		__m128 b0 = _mm_loadu_ps(std::bit_cast<const float*>(buf +  0));
519
		__m128 b1 = _mm_loadu_ps(std::bit_cast<const float*>(buf + 16));
520
		__m128 ta;
521
		if constexpr (REVERSE) {
522
			ta = reverse(_mm_loadu_ps(std::bit_cast<const float*>(tab - 16)));
523
		} else {
524
			ta = _mm_loadu_ps (std::bit_cast<const float*>(tab +  0));
525
		}
526
		__m128 t0 = shuffle<0x50>(ta);
527
		__m128 t1 = shuffle<0xFA>(ta);
528
		__m128 m0 = _mm_mul_ps(b0, t0);
529
		__m128 m1 = _mm_mul_ps(b1, t1);
530
		a0 = _mm_add_ps(a0, m0);
531
		a1 = _mm_add_ps(a1, m1);
532
	}
533
534
	__m128 a01 = _mm_add_ps(a0, a1);
535
	__m128 a23 = _mm_add_ps(a2, a3);
536
	__m128 a   = _mm_add_ps(a01, a23);
537
	// Can faster with SSE3, but (like above) not worth the trouble.
538
	__m128 s = _mm_add_ps(a, _mm_movehl_ps(a, a));
539
	_mm_store_ss(&out[0], s);
540
	_mm_store_ss(&out[1], shuffle<0x55>(s));
541
}
542
543
#endif
544
545
template<unsigned CHANNELS>
546
void ResampleHQ<CHANNELS>::calcOutput(
547
	float pos, float* __restrict output)
548
{
549
	assert((filterLen & 3) == 0);
550
551
	int bufIdx = int(pos) + bufStart;
552
	assert((bufIdx + filterLen) <= bufEnd);
553
	bufIdx *= CHANNELS;
554
	const float* buf = &buffer[bufIdx];
555
556
	auto t = size_t(lrintf(pos * TAB_LEN)) % TAB_LEN;
557
	if (!(t & HALF_TAB_LEN)) {
558
		// first half, begin of row 't'
559
		t = permute[t];
560
		const float* tab = &table[t * filterLen];
561
562
#ifdef __SSE2__
563
		if constexpr (CHANNELS == 1) {
564
			calcSseMono  <false>(buf, tab, filterLen, output);
565
		} else {
566
			calcSseStereo<false>(buf, tab, filterLen, output);
567
		}
568
		return;
569
#endif
570
571
		// c++ version, both mono and stereo
572
		for (auto ch : xrange(CHANNELS)) {
573
			float r0 = 0.0f;
574
			float r1 = 0.0f;
575
			float r2 = 0.0f;
576
			float r3 = 0.0f;
577
			for (size_t i = 0; i < filterLen; i += 4) {
578
				r0 += tab[i + 0] * buf[CHANNELS * (i + 0)];
579
				r1 += tab[i + 1] * buf[CHANNELS * (i + 1)];
580
				r2 += tab[i + 2] * buf[CHANNELS * (i + 2)];
581
				r3 += tab[i + 3] * buf[CHANNELS * (i + 3)];
582
			}
583
			output[ch] = r0 + r1 + r2 + r3;
584
			++buf;
585
		}
586
	} else {
587
		// 2nd half, end of row 'TAB_LEN - 1 - t'
588
		t = permute[TAB_LEN - 1 - t];
589
		const float* tab = &table[(t + 1) * filterLen];
590
591
#ifdef __SSE2__
592
		if constexpr (CHANNELS == 1) {
593
			calcSseMono  <true>(buf, tab, filterLen, output);
594
		} else {
595
			calcSseStereo<true>(buf, tab, filterLen, output);
596
		}
597
		return;
598
#endif
599
600
		// c++ version, both mono and stereo
601
		for (auto ch : xrange(CHANNELS)) {
602
			float r0 = 0.0f;
603
			float r1 = 0.0f;
604
			float r2 = 0.0f;
605
			float r3 = 0.0f;
606
			for (ptrdiff_t i = 0; i < ptrdiff_t(filterLen); i += 4) {
607
				r0 += tab[-i - 1] * buf[CHANNELS * (i + 0)];
608
				r1 += tab[-i - 2] * buf[CHANNELS * (i + 1)];
609
				r2 += tab[-i - 3] * buf[CHANNELS * (i + 2)];
610
				r3 += tab[-i - 4] * buf[CHANNELS * (i + 3)];
611
			}
612
			output[ch] = r0 + r1 + r2 + r3;
613
			++buf;
614
		}
615
	}
616
}
617
618
template<unsigned CHANNELS>
619
void ResampleHQ<CHANNELS>::prepareData(unsigned emuNum)
620
{
621
	// Still enough free space at end of buffer?
622
	unsigned free = unsigned(buffer.size() / CHANNELS) - bufEnd;
623
	if (free < emuNum) {
624
		// No, then move everything to the start
625
		// (data needs to be in a contiguous memory block)
626
		unsigned available = bufEnd - bufStart;
627
		memmove(&buffer[0], &buffer[bufStart * size_t(CHANNELS)],
628
			available * size_t(CHANNELS) * sizeof(float));
629
		bufStart = 0;
630
		bufEnd = available;
631
632
		free = unsigned(buffer.size() / CHANNELS) - bufEnd;
633
		auto missing = narrow_cast<int>(emuNum - free);
634
		if (missing > 0) [[unlikely]] {
635
			// Still not enough room: grow the buffer.
636
			// TODO an alternative is to instead of using a large
637
			// buffer, chop the work in multiple smaller pieces.
638
			// That may have the advantage that the data fits in
639
			// the CPU's data cache. OTOH too small chunks have
640
			// more overhead. (Not yet implemented because it's
641
			// more complex).
642
			buffer.resize(buffer.size() + missing * size_t(CHANNELS));
643
		}
644
	}
645
	small_buffer<float, 8192> tmpBufExtra(uninitialized_tag{}, emuNum * CHANNELS + 3); // typical ~5194 (PSG, samples=1024) but could be larger
646
	auto tmpBuf = subspan(tmpBufExtra, 0, emuNum * CHANNELS);
647
	if (input.generateInput(tmpBufExtra.data(), emuNum)) {
648
		copy_to_range(tmpBuf,
649
		              subspan(buffer, bufEnd * CHANNELS));
650
		bufEnd += emuNum;
651
		nonzeroSamples = bufEnd - bufStart;
652
	} else {
653
		std::ranges::fill(subspan(buffer, bufEnd * CHANNELS, emuNum * CHANNELS), 0);
654
		bufEnd += emuNum;
655
	}
656
657
	assert(bufStart <= bufEnd);
658
	assert(bufEnd <= (buffer.size() / CHANNELS));
659
}
660
661
template<unsigned CHANNELS>
662
bool ResampleHQ<CHANNELS>::generateOutputImpl(
663
	float* __restrict dataOut, size_t hostNum, EmuTime time)
664
{
665
	auto& emuClk = getEmuClock();
666
	unsigned emuNum = emuClk.getTicksTill(time);
667
	if (emuNum > 0) {
668
		prepareData(emuNum);
669
	}
670
671
	bool notMuted = nonzeroSamples > 0;
672
	if (notMuted) {
673
		// main processing loop
674
		EmuTime host1 = hostClock.getFastAdd(1);
675
		assert(host1 > emuClk.getTime());
676
		auto pos = narrow_cast<float>(emuClk.getTicksTillDouble(host1));
677
		assert(pos <= (ratio + 2));
678
		for (auto i : xrange(hostNum)) {
679
			calcOutput(pos, &dataOut[i * CHANNELS]);
680
			pos += ratio;
681
		}
682
	}
683
	emuClk += emuNum;
684
	bufStart += emuNum;
685
	nonzeroSamples = std::max<int>(0, nonzeroSamples - emuNum);
686
687
	assert(bufStart <= bufEnd);
688
	unsigned available = bufEnd - bufStart;
689
	unsigned extra = filterLen + 1 + narrow_cast<int>(ratio) + 1;
690
	assert(available == extra); (void)available; (void)extra;
691
692
	return notMuted;
693
}
694
695
// Force template instantiation.
696
template class ResampleHQ<1>;
697
template class ResampleHQ<2>;
698
699
} // namespace openmsx