openMSX
ResampleBlip.cc
Go to the documentation of this file.
1 #include "ResampleBlip.hh"
3 #include "likely.hh"
4 #include "one_of.hh"
5 #include "ranges.hh"
6 #include "vla.hh"
7 #include <cassert>
8 
9 namespace openmsx {
10 
11 template <unsigned CHANNELS>
13  ResampledSoundDevice& input_, const DynamicClock& hostClock_)
14  : ResampleAlgo(input_)
15  , hostClock(hostClock_)
16  , step([&]{ // calculate 'hostClock.getFreq() / getEmuClock().getFreq()', but with less rounding errors
17  uint64_t emuPeriod = input_.getEmuClock().getPeriod().length(); // unknown units
18  uint64_t hostPeriod = hostClock.getPeriod().length(); // unknown units, but same as above
19  assert(unsigned( emuPeriod) == emuPeriod);
20  assert(unsigned(hostPeriod) == hostPeriod);
21  return FP::roundRatioDown(emuPeriod, hostPeriod);
22  }())
23 {
24  ranges::fill(lastInput, 0.0f);
25 }
26 
27 template <unsigned CHANNELS>
28 bool ResampleBlip<CHANNELS>::generateOutputImpl(float* dataOut, unsigned hostNum,
29  EmuTime::param time)
30 {
31  auto& emuClk = getEmuClock();
32  unsigned emuNum = emuClk.getTicksTill(time);
33  if (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, emuNum)) {
42  FP pos1;
43  hostClock.getTicksTill(emu1, pos1);
44  for (unsigned ch = 0; ch < CHANNELS; ++ch) {
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  auto delta = buf[CHANNELS * i + ch] - last;
57  if (unlikely(delta != 0)) {
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 (unsigned ch = 0; ch < CHANNELS; ++ch) {
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  bool results[CHANNELS];
86  for (unsigned ch = 0; ch < CHANNELS; ++ch) {
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  bool result;
91  if (CHANNELS == 1) {
92  result = results[0];
93  } else {
94  if (results[0] == results[1]) {
95  // Both muted or both unmuted
96  result = 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 (unsigned i = 0; i < hostNum; ++i) {
102  dataOut[2 * i + offset] = 0.0f;
103  }
104  result = true;
105  }
106  }
107  return result;
108 }
109 
110 // Force template instantiation.
111 template class ResampleBlip<1>;
112 template class ResampleBlip<2>;
113 
114 } // namespace openmsx
one_of.hh
CHANNELS
constexpr unsigned CHANNELS
Definition: YM2413Test.cc:20
openmsx::ResampledSoundDevice
Definition: ResampledSoundDevice.hh:17
openmsx::ResampleBlip::ResampleBlip
ResampleBlip(ResampledSoundDevice &input, const DynamicClock &hostClock)
Definition: ResampleBlip.cc:12
unlikely
#define unlikely(x)
Definition: likely.hh:15
ResampleBlip.hh
ranges.hh
openmsx::EmuDuration::length
constexpr uint64_t length() const
Definition: EmuDuration.hh:50
step
constexpr auto step
Definition: eeprom.cc:9
likely.hh
openmsx::DynamicClock
Represents a clock with a variable frequency.
Definition: DynamicClock.hh:17
vla.hh
one_of
Definition: one_of.hh:7
openmsx::FixedPoint< 16 >::roundRatioDown
static constexpr FixedPoint roundRatioDown(unsigned n, unsigned d)
Definition: FixedPoint.hh:59
openmsx::ResampledSoundDevice::getEmuClock
DynamicClock & getEmuClock()
Definition: ResampledSoundDevice.hh:27
openmsx::DynamicClock::getPeriod
EmuDuration getPeriod() const
Returns the length of one clock-cycle.
Definition: DynamicClock.hh:131
openmsx::ResampleBlip::generateOutputImpl
bool generateOutputImpl(float *dataOut, unsigned num, EmuTime::param time) override
Definition: ResampleBlip.cc:28
openmsx::ResampleBlip
Definition: ResampleBlip.hh:14
gl::max
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:292
VLA_SSE_ALIGNED
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44
ranges::fill
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::ResampleAlgo
Definition: ResampleAlgo.hh:12
openmsx::FixedPoint< 16 >
ResampledSoundDevice.hh