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 unsigned emuNum = emuClk.getTicksTill(time);
34 if (emuNum > 0) {
35 // 3 extra for padding, CHANNELS extra for sentinel
36 // Clang will produce a link error if the length expression is put
37 // inside the macro.
38 const unsigned len = emuNum * CHANNELS + std::max(3u, CHANNELS);
39 VLA_SSE_ALIGNED(float, buf, len);
40 EmuTime emu1 = emuClk.getFastAdd(1); // time of 1st emu-sample
41 assert(emu1 > hostClock.getTime());
42 if (input.generateInput(buf.data(), emuNum)) {
43 FP pos1;
44 hostClock.getTicksTill(emu1, pos1);
45 for (auto ch : xrange(CHANNELS)) {
46 // In case of PSG (and to a lesser degree SCC) it happens
47 // very often that two consecutive samples have the same
48 // value. We can benefit from this by setting a sentinel
49 // at the end of the buffer and move the end-of-loop test
50 // into the 'samples differ' branch.
51 assert(emuNum > 0);
52 buf[CHANNELS * emuNum + ch] =
53 buf[CHANNELS * (emuNum - 1) + ch] + 1.0f;
54 FP pos = pos1;
55 auto last = lastInput[ch]; // local var is slightly faster
56 for (unsigned i = 0; ; ++i) {
57 auto delta = buf[CHANNELS * i + ch] - last;
58 if (delta != 0) [[unlikely]] {
59 if (i == emuNum) {
60 break;
61 }
62 last = buf[CHANNELS * i + ch];
63 blip[ch].addDelta(
65 delta);
66 }
67 pos += step;
68 }
69 lastInput[ch] = last;
70 }
71 } else {
72 // input all zero
74 hostClock.getTicksTill(emu1, pos);
75 for (auto ch : xrange(CHANNELS)) {
76 if (lastInput[ch] != 0.0f) {
77 auto delta = -lastInput[ch];
78 lastInput[ch] = 0.0f;
79 blip[ch].addDelta(pos, delta);
80 }
81 }
82 }
83 emuClk += emuNum;
84 }
85
86 std::array<bool, CHANNELS> results;
87 for (auto ch : xrange(CHANNELS)) {
88 results[ch] = blip[ch].template readSamples<CHANNELS>(dataOut + ch, hostNum);
89 }
90 static_assert(CHANNELS == one_of(1u, 2u), "either mono or stereo");
91 if constexpr (CHANNELS == 1) {
92 return results[0];
93 } else {
94 if (results[0] == results[1]) {
95 // Both muted or both unmuted
96 return results[0];
97 } else {
98 // One channel muted, the other not.
99 // We have to set the muted channel to all-zero.
100 unsigned offset = results[0] ? 1 : 0;
101 for (auto i : xrange(hostNum)) {
102 dataOut[2 * i + offset] = 0.0f;
103 }
104 return true;
105 }
106 }
107}
108
109// Force template instantiation.
110template class ResampleBlip<1>;
111template class ResampleBlip<2>;
112
113} // 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:9
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