openMSX
MSXMixer.cc
Go to the documentation of this file.
1 #include "MSXMixer.hh"
2 #include "Mixer.hh"
3 #include "SoundDevice.hh"
4 #include "MSXMotherBoard.hh"
6 #include "TclObject.hh"
7 #include "ThrottleManager.hh"
8 #include "GlobalSettings.hh"
9 #include "IntegerSetting.hh"
10 #include "StringSetting.hh"
11 #include "BooleanSetting.hh"
12 #include "CommandException.hh"
13 #include "AviRecorder.hh"
14 #include "Filename.hh"
15 #include "CliComm.hh"
16 #include "Math.hh"
17 #include "StringOp.hh"
18 #include "memory.hh"
19 #include "stl.hh"
20 #include "aligned.hh"
21 #include "unreachable.hh"
22 #include "vla.hh"
23 #include <algorithm>
24 #include <tuple>
25 #include <cmath>
26 #include <cstring>
27 #include <cassert>
28 
29 #ifdef __SSE2__
30 #include "emmintrin.h"
31 #endif
32 
33 using std::remove;
34 using std::string;
35 using std::vector;
36 
37 namespace openmsx {
38 
39 MSXMixer::SoundDeviceInfo::SoundDeviceInfo()
40 {
41 }
42 
43 MSXMixer::SoundDeviceInfo::SoundDeviceInfo(SoundDeviceInfo&& rhs)
44  : device (std::move(rhs.device))
45  , defaultVolume (std::move(rhs.defaultVolume))
46  , volumeSetting (std::move(rhs.volumeSetting))
47  , balanceSetting (std::move(rhs.balanceSetting))
48  , channelSettings(std::move(rhs.channelSettings))
49  , left1 (std::move(rhs.left1))
50  , right1 (std::move(rhs.right1))
51  , left2 (std::move(rhs.left2))
52  , right2 (std::move(rhs.right2))
53 {
54 }
55 
56 MSXMixer::SoundDeviceInfo& MSXMixer::SoundDeviceInfo::operator=(SoundDeviceInfo&& rhs)
57 {
58  device = std::move(rhs.device);
59  defaultVolume = std::move(rhs.defaultVolume);
60  volumeSetting = std::move(rhs.volumeSetting);
61  balanceSetting = std::move(rhs.balanceSetting);
62  channelSettings = std::move(rhs.channelSettings);
63  left1 = std::move(rhs.left1);
64  right1 = std::move(rhs.right1);
65  left2 = std::move(rhs.left2);
66  right2 = std::move(rhs.right2);
67  return *this;
68 }
69 
71 {
72 }
73 
75  : recordSetting(std::move(rhs.recordSetting))
76  , muteSetting (std::move(rhs.muteSetting))
77 {
78 }
79 
82 {
83  recordSetting = std::move(rhs.recordSetting);
84  muteSetting = std::move(rhs.muteSetting);
85  return *this;
86 }
87 
88 
89 MSXMixer::MSXMixer(Mixer& mixer_, MSXMotherBoard& motherBoard_,
90  GlobalSettings& globalSettings)
91  : Schedulable(motherBoard_.getScheduler())
92  , mixer(mixer_)
93  , motherBoard(motherBoard_)
94  , commandController(motherBoard.getMSXCommandController())
95  , masterVolume(mixer.getMasterVolume())
96  , speedSetting(globalSettings.getSpeedSetting())
97  , throttleManager(globalSettings.getThrottleManager())
98  , prevTime(getCurrentTime(), 44100)
99  , soundDeviceInfo(commandController.getMachineInfoCommand(), *this)
100  , recorder(nullptr)
101  , synchronousCounter(0)
102 {
103  hostSampleRate = 44100;
104  fragmentSize = 0;
105 
106  muteCount = 1;
107  unmute(); // calls Mixer::registerMixer()
108 
109  reschedule2();
110 
111  masterVolume.attach(*this);
112  speedSetting.attach(*this);
113  throttleManager.attach(*this);
114 }
115 
117 {
118  if (recorder) {
119  recorder->stop();
120  }
121  assert(infos.empty());
122 
123  throttleManager.detach(*this);
124  speedSetting.detach(*this);
125  masterVolume.detach(*this);
126 
127  mute(); // calls Mixer::unregisterMixer()
128 }
129 
130 void MSXMixer::registerSound(SoundDevice& device, double volume,
131  int balance, unsigned numChannels)
132 {
133  // TODO read volume/balance(mode) from config file
134  const string& name = device.getName();
135  SoundDeviceInfo info;
136  info.device = &device;
137  info.defaultVolume = volume;
138  info.volumeSetting = make_unique<IntegerSetting>(
139  commandController, name + "_volume",
140  "the volume of this sound chip", 75, 0, 100);
141  info.balanceSetting = make_unique<IntegerSetting>(
142  commandController, name + "_balance",
143  "the balance of this sound chip", balance, -100, 100);
144 
145  info.volumeSetting->attach(*this);
146  info.balanceSetting->attach(*this);
147 
148  for (unsigned i = 0; i < numChannels; ++i) {
149  SoundDeviceInfo::ChannelSettings channelSettings;
150  string ch_name = StringOp::Builder() << name << "_ch" << i + 1;
151 
152  channelSettings.recordSetting = make_unique<StringSetting>(
153  commandController, ch_name + "_record",
154  "filename to record this channel to",
155  "", Setting::DONT_SAVE);
156  channelSettings.recordSetting->attach(*this);
157 
158  channelSettings.muteSetting = make_unique<BooleanSetting>(
159  commandController, ch_name + "_mute",
160  "sets mute-status of individual sound channels",
161  false, Setting::DONT_SAVE);
162  channelSettings.muteSetting->attach(*this);
163 
164  info.channelSettings.push_back(std::move(channelSettings));
165  }
166 
167  device.setOutputRate(getSampleRate());
168  infos.push_back(std::move(info));
169  updateVolumeParams(infos.back());
170 
171  commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "add");
172 }
173 
175 {
176  auto it = find_if_unguarded(infos,
177  [&](const SoundDeviceInfo& i) { return i.device == &device; });
178  it->volumeSetting->detach(*this);
179  it->balanceSetting->detach(*this);
180  for (auto& s : it->channelSettings) {
181  s.recordSetting->detach(*this);
182  s.muteSetting->detach(*this);
183  }
184  if (it != (end(infos) - 1)) std::swap(*it, *(end(infos) - 1));
185  infos.pop_back();
186  commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "remove");
187 }
188 
189 void MSXMixer::setSynchronousMode(bool synchronous)
190 {
191  // TODO ATM synchronous is not used anymore
192  if (synchronous) {
193  ++synchronousCounter;
194  if (synchronousCounter == 1) {
195  setMixerParams(fragmentSize, hostSampleRate);
196  }
197  } else {
198  assert(synchronousCounter > 0);
199  --synchronousCounter;
200  if (synchronousCounter == 0) {
201  setMixerParams(fragmentSize, hostSampleRate);
202  }
203  }
204 }
205 
207 {
208  return synchronousCounter
209  ? 1.0
210  : speedSetting.getInt() / 100.0;
211 }
212 
214 {
215  union {
216  int16_t mixBuffer[8192 * 2];
217  int32_t dummy1; // make sure mixBuffer is also 32-bit aligned
218 #ifdef __SSE2__
219  __m128i dummy2; // and optionally also 128-bit
220 #endif
221  };
222 
223  unsigned count = prevTime.getTicksTill(time);
224  assert(count <= 8192);
225 
226  // call generate() even if count==0 and even if muted
227  generate(mixBuffer, time, count);
228 
229  if (!muteCount && fragmentSize) {
230  mixer.uploadBuffer(*this, mixBuffer, count);
231  }
232 
233  if (recorder) {
234  recorder->addWave(count, mixBuffer);
235  }
236 
237  prevTime += count;
238 }
239 
240 
241 // Various (inner) loops that multiply one buffer by a constant and add the
242 // result to a second buffer. Either buffer can be mono or stereo, so if
243 // necessary the mono buffer is expanded to stereo. It's possible the
244 // accumulation buffer is still empty (as-if it contains zeros), in that case
245 // we skip the accumulation step.
246 
247 // buf[0:n] *= f
248 static inline void mul(int32_t* buf, int n, int f)
249 {
250 #ifdef __arm__
251  // ARM assembly version
252  int32_t dummy1, dummy2;
253  asm volatile (
254  "0:\n\t"
255  "ldmia %[buf],{r3-r6}\n\t"
256  "mul r3,%[f],r3\n\t"
257  "mul r4,%[f],r4\n\t"
258  "mul r5,%[f],r5\n\t"
259  "mul r6,%[f],r6\n\t"
260  "stmia %[buf]!,{r3-r6}\n\t"
261  "subs %[n],%[n],#4\n\t"
262  "bgt 0b\n\t"
263  : [buf] "=r" (dummy1)
264  , [n] "=r" (dummy2)
265  : "[buf]" (buf)
266  , "[n]" (n)
267  , [f] "r" (f)
268  : "memory", "r3","r4","r5","r6"
269  );
270  return;
271 #endif
272 
273  // C++ version, unrolled 4x,
274  // this allows gcc/clang to do much better auto-vectorization
275  // Note that this can process upto 3 samples too many, but that's OK.
276  assume_SSE_aligned(buf);
277  int i = 0;
278  do {
279  buf[i + 0] *= f;
280  buf[i + 1] *= f;
281  buf[i + 2] *= f;
282  buf[i + 3] *= f;
283  i += 4;
284  } while (i < n);
285 }
286 
287 // acc[0:n] += mul[0:n] * f
288 static inline void mulAcc(
289  int32_t* __restrict acc, const int32_t* __restrict mul, int n, int f)
290 {
291 #ifdef __arm__
292  // ARM assembly version
293  int32_t dummy1, dummy2, dummy3;
294  asm volatile (
295  "0:\n\t"
296  "ldmia %[in]!,{r3,r4,r5,r6}\n\t"
297  "ldmia %[out],{r8,r9,r10,r12}\n\t"
298  "mla r3,%[f],r3,r8\n\t"
299  "mla r4,%[f],r4,r9\n\t"
300  "mla r5,%[f],r5,r10\n\t"
301  "mla r6,%[f],r6,r12\n\t"
302  "stmia %[out]!,{r3,r4,r5,r6}\n\t"
303  "subs %[n],%[n],#4\n\t"
304  "bgt 0b\n\t"
305  : [in] "=r" (dummy1)
306  , [out] "=r" (dummy2)
307  , [n] "=r" (dummy3)
308  : "[in]" (mul)
309  , "[out]" (acc)
310  , "[n]" (n)
311  , [f] "r" (f)
312  : "memory"
313  , "r3","r4","r5","r6"
314  , "r8","r9","r10","r12"
315  );
316  return;
317 #endif
318 
319  // C++ version, unrolled 4x, see comments above.
320  assume_SSE_aligned(acc);
321  assume_SSE_aligned(mul);
322  int i = 0;
323  do {
324  acc[i + 0] += mul[i + 0] * f;
325  acc[i + 1] += mul[i + 1] * f;
326  acc[i + 2] += mul[i + 2] * f;
327  acc[i + 3] += mul[i + 3] * f;
328  i += 4;
329  } while (i < n);
330 }
331 
332 // buf[0:2n+0:2] = buf[0:n] * l
333 // buf[1:2n+1:2] = buf[0:n] * r
334 static inline void mulExpand(int32_t* buf, int n, int l, int r)
335 {
336  int i = n;
337  do {
338  --i; // back-to-front
339  auto t = buf[i];
340  buf[2 * i + 0] = l * t;
341  buf[2 * i + 1] = r * t;
342  } while (i != 0);
343 }
344 
345 // acc[0:2n+0:2] += mul[0:n] * l
346 // acc[1:2n+1:2] += mul[0:n] * r
347 static inline void mulExpandAcc(
348  int32_t* __restrict acc, const int32_t* __restrict mul, int n,
349  int l, int r)
350 {
351  int i = 0;
352  do {
353  auto t = mul[i];
354  acc[2 * i + 0] += l * t;
355  acc[2 * i + 1] += r * t;
356  } while (++i < n);
357 }
358 
359 // buf[0:2n+0:2] = buf[0:2n+0:2] * l1 + buf[1:2n+1:2] * l2
360 // buf[1:2n+1:2] = buf[0:2n+0:2] * r1 + buf[1:2n+1:2] * r2
361 static inline void mulMix2(int32_t* buf, int n, int l1, int l2, int r1, int r2)
362 {
363  int i = 0;
364  do {
365  auto t1 = buf[2 * i + 0];
366  auto t2 = buf[2 * i + 1];
367  buf[2 * i + 0] = l1 * t1 + l2 * t2;
368  buf[2 * i + 1] = r1 * t1 + r2 * t2;
369  } while (++i < n);
370 }
371 
372 // acc[0:2n+0:2] += mul[0:2n+0:2] * l1 + mul[1:2n+1:2] * l2
373 // acc[1:2n+1:2] += mul[0:2n+0:2] * r1 + mul[1:2n+1:2] * r2
374 static inline void mulMix2Acc(
375  int32_t* __restrict acc, const int32_t* __restrict mul, int n,
376  int l1, int l2, int r1, int r2)
377 {
378  int i = 0;
379  do {
380  auto t1 = mul[2 * i + 0];
381  auto t2 = mul[2 * i + 1];
382  acc[2 * i + 0] += l1 * t1 + l2 * t2;
383  acc[2 * i + 1] += r1 * t1 + r2 * t2;
384  } while (++i < n);
385 }
386 
387 
388 // DC removal filter routines:
389 //
390 // formula:
391 // y(n) = x(n) - x(n-1) + R * y(n-1)
392 // with:
393 // R = 1 - (2*pi * cut-off-frequency / samplerate)
394 // we take R = 511/512
395 // 44100Hz --> cutt-off freq = 14Hz
396 // 22050Hz 7Hz
397 // Note1: we divide by 512 iso shift-right by 9 because we want
398 // to round towards zero (shift rounds to -inf).
399 // Note2: the input still needs to be divided by 512 (because of balance-
400 // multiplication), can be done together with the above division.
401 
402 // No new input, previous output was (non-zero) mono.
403 static inline int32_t filterMonoNull(
404  int32_t x0, int32_t y, int16_t* out, int n)
405 {
406  assert(n > 0);
407  y = ((511 * y) - x0) / 512;
408  auto s0 = Math::clipIntToShort(y);
409  out[0] = s0;
410  out[1] = s0;
411  for (int i = 1; i < n; ++i) {
412  y = (511 * y) / 512;
413  auto s = Math::clipIntToShort(y);
414  out[2 * i + 0] = s;
415  out[2 * i + 1] = s;
416  }
417  return y;
418 }
419 
420 // No new input, previous output was (non-zero) stereo.
421 static inline std::tuple<int32_t, int32_t> filterStereoNull(
422  int32_t xl0, int32_t xr0, int32_t yl, int32_t yr, int16_t* out, int n)
423 {
424  assert(n > 0);
425  yl = ((511 * yl) - xl0) / 512;
426  yr = ((511 * yr) - xr0) / 512;
427  out[0] = Math::clipIntToShort(yl);
428  out[1] = Math::clipIntToShort(yr);
429  for (int i = 1; i < n; ++i) {
430  yl = (511 * yl) / 512;
431  yr = (511 * yr) / 512;
432  out[2 * i + 0] = Math::clipIntToShort(yl);
433  out[2 * i + 1] = Math::clipIntToShort(yr);
434  }
435  return std::make_tuple(yl, yr);
436 }
437 
438 // New input is mono, previous output was also mono.
439 static inline std::tuple<int32_t, int32_t> filterMonoMono(
440  int32_t x0, int32_t y, void* buf, int n)
441 {
442  assert(n > 0);
443 #ifdef __arm__
444  // Note: there are two functional differences in the
445  // asm and c++ code below:
446  // - divide by 512 is replaced by ASR #9
447  // (different for negative numbers)
448  // - the outLeft variable is set to the clipped value
449  // Though this difference is very small, and we need
450  // the extra speed.
451  int32_t dummy1, dummy2, dummy3;
452  asm volatile (
453  "0:\n\t"
454  "rsb %[y],%[y],%[y],LSL #9\n\t" // y = (y0<<9)-y = 511y0
455  "rsb %[y],%[x],%[y]\n\t" // y = 511y0 - x0
456  "ldr %[x],[%[buf]]\n\t" // x = *(int*)buf
457  "add %[y],%[y],%[x]\n\t" // y = x - x0 + 511y0
458  "asrs %[y],%[y],#9\n\t" // y = (x-x0+511y0) >> 9
459  "lsls %[t],%[y],#16\n\t" // t = y << 16
460  "cmp %[y],%[t],ASR #16\n\t" // cond = y != (t >> 16)
461  "it ne\n\t" // if (cond)
462  "subne %[y],%[m],%[y],ASR #31\n\t" // then y = m - y>>31
463  "strh %[y],[%[buf]],#2\n\t" // *(short*)buf = y; buf += 2
464  "strh %[y],[%[buf]],#2\n\t" // *(short*)buf = y; buf += 2
465  "subs %[n],%[n],#1\n\t" // --n
466  "bne 0b\n\t" // while (n)
467  : [y] "=r" (y)
468  , [x] "=r" (x0)
469  , [buf] "=r" (dummy1)
470  , [n] "=r" (dummy2)
471  , [t] "=&r" (dummy3)
472  : "[y]" (y)
473  , "[x]" (x0)
474  , "[buf]" (buf)
475  , "[n]" (n)
476  , [m] "r" (0x7FFF)
477  : "memory"
478  );
479  return std::make_tuple(x0, y);
480 #endif
481 
482  // C++ version
483  const auto* in = static_cast<const int32_t*>(buf);
484  auto* out = static_cast< int16_t*>(buf);
485  int i = 0;
486  do {
487  auto x = in[i];
488  y = (x - x0 + (511 * y)) / 512;
489  x0 = x;
490  auto s = Math::clipIntToShort(y);
491  out[2 * i + 0] = s;
492  out[2 * i + 1] = s;
493  } while (++i < n);
494  return std::make_tuple(x0, y);
495 }
496 
497 // New input is mono, previous output was stereo
498 static inline std::tuple<int32_t, int32_t, int32_t> filterStereoMono(
499  int32_t xl0, int32_t xr0, int32_t yl, int32_t yr, void* buf, int n)
500 {
501  assert(n > 0);
502  const auto* in = static_cast<const int32_t*>(buf);
503  auto* out = static_cast< int16_t*>(buf);
504  auto x = in[0];
505  yl = (x - xl0 + (511 * yl)) / 512;
506  yr = (x - xr0 + (511 * yr)) / 512;
507  out[0] = Math::clipIntToShort(yl);
508  out[1] = Math::clipIntToShort(yr);
509  for (int i = 1; i < n; ++i) {
510  auto x1 = in[i];
511  yl = (x1 - x + (511 * yl)) / 512;
512  yr = (x1 - x + (511 * yr)) / 512;
513  x = x1;
514  out[2 * i + 0] = Math::clipIntToShort(yl);
515  out[2 * i + 1] = Math::clipIntToShort(yr);
516  }
517  return std::make_tuple(x, yl, yr);
518 }
519 
520 // New input is stereo, (previous output either mono/stereo)
521 static inline std::tuple<int32_t, int32_t, int32_t, int32_t> filterStereoStereo(
522  int32_t xl0, int32_t xr0, int32_t yl, int32_t yr,
523  const int32_t* in, int16_t* out, int n)
524 {
525  assert(n > 0);
526  int i = 0;
527  do {
528  auto xl = in[2 * i + 0];
529  auto xr = in[2 * i + 1];
530  yl = (xl - xl0 + (511 * yl)) / 512;
531  yr = (xr - xr0 + (511 * yr)) / 512;
532  xl0 = xl;
533  xr0 = xr;
534  out[2 * i + 0] = Math::clipIntToShort(yl);
535  out[2 * i + 1] = Math::clipIntToShort(yr);
536  } while (++i < n);
537  return std::make_tuple(xl0, xr0, yl, yr);
538 }
539 
540 // We have both mono and stereo input (and produce stereo output)
541 static inline std::tuple<int32_t, int32_t, int32_t, int32_t> filterBothStereo(
542  int32_t xl0, int32_t xr0, int32_t yl, int32_t yr,
543  const int32_t* inS, void* buf, int n)
544 {
545  assert(n > 0);
546  const auto* inM = static_cast<const int32_t*>(buf);
547  auto* out = static_cast< int16_t*>(buf);
548  int i = 0;
549  do {
550  auto m = inM[i];
551  auto xl = inS[2 * i + 0] + m;
552  auto xr = inS[2 * i + 1] + m;
553  yl = (xl - xl0 + (511 * yl)) / 512;
554  yr = (xr - xr0 + (511 * yr)) / 512;
555  xl0 = xl;
556  xr0 = xr;
557  out[2 * i + 0] = Math::clipIntToShort(yl);
558  out[2 * i + 1] = Math::clipIntToShort(yr);
559  } while (++i < n);
560  return std::make_tuple(xl0, xr0, yl, yr);
561 }
562 
563 
564 void MSXMixer::generate(int16_t* output, EmuTime::param time, unsigned samples)
565 {
566  // The code below is specialized for a lot of cases (before this
567  // routine was _much_ shorter). This is done because this routine
568  // ends up relatively high (top 5) in a profile run.
569  // After these specialization this routine runs about two times
570  // faster for the common cases (mono output or no sound at all).
571  // In total emulation time this gave a speedup of about 2%.
572 
573  // When samples==0, call updateBuffer() but skip all further processing
574  // (handling this as a special case allows to simply the code below).
575  if (samples == 0) {
576  int32_t dummyBuf[4];
577  for (auto& info : infos) {
578  info.device->updateBuffer(0, dummyBuf, time);
579  }
580  return;
581  }
582 
583  // +3 to allow processing samples in groups of 4 (and upto 3 samples
584  // more than requested).
585  VLA_SSE_ALIGNED(int32_t, stereoBuf, 2 * samples + 3);
586  VLA_SSE_ALIGNED(int32_t, tmpBuf, 2 * samples + 3);
587  // reuse 'output' as temporary storage
588  auto* monoBuf = reinterpret_cast<int32_t*>(output);
589 
590  static const unsigned HAS_MONO_FLAG = 1;
591  static const unsigned HAS_STEREO_FLAG = 2;
592  unsigned usedBuffers = 0;
593 
594  // FIXME: The Infos should be ordered such that all the mono
595  // devices are handled first
596  for (auto& info : infos) {
597  SoundDevice& device = *info.device;
598  int l1 = info.left1;
599  int r1 = info.right1;
600  if (!device.isStereo()) {
601  if (l1 == r1) {
602  if (!(usedBuffers & HAS_MONO_FLAG)) {
603  if (device.updateBuffer(samples, monoBuf, time)) {
604  usedBuffers |= HAS_MONO_FLAG;
605  mul(monoBuf, samples, l1);
606  }
607  } else {
608  if (device.updateBuffer(samples, tmpBuf, time)) {
609  mulAcc(monoBuf, tmpBuf, samples, l1);
610  }
611  }
612  } else {
613  if (!(usedBuffers & HAS_STEREO_FLAG)) {
614  if (device.updateBuffer(samples, stereoBuf, time)) {
615  usedBuffers |= HAS_STEREO_FLAG;
616  mulExpand(stereoBuf, samples, l1, r1);
617  }
618  } else {
619  if (device.updateBuffer(samples, tmpBuf, time)) {
620  mulExpandAcc(stereoBuf, tmpBuf, samples, l1, r1);
621  }
622  }
623  }
624  } else {
625  int l2 = info.left2;
626  int r2 = info.right2;
627  if (l1 == r2) {
628  assert(l2 == 0);
629  assert(r1 == 0);
630  if (!(usedBuffers & HAS_STEREO_FLAG)) {
631  if (device.updateBuffer(samples, stereoBuf, time)) {
632  usedBuffers |= HAS_STEREO_FLAG;
633  mul(stereoBuf, 2 * samples, l1);
634  }
635  } else {
636  if (device.updateBuffer(samples, tmpBuf, time)) {
637  mulAcc(stereoBuf, tmpBuf, 2 * samples, l1);
638  }
639  }
640  } else {
641  if (!(usedBuffers & HAS_STEREO_FLAG)) {
642  if (device.updateBuffer(samples, stereoBuf, time)) {
643  usedBuffers |= HAS_STEREO_FLAG;
644  mulMix2(stereoBuf, samples, l1, l2, r1, r2);
645  }
646  } else {
647  if (device.updateBuffer(samples, tmpBuf, time)) {
648  mulMix2Acc(stereoBuf, tmpBuf, samples, l1, l2, r1, r2);
649  }
650  }
651  }
652  }
653  }
654 
655  // DC removal filter
656  switch (usedBuffers) {
657  case 0: // no new input
658  if ((outLeft == outRight) && (prevLeft == prevRight)) {
659  if ((outLeft == 0) && (prevLeft == 0)) {
660  // Output was zero, new input is zero,
661  // after DC-filter output will still be zero.
662  memset(output, 0, 2 * samples * sizeof(int16_t));
663  } else {
664  // Output was not zero, but it was the same left and right.
665  outLeft = filterMonoNull(prevLeft, outLeft, output, samples);
666  outRight = outLeft;
667  }
668  } else {
669  std::tie(outLeft, outRight) = filterStereoNull(
670  prevLeft, prevRight, outLeft, outRight, output, samples);
671  }
672  prevLeft = prevRight = 0;
673  break;
674 
675  case HAS_MONO_FLAG: // only mono
676  assert(static_cast<void*>(monoBuf) == static_cast<void*>(output));
677  if ((outLeft == outRight) && (prevLeft == prevRight)) {
678  // previous output was also mono
679  std::tie(prevLeft, outLeft) = filterMonoMono(
680  prevLeft, outLeft, output, samples);
681  outRight = outLeft;
682  } else {
683  // previous output was stereo, rarely triggers but needed for correctness
684  std::tie(prevLeft, outLeft, outRight) = filterStereoMono(
685  prevLeft, prevRight, outLeft, outRight, output, samples);
686  }
687  prevRight = prevLeft;
688  break;
689 
690  case HAS_STEREO_FLAG: // only stereo
691  std::tie(prevLeft, prevRight, outLeft, outRight) = filterStereoStereo(
692  prevLeft, prevRight, outLeft, outRight, stereoBuf, output, samples);
693  break;
694 
695  default: // mono + stereo
696  assert(static_cast<void*>(monoBuf) == static_cast<void*>(output));
697  std::tie(prevLeft, prevRight, outLeft, outRight) = filterBothStereo(
698  prevLeft, prevRight, outLeft, outRight, stereoBuf, output, samples);
699  }
700 }
701 
703 {
704  return any_of(begin(infos), end(infos),
705  [](const SoundDeviceInfo& info) {
706  return info.device->isStereo() ||
707  info.balanceSetting->getInt() != 0; });
708 }
709 
711 {
712  if (muteCount == 0) {
713  mixer.unregisterMixer(*this);
714  }
715  ++muteCount;
716 }
717 
719 {
720  --muteCount;
721  if (muteCount == 0) {
722  prevLeft = outLeft = 0;
723  prevRight = outRight = 0;
724  mixer.registerMixer(*this);
725  }
726 }
727 
729 {
730  prevTime.reset(getCurrentTime());
731  prevTime.setFreq(hostSampleRate / getEffectiveSpeed());
732  reschedule();
733 }
734 void MSXMixer::reschedule()
735 {
737  reschedule2();
738 }
739 void MSXMixer::reschedule2()
740 {
741  unsigned size = (!muteCount && fragmentSize) ? fragmentSize : 512;
742  setSyncPoint(prevTime.getFastAdd(size));
743 }
744 
745 void MSXMixer::setMixerParams(unsigned newFragmentSize, unsigned newSampleRate)
746 {
747  // TODO old code checked that values did actually change,
748  // investigate if this optimization is worth it
749  hostSampleRate = newSampleRate;
750  fragmentSize = newFragmentSize;
751 
752  reInit(); // must come before call to setOutputRate()
753 
754  for (auto& info : infos) {
755  info.device->setOutputRate(newSampleRate);
756  }
757 }
758 
760 {
761  if ((recorder != nullptr) != (newRecorder != nullptr)) {
762  setSynchronousMode(newRecorder != nullptr);
763  }
764  recorder = newRecorder;
765 }
766 
767 void MSXMixer::update(const Setting& setting)
768 {
769  if (&setting == &masterVolume) {
770  updateMasterVolume();
771  } else if (&setting == &speedSetting) {
772  if (synchronousCounter == 0) {
773  setMixerParams(fragmentSize, hostSampleRate);
774  } else {
775  // Avoid calling reInit() while recording because
776  // each call causes a small hiccup in the sound (and
777  // while recording this call anyway has no effect).
778  // This was noticable while sliding the speed slider
779  // in catapult (becuase this causes many changes in
780  // the speed setting).
781  }
782  } else if (dynamic_cast<const IntegerSetting*>(&setting)) {
783  auto it = find_if_unguarded(infos,
784  [&](const SoundDeviceInfo& i) {
785  return (i.volumeSetting .get() == &setting) ||
786  (i.balanceSetting.get() == &setting); });
787  updateVolumeParams(*it);
788  } else if (dynamic_cast<const StringSetting*>(&setting)) {
789  changeRecordSetting(setting);
790  } else if (dynamic_cast<const BooleanSetting*>(&setting)) {
791  changeMuteSetting(setting);
792  } else {
793  UNREACHABLE;
794  }
795 }
796 
797 void MSXMixer::changeRecordSetting(const Setting& setting)
798 {
799  for (auto& info : infos) {
800  unsigned channel = 0;
801  for (auto& s : info.channelSettings) {
802  if (s.recordSetting.get() == &setting) {
803  info.device->recordChannel(
804  channel,
805  Filename(s.recordSetting->getString().str()));
806  return;
807  }
808  ++channel;
809  }
810  }
811  UNREACHABLE;
812 }
813 
814 void MSXMixer::changeMuteSetting(const Setting& setting)
815 {
816  for (auto& info : infos) {
817  unsigned channel = 0;
818  for (auto& s : info.channelSettings) {
819  if (s.muteSetting.get() == &setting) {
820  info.device->muteChannel(
821  channel, s.muteSetting->getBoolean());
822  return;
823  }
824  ++channel;
825  }
826  }
827  UNREACHABLE;
828 }
829 
830 void MSXMixer::update(const ThrottleManager& /*throttleManager*/)
831 {
832  //reInit();
833  // TODO Should this be removed?
834 }
835 
836 void MSXMixer::updateVolumeParams(SoundDeviceInfo& info)
837 {
838  int mVolume = masterVolume.getInt();
839  int dVolume = info.volumeSetting->getInt();
840  double volume = info.defaultVolume * mVolume * dVolume / (100.0 * 100.0);
841  int balance = info.balanceSetting->getInt();
842  double l1, r1, l2, r2;
843  if (info.device->isStereo()) {
844  if (balance < 0) {
845  double b = (balance + 100.0) / 100.0;
846  l1 = volume;
847  r1 = 0.0;
848  l2 = volume * sqrt(std::max(0.0, 1.0 - b));
849  r2 = volume * sqrt(std::max(0.0, b));
850  } else {
851  double b = balance / 100.0;
852  l1 = volume * sqrt(std::max(0.0, 1.0 - b));
853  r1 = volume * sqrt(std::max(0.0, b));
854  l2 = 0.0;
855  r2 = volume;
856  }
857  } else {
858  // make sure that in case of rounding errors
859  // we don't take sqrt() of negative numbers
860  double b = (balance + 100.0) / 200.0;
861  l1 = volume * sqrt(std::max(0.0, 1.0 - b));
862  r1 = volume * sqrt(std::max(0.0, b));
863  l2 = r2 = 0.0; // dummy
864  }
865  // 512 (9 bits) because in the DC filter we also have a factor 512, and
866  // using the same allows to fold both (later) divisions into one.
867  int amp = 512 * info.device->getAmplificationFactor();
868  info.left1 = int(l1 * amp);
869  info.right1 = int(r1 * amp);
870  info.left2 = int(l2 * amp);
871  info.right2 = int(r2 * amp);
872 }
873 
874 void MSXMixer::updateMasterVolume()
875 {
876  for (auto& p : infos) {
877  updateVolumeParams(p);
878  }
879 }
880 
881 void MSXMixer::executeUntil(EmuTime::param time)
882 {
883  updateStream(time);
884  reschedule2();
885 
886  // This method gets called very regularly, typically 44100/512 = 86x
887  // per second (even if sound is muted and even with sound_driver=null).
888  // This rate is constant in real-time (compared to e.g. the VDP sync
889  // points that are constant in emutime). So we can use this to
890  // regularly exit from the main CPU emulation loop. Without this there
891  // were problems like described in 'bug#563 Console very slow when
892  // setting speed to low values like 1'.
893  motherBoard.exitCPULoopSync();
894 }
895 
896 
897 // Sound device info
898 
900 {
901  auto it = find_if(begin(infos), end(infos),
902  [&](const SoundDeviceInfo& i) {
903  return i.device->getName() == name; });
904  return (it != end(infos)) ? it->device : nullptr;
905 }
906 
907 MSXMixer::SoundDeviceInfoTopic::SoundDeviceInfoTopic(
908  InfoCommand& machineInfoCommand, MSXMixer& mixer_)
909  : InfoTopic(machineInfoCommand, "sounddevice")
910  , mixer(mixer_)
911 {
912 }
913 
914 void MSXMixer::SoundDeviceInfoTopic::execute(
915  array_ref<TclObject> tokens, TclObject& result) const
916 {
917  switch (tokens.size()) {
918  case 2:
919  for (auto& info : mixer.infos) {
920  result.addListElement(info.device->getName());
921  }
922  break;
923  case 3: {
924  SoundDevice* device = mixer.findDevice(tokens[2].getString());
925  if (!device) {
926  throw CommandException("Unknown sound device");
927  }
928  result.setString(device->getDescription());
929  break;
930  }
931  default:
932  throw CommandException("Too many parameters");
933  }
934 }
935 
936 string MSXMixer::SoundDeviceInfoTopic::help(const vector<string>& /*tokens*/) const
937 {
938  return "Shows a list of available sound devices.\n";
939 }
940 
941 void MSXMixer::SoundDeviceInfoTopic::tabCompletion(vector<string>& tokens) const
942 {
943  if (tokens.size() == 3) {
944  vector<string_ref> devices;
945  for (auto& info : mixer.infos) {
946  devices.push_back(info.device->getName());
947  }
948  completeString(tokens, devices);
949  }
950 }
951 
952 } // namespace openmsx
void addWave(unsigned num, short *data)
Definition: AviRecorder.cc:117
void mute()
TODO This methods (un)mute the sound.
Definition: MSXMixer.cc:710
string_ref::const_iterator end(const string_ref &x)
Definition: string_ref.hh:150
size_type size() const
Definition: array_ref.hh:61
void unregisterSound(SoundDevice &device)
Every sounddevice must unregister before it is destructed.
Definition: MSXMixer.cc:174
unsigned getSampleRate() const
Definition: MSXMixer.hh:101
void uploadBuffer(MSXMixer &msxMixer, short *buffer, unsigned len)
Upload new sample data.
Definition: Mixer.cc:170
bool needStereoRecording() const
Definition: MSXMixer.cc:702
void setSynchronousMode(bool synchronous)
If we're recording, we want to emulate sound at 100% emutime speed.
Definition: MSXMixer.cc:189
STL namespace.
void updateStream(EmuTime::param time)
Use this method to force an 'early' call to all updateBuffer() methods.
Definition: MSXMixer.cc:213
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
void setMixerParams(unsigned fragmentSize, unsigned sampleRate)
Set new fragment size and sample frequency.
Definition: MSXMixer.cc:745
double getEffectiveSpeed() const
Returns the ratio of emutime-speed per realtime-speed.
Definition: MSXMixer.cc:206
int * mixBuffer
Definition: SoundDevice.cc:21
virtual void update(UpdateType type, string_ref name, string_ref value)=0
std::unique_ptr< StringSetting > recordSetting
Definition: MSXMixer.hh:123
int16_t clipIntToShort(int x)
Clip x to range [-32768,32767].
Definition: Math.hh:37
std::unique_ptr< BooleanSetting > muteSetting
Definition: MSXMixer.hh:124
void attach(Observer< T > &observer)
Definition: Subject.hh:52
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Definition: DynamicClock.hh:51
MSXMixer(Mixer &mixer, MSXMotherBoard &motherBoard, GlobalSettings &globalSettings)
Definition: MSXMixer.cc:89
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
Definition: DynamicClock.hh:86
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:49
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
const EmuTime & param
Definition: EmuTime.hh:20
void unregisterMixer(MSXMixer &mixer)
Unregister per-machine mixer.
Definition: Mixer.cc:132
void setRecorder(AviRecorder *recorder)
Definition: MSXMixer.cc:759
SoundDevice * findDevice(string_ref name) const
Definition: MSXMixer.cc:899
virtual void setOutputRate(unsigned sampleRate)=0
When a SoundDevice registers itself with the Mixer, the Mixer sets the required sampleRate through th...
const std::string & getName() const
Get the unique name that identifies this sound device.
Definition: SoundDevice.hh:25
void detach(Observer< T > &observer)
Definition: Subject.hh:58
void registerMixer(MSXMixer &mixer)
Register per-machine mixer.
Definition: Mixer.cc:125
ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to 'find_if' when it's guaranteed that the predicate will be true for at least one...
Definition: stl.hh:136
ChannelSettings & operator=(ChannelSettings &&rhs)
Definition: MSXMixer.cc:81
size_t size(string_ref utf8)
This class contains settings that are used by several other class (including some singletons)...
string_ref::const_iterator begin(const string_ref &x)
Definition: string_ref.hh:149
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
Scheduler & getScheduler() const
Definition: Schedulable.hh:56
void registerSound(SoundDevice &device, double volume, int balance, unsigned numChannels)
Use this method to register a given sounddevice.
Definition: MSXMixer.cc:130
EmuTime getFastAdd(unsigned n) const
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44
#define UNREACHABLE
Definition: unreachable.hh:35