openMSX
ResampleBlip.cc
Go to the documentation of this file.
1#include "ResampleBlip.hh"
3#include "narrow.hh"
4#include "one_of.hh"
5#include "ranges.hh"
6#include "vla.hh"
7#include "xrange.hh"
8#include <array>
9#include <cassert>
10
11namespace openmsx {
12
13template<unsigned CHANNELS>
15 ResampledSoundDevice& input_, const DynamicClock& hostClock_)
16 : ResampleAlgo(input_)
17 , hostClock(hostClock_)
18 , step([&]{ // calculate 'hostClock.getFreq() / getEmuClock().getFreq()', but with less rounding errors
19 uint64_t emuPeriod = input_.getEmuClock().getPeriod().length(); // unknown units
20 uint64_t hostPeriod = hostClock.getPeriod().length(); // unknown units, but same as above
21 return FP::roundRatioDown(narrow<unsigned>(emuPeriod),
22 narrow<unsigned>(hostPeriod));
23 }())
24{
25 ranges::fill(lastInput, 0.0f);
26}
27
28template<unsigned CHANNELS>
29bool ResampleBlip<CHANNELS>::generateOutputImpl(float* dataOut, size_t hostNum,
30 EmuTime::param time)
31{
32 auto& emuClk = getEmuClock();
33 if (unsigned emuNum = emuClk.getTicksTill(time); emuNum > 0) {
34 // 3 extra for padding, CHANNELS extra for sentinel
35 // Clang will produce a link error if the length expression is put
36 // inside the macro.
37 const unsigned len = emuNum * CHANNELS + std::max(3u, CHANNELS);
38 VLA_SSE_ALIGNED(float, buf, len);
39 EmuTime emu1 = emuClk.getFastAdd(1); // time of 1st emu-sample
40 assert(emu1 > hostClock.getTime());
41 if (input.generateInput(buf.data(), emuNum)) {
42 FP pos1;
43 hostClock.getTicksTill(emu1, pos1);
44 for (auto ch : xrange(CHANNELS)) {
45 // In case of PSG (and to a lesser degree SCC) it happens
46 // very often that two consecutive samples have the same
47 // value. We can benefit from this by setting a sentinel
48 // at the end of the buffer and move the end-of-loop test
49 // into the 'samples differ' branch.
50 assert(emuNum > 0);
51 buf[CHANNELS * emuNum + ch] =
52 buf[CHANNELS * (emuNum - 1) + ch] + 1.0f;
53 FP pos = pos1;
54 auto last = lastInput[ch]; // local var is slightly faster
55 for (unsigned i = 0; ; ++i) {
56 if (auto delta = buf[CHANNELS * i + ch] - last;
57 delta != 0) [[unlikely]] {
58 if (i == emuNum) {
59 break;
60 }
61 last = buf[CHANNELS * i + ch];
62 blip[ch].addDelta(
64 delta);
65 }
66 pos += step;
67 }
68 lastInput[ch] = last;
69 }
70 } else {
71 // input all zero
73 hostClock.getTicksTill(emu1, pos);
74 for (auto ch : xrange(CHANNELS)) {
75 if (lastInput[ch] != 0.0f) {
76 auto delta = -lastInput[ch];
77 lastInput[ch] = 0.0f;
78 blip[ch].addDelta(pos, delta);
79 }
80 }
81 }
82 emuClk += emuNum;
83 }
84
85 std::array<bool, CHANNELS> results;
86 for (auto ch : xrange(CHANNELS)) {
87 results[ch] = blip[ch].template readSamples<CHANNELS>(dataOut + ch, hostNum);
88 }
89 static_assert(CHANNELS == one_of(1u, 2u), "either mono or stereo");
90 if constexpr (CHANNELS == 1) {
91 return results[0];
92 } else {
93 if (results[0] == results[1]) {
94 // Both muted or both unmuted
95 return results[0];
96 } else {
97 // One channel muted, the other not.
98 // We have to set the muted channel to all-zero.
99 unsigned offset = results[0] ? 1 : 0;
100 for (auto i : xrange(hostNum)) {
101 dataOut[2 * i + offset] = 0.0f;
102 }
103 return true;
104 }
105 }
106}
107
108// Force template instantiation.
109template class ResampleBlip<1>;
110template class ResampleBlip<2>;
111
112} // namespace openmsx
Represents a clock with a variable frequency.
EmuDuration getPeriod() const
Returns the length of one clock-cycle.
constexpr uint64_t length() const
static constexpr FixedPoint roundRatioDown(unsigned n, unsigned d)
Definition FixedPoint.hh:60
bool generateOutputImpl(float *dataOut, size_t num, EmuTime::param time) override
ResampleBlip(ResampledSoundDevice &input, const DynamicClock &hostClock)
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:305
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition vla.hh:50
constexpr auto xrange(T e)
Definition xrange.hh:132