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 "vla.hh"
19 #include "unreachable.hh"
20 #include "memory.hh"
21 #include <algorithm>
22 #include <cmath>
23 #include <cstring>
24 #include <cassert>
25 
26 using std::remove;
27 using std::string;
28 using std::vector;
29 
30 namespace openmsx {
31 
33 {
34 public:
35  SoundDeviceInfoTopic(InfoCommand& machineInfoCommand, MSXMixer& mixer);
36  virtual void execute(const vector<TclObject>& tokens,
37  TclObject& result) const;
38  virtual string help(const vector<string>& tokens) const;
39  virtual void tabCompletion(vector<string>& tokens) const;
40 private:
41  MSXMixer& mixer;
42 };
43 
44 
45 MSXMixer::SoundDeviceInfo::SoundDeviceInfo()
46 {
47 }
48 
49 MSXMixer::SoundDeviceInfo::SoundDeviceInfo(SoundDeviceInfo&& rhs)
50  : defaultVolume (std::move(rhs.defaultVolume))
51  , volumeSetting (std::move(rhs.volumeSetting))
52  , balanceSetting (std::move(rhs.balanceSetting))
53  , channelSettings(std::move(rhs.channelSettings))
54  , left1 (std::move(rhs.left1))
55  , right1 (std::move(rhs.right1))
56  , left2 (std::move(rhs.left2))
57  , right2 (std::move(rhs.right2))
58 {
59 }
60 
61 MSXMixer::SoundDeviceInfo& MSXMixer::SoundDeviceInfo::operator=(SoundDeviceInfo&& rhs)
62 {
63  defaultVolume = std::move(rhs.defaultVolume);
64  volumeSetting = std::move(rhs.volumeSetting);
65  balanceSetting = std::move(rhs.balanceSetting);
66  channelSettings = std::move(rhs.channelSettings);
67  left1 = std::move(rhs.left1);
68  right1 = std::move(rhs.right1);
69  left2 = std::move(rhs.left2);
70  right2 = std::move(rhs.right2);
71  return *this;
72 }
73 
75 {
76 }
77 
79  : recordSetting(std::move(rhs.recordSetting))
80  , muteSetting (std::move(rhs.muteSetting))
81 {
82 }
83 
86 {
87  recordSetting = std::move(rhs.recordSetting);
88  muteSetting = std::move(rhs.muteSetting);
89  return *this;
90 }
91 
92 
93 MSXMixer::MSXMixer(Mixer& mixer_, Scheduler& scheduler,
94  MSXCommandController& msxCommandController_,
95  GlobalSettings& globalSettings)
96  : Schedulable(scheduler)
97  , mixer(mixer_)
98  , commandController(msxCommandController_)
99  , masterVolume(mixer.getMasterVolume())
100  , speedSetting(globalSettings.getSpeedSetting())
101  , throttleManager(globalSettings.getThrottleManager())
102  , prevTime(getCurrentTime(), 44100)
103  , soundDeviceInfo(make_unique<SoundDeviceInfoTopic>(
104  msxCommandController_.getMachineInfoCommand(), *this))
105  , recorder(nullptr)
106  , synchronousCounter(0)
107 {
108  hostSampleRate = 44100;
109  fragmentSize = 0;
110 
111  muteCount = 1;
112  unmute(); // calls Mixer::registerMixer()
113 
114  reschedule2();
115 
116  masterVolume.attach(*this);
117  speedSetting.attach(*this);
118  throttleManager.attach(*this);
119 }
120 
122 {
123  if (recorder) {
124  recorder->stop();
125  }
126  assert(infos.empty());
127 
128  throttleManager.detach(*this);
129  speedSetting.detach(*this);
130  masterVolume.detach(*this);
131 
132  mute(); // calls Mixer::unregisterMixer()
133 }
134 
135 void MSXMixer::registerSound(SoundDevice& device, double volume,
136  int balance, unsigned numChannels)
137 {
138  // TODO read volume/balance(mode) from config file
139  const string& name = device.getName();
140  SoundDeviceInfo info;
141  info.defaultVolume = volume;
142  info.volumeSetting = make_unique<IntegerSetting>(
143  commandController, name + "_volume",
144  "the volume of this sound chip", 75, 0, 100);
145  info.balanceSetting = make_unique<IntegerSetting>(
146  commandController, name + "_balance",
147  "the balance of this sound chip", balance, -100, 100);
148 
149  info.volumeSetting->attach(*this);
150  info.balanceSetting->attach(*this);
151 
152  for (unsigned i = 0; i < numChannels; ++i) {
153  SoundDeviceInfo::ChannelSettings channelSettings;
154  string ch_name = StringOp::Builder() << name << "_ch" << i + 1;
155 
156  channelSettings.recordSetting = make_unique<StringSetting>(
157  commandController, ch_name + "_record",
158  "filename to record this channel to",
159  "", Setting::DONT_SAVE);
160  channelSettings.recordSetting->attach(*this);
161 
162  channelSettings.muteSetting = make_unique<BooleanSetting>(
163  commandController, ch_name + "_mute",
164  "sets mute-status of individual sound channels",
165  false, Setting::DONT_SAVE);
166  channelSettings.muteSetting->attach(*this);
167 
168  info.channelSettings.push_back(std::move(channelSettings));
169  }
170 
171  infos[&device] = std::move(info);
172  device.setOutputRate(getSampleRate());
173 
174  auto it = infos.find(&device);
175  assert(it != infos.end());
176  updateVolumeParams(*it);
177 
178  commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "add");
179 }
180 
182 {
183  auto it = infos.find(&device);
184  assert(it != infos.end());
185  it->second.volumeSetting->detach(*this);
186  it->second.balanceSetting->detach(*this);
187  for (auto& s : it->second.channelSettings) {
188  s.recordSetting->detach(*this);
189  s.muteSetting->detach(*this);
190  }
191  infos.erase(it);
192  commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "remove");
193 }
194 
195 void MSXMixer::setSynchronousMode(bool synchronous)
196 {
197  // TODO ATM synchronous is not used anymore
198  if (synchronous) {
199  ++synchronousCounter;
200  if (synchronousCounter == 1) {
201  setMixerParams(fragmentSize, hostSampleRate);
202  }
203  } else {
204  assert(synchronousCounter > 0);
205  --synchronousCounter;
206  if (synchronousCounter == 0) {
207  setMixerParams(fragmentSize, hostSampleRate);
208  }
209  }
210 }
211 
213 {
214  return synchronousCounter
215  ? 1.0
216  : speedSetting.getInt() / 100.0;
217 }
218 
220 {
221  unsigned count = prevTime.getTicksTill(time);
222 
223  // call generate() even if count==0 and even if muted
224  short mixBuffer[8192 * 2];
225  assert(count <= 8192);
226  generate(mixBuffer, time, count);
227 
228  if (!muteCount && fragmentSize) {
229  mixer.uploadBuffer(*this, mixBuffer, count);
230  }
231 
232  if (recorder) {
233  recorder->addWave(count, mixBuffer);
234  }
235 
236  prevTime += count;
237 }
238 
239 void MSXMixer::generate(short* output, EmuTime::param time, unsigned samples)
240 {
241  // The code below is specialized for a lot of cases (before this
242  // routine was _much_ shorter). This is done because this routine
243  // ends up relatively high (top 5) in a profile run.
244  // After these specialization this routine runs about two times
245  // faster for the common cases (mono output or no sound at all).
246  // In total emulation time this gave a speedup of about 2%.
247 
248  VLA(int, stereoBuf, 2 * samples + 3);
249  VLA(int, monoBuf, samples + 3);
250  VLA_SSE_ALIGNED(int, tmpBuf, 2 * samples + 3);
251 
252  static const unsigned HAS_MONO_FLAG = 1;
253  static const unsigned HAS_STEREO_FLAG = 2;
254  unsigned usedBuffers = 0;
255 
256  // FIXME: The Infos should be ordered such that all the mono
257  // devices are handled first
258  for (auto& p : infos) {
259  // When samples==0, call updateBuffer() but skip mixing
260  SoundDevice& device = *p.first;
261  if (device.updateBuffer(samples, tmpBuf, time) &&
262  (samples > 0)) {
263  if (!device.isStereo()) {
264  int l1 = p.second.left1;
265  int r1 = p.second.right1;
266  if (l1 == r1) {
267  if (!(usedBuffers & HAS_MONO_FLAG)) {
268  usedBuffers |= HAS_MONO_FLAG;
269 #ifdef __arm__
270  unsigned dummy1, dummy2, dummy3;
271  asm volatile (
272  "0:\n\t"
273  "ldmia %[in]!,{r3-r6}\n\t"
274  "mul r3,%[f],r3\n\t"
275  "mul r4,%[f],r4\n\t"
276  "mul r5,%[f],r5\n\t"
277  "mul r6,%[f],r6\n\t"
278  "stmia %[out]!,{r3-r6}\n\t"
279  "subs %[n],%[n],#4\n\t"
280  "bgt 0b\n\t"
281  : [in] "=r" (dummy1)
282  , [out] "=r" (dummy2)
283  , [n] "=r" (dummy3)
284  : "[in]" (tmpBuf)
285  , "[out]" (monoBuf)
286  , "[n]" (samples)
287  , [f] "r" (l1)
288  : "memory", "r3","r4","r5","r6"
289  );
290 #else
291  for (unsigned i = 0; i < samples; ++i) {
292  int tmp = l1 * tmpBuf[i];
293  monoBuf[i] = tmp;
294  }
295 #endif
296  } else {
297 #ifdef __arm__
298  unsigned dummy1, dummy2, dummy3;
299  asm volatile (
300  "0:\n\t"
301  "ldmia %[in]!,{r3,r4,r5,r6}\n\t"
302  "ldmia %[out],{r8,r9,r10,r12}\n\t"
303  "mla r3,%[f],r3,r8\n\t"
304  "mla r4,%[f],r4,r9\n\t"
305  "mla r5,%[f],r5,r10\n\t"
306  "mla r6,%[f],r6,r12\n\t"
307  "stmia %[out]!,{r3,r4,r5,r6}\n\t"
308  "subs %[n],%[n],#4\n\t"
309  "bgt 0b\n\t"
310  : [in] "=r" (dummy1)
311  , [out] "=r" (dummy2)
312  , [n] "=r" (dummy3)
313  : "[in]" (tmpBuf)
314  , "[out]" (monoBuf)
315  , "[n]" (samples)
316  , [f] "r" (l1)
317  : "memory"
318  , "r3","r4","r5","r6"
319  , "r8","r9","r10","r12"
320  );
321 #else
322  for (unsigned i = 0; i < samples; ++i) {
323  int tmp = l1 * tmpBuf[i];
324  monoBuf[i] += tmp;
325  }
326 #endif
327  }
328  } else {
329  if (!(usedBuffers & HAS_STEREO_FLAG)) {
330  usedBuffers |= HAS_STEREO_FLAG;
331  for (unsigned i = 0; i < samples; ++i) {
332  int l = l1 * tmpBuf[i];
333  int r = r1 * tmpBuf[i];
334  stereoBuf[2 * i + 0] = l;
335  stereoBuf[2 * i + 1] = r;
336  }
337  } else {
338  for (unsigned i = 0; i < samples; ++i) {
339  int l = l1 * tmpBuf[i];
340  int r = r1 * tmpBuf[i];
341  stereoBuf[2 * i + 0] += l;
342  stereoBuf[2 * i + 1] += r;
343  }
344  }
345  }
346  } else {
347  int l1 = p.second.left1;
348  int r1 = p.second.right1;
349  int l2 = p.second.left2;
350  int r2 = p.second.right2;
351  if (!(usedBuffers & HAS_STEREO_FLAG)) {
352  usedBuffers |= HAS_STEREO_FLAG;
353  for (unsigned i = 0; i < samples; ++i) {
354  int in1 = tmpBuf[2 * i + 0];
355  int in2 = tmpBuf[2 * i + 1];
356  int l = l1 * in1 + l2 * in2;
357  int r = r1 * in1 + r2 * in2;
358  stereoBuf[2 * i + 0] = l;
359  stereoBuf[2 * i + 1] = r;
360  }
361  } else {
362  for (unsigned i = 0; i < samples; ++i) {
363  int in1 = tmpBuf[2 * i + 0];
364  int in2 = tmpBuf[2 * i + 1];
365  int l = l1 * in1 + l2 * in2;
366  int r = r1 * in1 + r2 * in2;
367  stereoBuf[2 * i + 0] += l;
368  stereoBuf[2 * i + 1] += r;
369  }
370  }
371  }
372  }
373  }
374 
375  // DC removal filter
376  // y(n) = x(n) - x(n-1) + R * y(n-1)
377  // R = 1 - (pi*2 * cut-off-frequency / samplerate)
378  // take R = 511/512
379  // 44100Hz --> cutt-off freq = 14Hz
380  // 22050Hz 7Hz
381  // Note: we divide by 512 iso shift-right by 9 because we want
382  // to round towards zero.
383  switch (usedBuffers) {
384  case 0:
385  // no new input
386  if (samples == 0) break;
387  if ((outLeft == outRight) && (prevLeft == prevRight)) {
388  if ((outLeft == 0) && (prevLeft == 0)) {
389  // output was already zero, after DC-filter
390  // it will still be zero
391  memset(output, 0, 2 * samples * sizeof(short));
392  } else {
393  // output was not zero, but it was the same
394  // left and right
395  assert(samples > 0);
396  outLeft = -prevLeft + ((511 * outLeft) / 512);
397  prevLeft = 0;
398  short out = Math::clipIntToShort(outLeft);
399  output[0] = out;
400  output[1] = out;
401  for (unsigned j = 1; j < samples; ++j) {
402  outLeft = ((511 * outLeft) / 512);
403  out = Math::clipIntToShort(outLeft);
404  output[2 * j + 0] = out;
405  output[2 * j + 1] = out;
406  }
407  }
408  outRight = outLeft;
409  prevRight = prevLeft;
410  } else {
411  assert(samples > 0);
412  outLeft = -prevLeft + ((511 * outLeft ) / 512);
413  outRight = -prevRight + ((511 * outRight) / 512);
414  prevLeft = 0;
415  prevRight = 0;
416  output[0] = Math::clipIntToShort(outLeft);
417  output[1] = Math::clipIntToShort(outRight);
418  for (unsigned j = 1; j < samples; ++j) {
419  outLeft = ((511 * outLeft) / 512);
420  outRight = ((511 * outRight) / 512);
421  output[2 * j + 0] = Math::clipIntToShort(outLeft);
422  output[2 * j + 1] = Math::clipIntToShort(outRight);
423  }
424  }
425  break;
426 
427  case HAS_MONO_FLAG:
428  // only mono
429  if ((outLeft == outRight) && (prevLeft == prevRight)) {
430  // previous output was also mono
431 #ifdef __arm__
432  // Note: there are two functional differences in the
433  // asm and c++ code below:
434  // - divide by 512 is replaced by ASR #9
435  // (different for negative numbers)
436  // - the outLeft variable is set to the clipped value
437  // Though this difference is very small, and we need
438  // the extra speed.
439  unsigned dummy1, dummy2, dummy3, dummy4;
440  asm volatile (
441  "0:\n\t"
442  "rsb %[o],%[o],%[o],LSL #9\n\t"
443  "rsb %[o],%[p],%[o],ASR #9\n\t"
444  "ldr %[p],[%[in]],#4\n\t"
445  "asrs %[p],%[p],#8\n\t"
446  "add %[o],%[o],%[p]\n\t"
447  "lsls %[t],%[o],#16\n\t"
448  "cmp %[o],%[t],ASR #16\n\t"
449  "it ne\n\t"
450  "subne %[o],%[m],%[o],ASR #31\n\t"
451  "strh %[o],[%[out]],#2\n\t"
452  "strh %[o],[%[out]],#2\n\t"
453  "subs %[n],%[n],#1\n\t"
454  "bne 0b\n\t"
455  : [o] "=r" (outLeft)
456  , [p] "=r" (prevLeft)
457  , [in] "=r" (dummy1)
458  , [out] "=r" (dummy2)
459  , [n] "=r" (dummy3)
460  , [t] "=&r" (dummy4)
461  : "[o]" (outLeft)
462  , "[p]" (prevLeft)
463  , "[in]" (monoBuf)
464  , "[out]" (output)
465  , "[n]" (samples)
466  , [m] "r" (0x7FFF)
467  : "memory"
468  );
469 #else
470  for (unsigned j = 0; j < samples; ++j) {
471  int mono = monoBuf[j] >> 8;
472  outLeft = mono - prevLeft + ((511 * outLeft) / 512);
473  prevLeft = mono;
474  short out = Math::clipIntToShort(outLeft);
475  output[2 * j + 0] = out;
476  output[2 * j + 1] = out;
477  }
478 #endif
479  outRight = outLeft;
480  prevRight = prevLeft;
481  } else {
482  for (unsigned j = 0; j < samples; ++j) {
483  int mono = monoBuf[j] >> 8;
484  outLeft = mono - prevLeft + ((511 * outLeft) / 512);
485  prevLeft = mono;
486  outRight = mono - prevRight + ((511 * outRight) / 512);
487  prevRight = mono;
488  output[2 * j + 0] = Math::clipIntToShort(outLeft);
489  output[2 * j + 1] = Math::clipIntToShort(outRight);
490  }
491  }
492  break;
493 
494  case HAS_STEREO_FLAG:
495  // only stereo
496  for (unsigned j = 0; j < samples; ++j) {
497  int left = stereoBuf[2 * j + 0] >> 8;
498  int right = stereoBuf[2 * j + 1] >> 8;
499  outLeft = left - prevLeft + ((511 * outLeft) / 512);
500  prevLeft = left;
501  outRight = right - prevRight + ((511 * outRight) / 512);
502  prevRight = right;
503  output[2 * j + 0] = Math::clipIntToShort(outLeft);
504  output[2 * j + 1] = Math::clipIntToShort(outRight);
505  }
506  break;
507 
508  default:
509  // mono + stereo
510  for (unsigned j = 0; j < samples; ++j) {
511  int mono = monoBuf[j] >> 8;
512  int left = (stereoBuf[2 * j + 0] >> 8) + mono;
513  int right = (stereoBuf[2 * j + 1] >> 8) + mono;
514  outLeft = left - prevLeft + ((511 * outLeft) / 512);
515  prevLeft = left;
516  outRight = right - prevRight + ((511 * outRight) / 512);
517  prevRight = right;
518  output[2 * j + 0] = Math::clipIntToShort(outLeft);
519  output[2 * j + 1] = Math::clipIntToShort(outRight);
520  }
521  }
522 }
523 
525 {
526  for (auto& p : infos) {
527  auto& device = *p.first;
528  auto& balance = *p.second.balanceSetting;
529  if (device.isStereo() || balance.getInt() != 0) {
530  return true;
531  }
532  }
533  return false;
534 }
535 
537 {
538  if (muteCount == 0) {
539  mixer.unregisterMixer(*this);
540  }
541  ++muteCount;
542 }
543 
545 {
546  --muteCount;
547  if (muteCount == 0) {
548  prevLeft = outLeft = 0;
549  prevRight = outRight = 0;
550  mixer.registerMixer(*this);
551  }
552 }
553 
555 {
556  prevTime.reset(getCurrentTime());
557  prevTime.setFreq(hostSampleRate / getEffectiveSpeed());
558  reschedule();
559 }
560 void MSXMixer::reschedule()
561 {
563  reschedule2();
564 }
565 void MSXMixer::reschedule2()
566 {
567  unsigned size = (!muteCount && fragmentSize) ? fragmentSize : 512;
568  setSyncPoint(prevTime.getFastAdd(size));
569 }
570 
571 void MSXMixer::setMixerParams(unsigned newFragmentSize, unsigned newSampleRate)
572 {
573  // TODO old code checked that values did actually change,
574  // investigate if this optimization is worth it
575  hostSampleRate = newSampleRate;
576  fragmentSize = newFragmentSize;
577 
578  reInit(); // must come before call to setOutputRate()
579 
580  for (auto& p : infos) {
581  p.first->setOutputRate(newSampleRate);
582  }
583 }
584 
586 {
587  return prevTime;
588 }
589 
591 {
592  if ((recorder != nullptr) != (newRecorder != nullptr)) {
593  setSynchronousMode(newRecorder != nullptr);
594  }
595  recorder = newRecorder;
596 }
597 
598 unsigned MSXMixer::getSampleRate() const
599 {
600  return hostSampleRate;
601 }
602 
603 void MSXMixer::update(const Setting& setting)
604 {
605  if (&setting == &masterVolume) {
606  updateMasterVolume();
607  } else if (&setting == &speedSetting) {
608  if (synchronousCounter == 0) {
609  setMixerParams(fragmentSize, hostSampleRate);
610  } else {
611  // Avoid calling reInit() while recording because
612  // each call causes a small hiccup in the sound (and
613  // while recording this call anyway has no effect).
614  // This was noticable while sliding the speed slider
615  // in catapult (becuase this causes many changes in
616  // the speed setting).
617  }
618  } else if (dynamic_cast<const IntegerSetting*>(&setting)) {
619  auto it = infos.begin();
620  while (it != infos.end() &&
621  it->second.volumeSetting.get() != &setting &&
622  it->second.balanceSetting.get() != &setting) {
623  ++it;
624  }
625  assert(it != infos.end());
626  updateVolumeParams(*it);
627  } else if (dynamic_cast<const StringSetting*>(&setting)) {
628  changeRecordSetting(setting);
629  } else if (dynamic_cast<const BooleanSetting*>(&setting)) {
630  changeMuteSetting(setting);
631  } else {
632  UNREACHABLE;
633  }
634 }
635 
636 void MSXMixer::changeRecordSetting(const Setting& setting)
637 {
638  for (auto& p : infos) {
639  unsigned channel = 0;
640  for (auto& s : p.second.channelSettings) {
641  if (s.recordSetting.get() == &setting) {
642  p.first->recordChannel(
643  channel,
644  Filename(s.recordSetting->getString()));
645  return;
646  }
647  ++channel;
648  }
649  }
650  UNREACHABLE;
651 }
652 
653 void MSXMixer::changeMuteSetting(const Setting& setting)
654 {
655  for (auto& p : infos) {
656  unsigned channel = 0;
657  for (auto& s : p.second.channelSettings) {
658  if (s.muteSetting.get() == &setting) {
659  p.first->muteChannel(
660  channel, s.muteSetting->getBoolean());
661  return;
662  }
663  ++channel;
664  }
665  }
666  UNREACHABLE;
667 }
668 
669 void MSXMixer::update(const ThrottleManager& /*throttleManager*/)
670 {
671  //reInit();
672  // TODO Should this be removed?
673 }
674 
675 void MSXMixer::updateVolumeParams(Infos::value_type& p)
676 {
677  auto& info = p.second;
678  int mVolume = masterVolume.getInt();
679  int dVolume = info.volumeSetting->getInt();
680  double volume = info.defaultVolume * mVolume * dVolume / (100.0 * 100.0);
681  int balance = info.balanceSetting->getInt();
682  double l1, r1, l2, r2;
683  if (p.first->isStereo()) {
684  if (balance < 0) {
685  double b = (balance + 100.0) / 100.0;
686  l1 = volume;
687  r1 = 0.0;
688  l2 = volume * sqrt(std::max(0.0, 1.0 - b));
689  r2 = volume * sqrt(std::max(0.0, b));
690  } else {
691  double b = balance / 100.0;
692  l1 = volume * sqrt(std::max(0.0, 1.0 - b));
693  r1 = volume * sqrt(std::max(0.0, b));
694  l2 = 0.0;
695  r2 = volume;
696  }
697  } else {
698  // make sure that in case of rounding errors
699  // we don't take sqrt() of negative numbers
700  double b = (balance + 100.0) / 200.0;
701  l1 = volume * sqrt(std::max(0.0, 1.0 - b));
702  r1 = volume * sqrt(std::max(0.0, b));
703  l2 = r2 = 0.0; // dummy
704  }
705  int amp = 256 * p.first->getAmplificationFactor();
706  info.left1 = int(l1 * amp);
707  info.right1 = int(r1 * amp);
708  info.left2 = int(l2 * amp);
709  info.right2 = int(r2 * amp);
710 }
711 
712 void MSXMixer::updateMasterVolume()
713 {
714  for (auto& p : infos) {
715  updateVolumeParams(p);
716  }
717 }
718 
719 void MSXMixer::executeUntil(EmuTime::param time, int /*userData*/)
720 {
721  updateStream(time);
722  reschedule2();
723 }
724 
725 
726 // Sound device info
727 
729 {
730  for (auto& p : infos) {
731  if (p.first->getName() == name) {
732  return p.first;
733  }
734  }
735  return nullptr;
736 }
737 
739  InfoCommand& machineInfoCommand, MSXMixer& mixer_)
740  : InfoTopic(machineInfoCommand, "sounddevice")
741  , mixer(mixer_)
742 {
743 }
744 
745 void SoundDeviceInfoTopic::execute(const vector<TclObject>& tokens,
746  TclObject& result) const
747 {
748  switch (tokens.size()) {
749  case 2:
750  for (auto& p : mixer.infos) {
751  result.addListElement(p.first->getName());
752  }
753  break;
754  case 3: {
755  SoundDevice* device = mixer.findDevice(tokens[2].getString());
756  if (!device) {
757  throw CommandException("Unknown sound device");
758  }
759  result.setString(device->getDescription());
760  break;
761  }
762  default:
763  throw CommandException("Too many parameters");
764  }
765 }
766 
767 string SoundDeviceInfoTopic::help(const vector<string>& /*tokens*/) const
768 {
769  return "Shows a list of available sound devices.\n";
770 }
771 
772 void SoundDeviceInfoTopic::tabCompletion(vector<string>& tokens) const
773 {
774  if (tokens.size() == 3) {
775  vector<string_ref> devices;
776  for (auto& p : mixer.infos) {
777  devices.push_back(p.first->getName());
778  }
779  completeString(tokens, devices);
780  }
781 }
782 
783 } // namespace openmsx
virtual ~MSXMixer()
Definition: MSXMixer.cc:121
void addWave(unsigned num, short *data)
Definition: AviRecorder.cc:131
void mute()
TODO This methods (un)mute the sound.
Definition: MSXMixer.cc:536
void unregisterSound(SoundDevice &device)
Every sounddevice must unregister before it is destructed.
Definition: MSXMixer.cc:181
virtual bool updateBuffer(unsigned length, int *buffer, EmuTime::param time)=0
Generate sample data.
unsigned getSampleRate() const
Definition: MSXMixer.cc:598
void uploadBuffer(MSXMixer &msxMixer, short *buffer, unsigned len)
Upload new sample data.
Definition: Mixer.cc:176
bool needStereoRecording() const
Definition: MSXMixer.cc:524
void setSynchronousMode(bool synchronous)
If we're recording, we want to emulate sound at 100% emutime speed.
Definition: MSXMixer.cc:195
const std::string & getDescription() const
Gets a description of this sound device, to be presented to the user.
Definition: SoundDevice.cc:76
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:219
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:571
double getEffectiveSpeed() const
Returns the ratio of emutime-speed per realtime-speed.
Definition: MSXMixer.cc:212
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
std::unique_ptr< BooleanSetting > muteSetting
Definition: MSXMixer.hh:124
void attach(Observer< T > &observer)
Definition: Subject.hh:51
const DynamicClock & getHostSampleClock() const
Clock that ticks at the exact moment(s) in time that a host sample should be generated.
Definition: MSXMixer.cc:585
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:16
unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Definition: DynamicClock.hh:51
Represents a clock with a variable frequency.
Definition: DynamicClock.hh:15
virtual string help(const vector< string > &tokens) const
Print help for this topic.
Definition: MSXMixer.cc:767
SoundDeviceInfoTopic(InfoCommand &machineInfoCommand, MSXMixer &mixer)
Definition: MSXMixer.cc:738
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:50
const EmuTime & param
Definition: EmuTime.hh:20
void unregisterMixer(MSXMixer &mixer)
Unregister per-machine mixer.
Definition: Mixer.cc:131
MSXMixer(Mixer &mixer, Scheduler &scheduler, MSXCommandController &msxCommandController, GlobalSettings &globalSettings)
Definition: MSXMixer.cc:93
void setRecorder(AviRecorder *recorder)
Definition: MSXMixer.cc:590
SoundDevice * findDevice(string_ref name) const
Definition: MSXMixer.cc:728
virtual void setOutputRate(unsigned sampleRate)=0
When a SoundDevice registers itself with the Mixer, the Mixer sets the required sampleRate through th...
void addListElement(string_ref element)
Definition: TclObject.cc:154
static void completeString(std::vector< std::string > &tokens, const RANGE &possibleValues, bool caseSensitive=true)
Definition: Completer.hh:88
void setString(string_ref value)
Definition: TclObject.cc:99
virtual CliComm & getCliComm()=0
bool isStereo() const
Is this a stereo device? This is set in the constructor and cannot be changed anymore.
Definition: SoundDevice.cc:81
size_t size() const
void detach(Observer< T > &observer)
Definition: Subject.hh:57
void registerMixer(MSXMixer &mixer)
Register per-machine mixer.
Definition: Mixer.cc:123
const std::string & getName() const
Get the unique name that identifies this sound device.
Definition: SoundDevice.cc:71
ChannelSettings & operator=(ChannelSettings &&rhs)
Definition: MSXMixer.cc:85
virtual void tabCompletion(vector< string > &tokens) const
Attempt tab completion for this topic.
Definition: MSXMixer.cc:772
short clipIntToShort(int x)
Clip x to range [-32768,32767].
Definition: Math.hh:37
virtual void execute(const vector< TclObject > &tokens, TclObject &result) const
Show info on this topic.
Definition: MSXMixer.cc:745
This class contains settings that are used by several other class (including some singletons)...
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
void registerSound(SoundDevice &device, double volume, int balance, unsigned numChannels)
Use this method to register a given sounddevice.
Definition: MSXMixer.cc:135
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:56