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