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