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