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_,
14  const DynamicClock& hostClock_, unsigned emuSampleRate)
15  : ResampleAlgo(input_)
16  , hostClock(hostClock_)
17  , step(FP::roundRatioDown(hostClock.getFreq(), emuSampleRate))
18 {
19  ranges::fill(lastInput, 0.0f);
20 }
21 
22 template <unsigned CHANNELS>
23 bool ResampleBlip<CHANNELS>::generateOutputImpl(float* dataOut, unsigned hostNum,
24  EmuTime::param time)
25 {
26  auto& emuClk = getEmuClock();
27  unsigned emuNum = emuClk.getTicksTill(time);
28  if (emuNum > 0) {
29  // 3 extra for padding, CHANNELS extra for sentinel
30  // Clang will produce a link error if the length expression is put
31  // inside the macro.
32  const unsigned len = emuNum * CHANNELS + std::max(3u, CHANNELS);
33  VLA_SSE_ALIGNED(float, buf, len);
34  EmuTime emu1 = emuClk.getFastAdd(1); // time of 1st emu-sample
35  assert(emu1 > hostClock.getTime());
36  if (input.generateInput(buf, emuNum)) {
37  FP pos1;
38  hostClock.getTicksTill(emu1, pos1);
39  for (unsigned ch = 0; ch < CHANNELS; ++ch) {
40  // In case of PSG (and to a lesser degree SCC) it happens
41  // very often that two consecutive samples have the same
42  // value. We can benefit from this by setting a sentinel
43  // at the end of the buffer and move the end-of-loop test
44  // into the 'samples differ' branch.
45  assert(emuNum > 0);
46  buf[CHANNELS * emuNum + ch] =
47  buf[CHANNELS * (emuNum - 1) + ch] + 1.0f;
48  FP pos = pos1;
49  auto last = lastInput[ch]; // local var is slightly faster
50  for (unsigned i = 0; ; ++i) {
51  auto delta = buf[CHANNELS * i + ch] - last;
52  if (unlikely(delta != 0)) {
53  if (i == emuNum) {
54  break;
55  }
56  last = buf[CHANNELS * i + ch];
57  blip[ch].addDelta(
59  delta);
60  }
61  pos += step;
62  }
63  lastInput[ch] = last;
64  }
65  } else {
66  // input all zero
68  hostClock.getTicksTill(emu1, pos);
69  for (unsigned ch = 0; ch < CHANNELS; ++ch) {
70  if (lastInput[ch] != 0.0f) {
71  auto delta = -lastInput[ch];
72  lastInput[ch] = 0.0f;
73  blip[ch].addDelta(pos, delta);
74  }
75  }
76  }
77  emuClk += emuNum;
78  }
79 
80  bool results[CHANNELS];
81  for (unsigned ch = 0; ch < CHANNELS; ++ch) {
82  results[ch] = blip[ch].template readSamples<CHANNELS>(dataOut + ch, hostNum);
83  }
84  static_assert(CHANNELS == one_of(1u, 2u), "either mono or stereo");
85  bool result;
86  if (CHANNELS == 1) {
87  result = results[0];
88  } else {
89  if (results[0] == results[1]) {
90  // Both muted or both unmuted
91  result = results[0];
92  } else {
93  // One channel muted, the other not.
94  // We have to set the muted channel to all-zero.
95  unsigned offset = results[0] ? 1 : 0;
96  for (unsigned i = 0; i < hostNum; ++i) {
97  dataOut[2 * i + offset] = 0.0f;
98  }
99  result = true;
100  }
101  }
102  return result;
103 }
104 
105 // Force template instantiation.
106 template class ResampleBlip<1>;
107 template class ResampleBlip<2>;
108 
109 } // namespace openmsx
one_of.hh
CHANNELS
constexpr unsigned CHANNELS
Definition: YM2413Test.cc:20
openmsx::ResampledSoundDevice
Definition: ResampledSoundDevice.hh:16
unlikely
#define unlikely(x)
Definition: likely.hh:15
ResampleBlip.hh
ranges.hh
step
constexpr auto step
Definition: eeprom.cc:9
likely.hh
openmsx::DynamicClock
Represents a clock with a variable frequency.
Definition: DynamicClock.hh:16
vla.hh
one_of
Definition: one_of.hh:7
openmsx::ResampleBlip::ResampleBlip
ResampleBlip(ResampledSoundDevice &input, const DynamicClock &hostClock, unsigned emuSampleRate)
Definition: ResampleBlip.cc:12
openmsx::ResampleBlip::generateOutputImpl
bool generateOutputImpl(float *dataOut, unsigned num, EmuTime::param time) override
Definition: ResampleBlip.cc:23
openmsx::ResampleBlip
Definition: ResampleBlip.hh:13
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
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::ResampleAlgo
Definition: ResampleAlgo.hh:11
openmsx::FixedPoint< 16 >
ResampledSoundDevice.hh