openMSX
AY8910.cc
Go to the documentation of this file.
1 /*
2  * Emulation of the AY-3-8910
3  *
4  * Original code taken from xmame-0.37b16.1
5  * Based on various code snippets by Ville Hallik, Michael Cuddy,
6  * Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
7  * Integrated into openMSX by ???.
8  * Refactored in C++ style by Maarten ter Huurne.
9  */
10 
11 #include "AY8910.hh"
12 #include "AY8910Periphery.hh"
13 #include "DeviceConfig.hh"
14 #include "GlobalSettings.hh"
15 #include "MSXException.hh"
16 #include "Math.hh"
17 #include "StringOp.hh"
18 #include "serialize.hh"
19 #include "cstd.hh"
20 #include "likely.hh"
21 #include "one_of.hh"
22 #include "outer.hh"
23 #include "random.hh"
24 #include "xrange.hh"
25 #include <cassert>
26 #include <cstring>
27 #include <iostream>
28 
29 namespace openmsx {
30 
31 // The step clock for the tone and noise generators is the chip clock
32 // divided by 8; for the envelope generator of the AY-3-8910, it is half
33 // that much (clock/16).
34 constexpr float NATIVE_FREQ_FLOAT = (3579545.0f / 2) / 8;
36 
37 constexpr int PORT_A_DIRECTION = 0x40;
38 constexpr int PORT_B_DIRECTION = 0x80;
39 
40 enum Register {
41  AY_AFINE = 0, AY_ACOARSE = 1, AY_BFINE = 2, AY_BCOARSE = 3,
43  AY_AVOL = 8, AY_BVOL = 9, AY_CVOL = 10, AY_EFINE = 11,
44  AY_ECOARSE = 12, AY_ESHAPE = 13, AY_PORTA = 14, AY_PORTB = 15
45 };
46 
47 // Calculate the volume->voltage conversion table. The AY-3-8910 has 16 levels,
48 // in a logarithmic scale (3dB per step). YM2149 has 32 levels, the 16 extra
49 // levels are only used for envelope volumes
50 constexpr auto YM2149EnvelopeTab = [] {
51  std::array<float, 32> result = {};
52  double out = 1.0;
53  double factor = cstd::pow<5, 3>(0.5, 0.25); // 1/sqrt(sqrt(2)) ~= 1/(1.5dB)
54  for (int i = 31; i > 0; --i) {
55  result[i] = float(out);
56  out *= factor;
57  }
58  result[0] = 0.0f;
59  return result;
60 }();
61 constexpr auto AY8910EnvelopeTab = [] {
62  // only 16 envelope steps, duplicate every step
63  std::array<float, 32> result = {};
64  result[0] = 0.0f;
65  result[1] = 0.0f;
66  for (int i = 2; i < 32; i += 2) {
67  result[i + 0] = YM2149EnvelopeTab[i + 1];
68  result[i + 1] = YM2149EnvelopeTab[i + 1];
69  }
70  return result;
71 }();
72 constexpr auto volumeTab = [] {
73  std::array<float, 16> result = {};
74  result[0] = 0.0f;
75  for (auto i : xrange(1, 16)) {
76  result[i] = YM2149EnvelopeTab[2 * i + 1];
77  }
78  return result;
79 }();
80 
81 
82 // Perlin noise
83 
84 static float noiseTab[256 + 3];
85 
86 static void initDetune()
87 {
88  auto& generator = global_urng(); // fast (non-cryptographic) random numbers
89  std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
90 
91  for (auto i : xrange(256)) {
92  noiseTab[i] = distribution(generator);
93  }
94  noiseTab[256] = noiseTab[0];
95  noiseTab[257] = noiseTab[1];
96  noiseTab[258] = noiseTab[2];
97 }
98 static float noiseValue(float x)
99 {
100  // cubic hermite spline interpolation
101  assert(0.0f <= x);
102  int xi = int(x);
103  float xf = x - xi;
104  xi &= 255;
105  return Math::cubicHermite(&noiseTab[xi + 1], xf);
106 }
107 
108 
109 // Generator:
110 
111 inline void AY8910::Generator::reset()
112 {
113  count = 0;
114 }
115 
116 inline void AY8910::Generator::setPeriod(int value)
117 {
118  // Careful studies of the chip output prove that it instead counts up from
119  // 0 until the counter becomes greater or equal to the period. This is an
120  // important difference when the program is rapidly changing the period to
121  // modulate the sound.
122  // Also, note that period = 0 is the same as period = 1. This is mentioned
123  // in the YM2203 data sheets. However, this does NOT apply to the Envelope
124  // period. In that case, period = 0 is half as period = 1.
125  period = std::max(1, value);
126  count = std::min(count, period - 1);
127 }
128 
129 inline unsigned AY8910::Generator::getNextEventTime() const
130 {
131  assert(count < period);
132  return period - count;
133 }
134 
135 inline void AY8910::Generator::advanceFast(unsigned duration)
136 {
137  count += duration;
138  assert(count < period);
139 }
140 
141 
142 // ToneGenerator:
143 
144 AY8910::ToneGenerator::ToneGenerator()
145 {
146  reset();
147 }
148 
149 inline void AY8910::ToneGenerator::reset()
150 {
151  Generator::reset();
152  output = false;
153 }
154 
155 int AY8910::ToneGenerator::getDetune(AY8910& ay8910)
156 {
157  int result = 0;
158  float vibPerc = ay8910.vibratoPercent.getDouble();
159  if (vibPerc != 0.0f) {
160  int vibratoPeriod = int(
162  float(ay8910.vibratoFrequency.getDouble()));
163  vibratoCount += period;
164  vibratoCount %= vibratoPeriod;
165  result += int(
166  sinf((float(2 * M_PI) * vibratoCount) / vibratoPeriod)
167  * vibPerc * 0.01f * period);
168  }
169  float detunePerc = ay8910.detunePercent.getDouble();
170  if (detunePerc != 0.0f) {
171  float detunePeriod = NATIVE_FREQ_FLOAT /
172  float(ay8910.detuneFrequency.getDouble());
173  detuneCount += period;
174  float noiseIdx = detuneCount / detunePeriod;
175  float detuneNoise = noiseValue( noiseIdx)
176  + noiseValue(2.0f * noiseIdx) / 2.0f;
177  result += int(detuneNoise * detunePerc * 0.01f * period);
178  }
179  return std::min(result, period - 1);
180 }
181 
182 inline void AY8910::ToneGenerator::advance(int duration)
183 {
184  assert(count < period);
185  count += duration;
186  if (count >= period) {
187  // Calculate number of output transitions.
188  int cycles = count / period;
189  count -= period * cycles; // equivalent to count %= period;
190  output ^= cycles & 1;
191  }
192 }
193 
194 inline void AY8910::ToneGenerator::doNextEvent(AY8910& ay8910)
195 {
196  if (unlikely(ay8910.doDetune)) {
197  count = getDetune(ay8910);
198  } else {
199  count = 0;
200  }
201  output ^= 1;
202 }
203 
204 
205 // NoiseGenerator:
206 
207 AY8910::NoiseGenerator::NoiseGenerator()
208 {
209  reset();
210 }
211 
212 inline void AY8910::NoiseGenerator::reset()
213 {
214  Generator::reset();
215  random = 1;
216 }
217 
218 inline void AY8910::NoiseGenerator::doNextEvent()
219 {
220  count = 0;
221 
222  // The Random Number Generator of the 8910 is a 17-bit shift register.
223  // The input to the shift register is bit0 XOR bit3 (bit0 is the
224  // output). Verified on real AY8910 and YM2149 chips.
225  //
226  // Fibonacci configuration:
227  // random ^= ((random & 1) ^ ((random >> 3) & 1)) << 17;
228  // random >>= 1;
229  // Galois configuration:
230  // if (random & 1) random ^= 0x24000;
231  // random >>= 1;
232  // or alternatively:
233  random = (random >> 1) ^ ((random & 1) << 13) ^ ((random & 1) << 16);
234 }
235 
236 inline void AY8910::NoiseGenerator::advance(int duration)
237 {
238  assert(count < period);
239  count += duration;
240  int cycles = count / period;
241  count -= cycles * period; // equivalent to count %= period
242 
243  // The following loops advance the random state N steps at once. The
244  // values for N (4585, 275, 68, 8, 1) are chosen so that:
245  // - The formulas are relatively simple.
246  // - The ratio between the step sizes is roughly the same.
247  for (; cycles >= 4585; cycles -= 4585) {
248  random = ((random & 0x1f) << 12)
249  ^ ((random & 0x1f) << 9)
250  ^ random
251  ^ ( random >> 5);
252  }
253  for (; cycles >= 275; cycles -= 275) {
254  random = ((random & 0x03f) << 11)
255  ^ ((random & 0x1c0) << 8)
256  ^ ((random & 0x1ff) << 5)
257  ^ random
258  ^ ( random >> 6)
259  ^ ( random >> 9);
260  }
261  for (; cycles >= 68; cycles -= 68) {
262  random = ((random & 0xfff) << 5)
263  ^ ((random & 0xfff) << 2)
264  ^ random
265  ^ ( random >> 12);
266  }
267  for (; cycles >= 8; cycles -= 8) {
268  random = ((random & 0xff) << 9)
269  ^ ((random & 0xff) << 6)
270  ^ ( random >> 8);
271  }
272  for (; cycles >= 1; cycles -= 1) {
273  random = ((random & 1) << 16)
274  ^ ((random & 1) << 13)
275  ^ ( random >> 1);
276  }
277 }
278 
279 
280 // Amplitude:
281 
282 static bool checkAY8910(const DeviceConfig& config)
283 {
284  auto type = config.getChildData("type", "ay8910");
285  StringOp::casecmp cmp;
286  if (cmp(type, "ay8910")) {
287  return true;
288  } else if (cmp(type, "ym2149")) {
289  return false;
290  }
291  throw FatalError("Unknown PSG type: ", type);
292 }
293 
294 AY8910::Amplitude::Amplitude(const DeviceConfig& config)
295  : isAY8910(checkAY8910(config))
296 {
297  vol[0] = vol[1] = vol[2] = 0.0f;
298  envChan[0] = false;
299  envChan[1] = false;
300  envChan[2] = false;
301  envVolTable = isAY8910 ? AY8910EnvelopeTab.data() : YM2149EnvelopeTab.data();
302 
303  if (false) {
304  std::cout << "YM2149Envelope:";
305  for (const auto& e : YM2149EnvelopeTab) {
306  std::cout << ' ' << std::hexfloat << e;
307  }
308  std::cout << "\nAY8910Envelope:";
309  for (const auto& e : AY8910EnvelopeTab) {
310  std::cout << ' ' << std::hexfloat << e;
311  }
312  std::cout << "\nvolume:";
313  for (const auto& e : volumeTab) {
314  std::cout << ' ' << std::hexfloat << e;
315  }
316  std::cout << '\n';
317  }
318 }
319 
320 const float* AY8910::Amplitude::getEnvVolTable() const
321 {
322  return envVolTable;
323 }
324 
325 inline float AY8910::Amplitude::getVolume(unsigned chan) const
326 {
327  assert(!followsEnvelope(chan));
328  return vol[chan];
329 }
330 
331 inline void AY8910::Amplitude::setChannelVolume(unsigned chan, unsigned value)
332 {
333  envChan[chan] = (value & 0x10) != 0;
334  vol[chan] = volumeTab[value & 0x0F];
335 }
336 
337 inline bool AY8910::Amplitude::followsEnvelope(unsigned chan) const
338 {
339  return envChan[chan];
340 }
341 
342 
343 // Envelope:
344 
345 // AY8910 and YM2149 behave different here:
346 // YM2149 envelope goes twice as fast and has twice as many levels. Here
347 // we implement the YM2149 behaviour, but to get the AY8910 behaviour we
348 // repeat every level twice in the envVolTable
349 
350 inline AY8910::Envelope::Envelope(const float* envVolTable_)
351 {
352  envVolTable = envVolTable_;
353  period = 1;
354  count = 0;
355  step = 0;
356  attack = 0;
357  hold = false;
358  alternate = false;
359  holding = false;
360 }
361 
362 inline void AY8910::Envelope::reset()
363 {
364  count = 0;
365 }
366 
367 inline void AY8910::Envelope::setPeriod(int value)
368 {
369  // twice as fast as AY8910
370  // see also Generator::setPeriod()
371  period = std::max(1, 2 * value);
372  count = std::min(count, period - 1);
373 }
374 
375 inline float AY8910::Envelope::getVolume() const
376 {
377  return envVolTable[step ^ attack];
378 }
379 
380 inline void AY8910::Envelope::setShape(unsigned shape)
381 {
382  // do 32 steps for both AY8910 and YM2149
383  /*
384  envelope shapes:
385  C AtAlH
386  0 0 x x \___
387  0 1 x x /___
388  1 0 0 0 \\\\
389  1 0 0 1 \___
390  1 0 1 0 \/\/
391  1 0 1 1 \
392  1 1 0 0 ////
393  1 1 0 1 /
394  1 1 1 0 /\/\
395  1 1 1 1 /___
396  */
397  attack = (shape & 0x04) ? 0x1F : 0x00;
398  if ((shape & 0x08) == 0) {
399  // If Continue = 0, map the shape to the equivalent one
400  // which has Continue = 1.
401  hold = true;
402  alternate = attack != 0;
403  } else {
404  hold = (shape & 0x01) != 0;
405  alternate = (shape & 0x02) != 0;
406  }
407  count = 0;
408  step = 0x1F;
409  holding = false;
410 }
411 
412 inline bool AY8910::Envelope::isChanging() const
413 {
414  return !holding;
415 }
416 
417 inline void AY8910::Envelope::doSteps(int steps)
418 {
419  // For best performance callers should check upfront whether
420  // isChanging() == true
421  // Though we can't assert on it because the condition might change
422  // in the inner loop(s) of generateChannels().
423  //assert(!holding);
424 
425  if (holding) return;
426  step -= steps;
427 
428  // Check current envelope position.
429  if (step < 0) {
430  if (hold) {
431  if (alternate) attack ^= 0x1F;
432  holding = true;
433  step = 0;
434  } else {
435  // If step has looped an odd number of times
436  // (usually 1), invert the output.
437  if (alternate && (step & 0x10)) {
438  attack ^= 0x1F;
439  }
440  step &= 0x1F;
441  }
442  }
443 }
444 
445 inline void AY8910::Envelope::advance(int duration)
446 {
447  assert(count < period);
448  count += duration * 2;
449  if (count >= period) {
450  int steps = count / period;
451  count -= steps * period; // equivalent to count %= period;
452  doSteps(steps);
453  }
454 }
455 
456 inline void AY8910::Envelope::doNextEvent()
457 {
458  count = 0;
459  doSteps(period == 1 ? 2 : 1);
460 }
461 
462 inline unsigned AY8910::Envelope::getNextEventTime() const
463 {
464  assert(count < period);
465  return (period - count + 1) / 2;
466 }
467 
468 inline void AY8910::Envelope::advanceFast(unsigned duration)
469 {
470  count += 2 * duration;
471  assert(count < period);
472 }
473 
474 
475 
476 // AY8910 main class:
477 
478 AY8910::AY8910(const std::string& name_, AY8910Periphery& periphery_,
479  const DeviceConfig& config, EmuTime::param time)
480  : ResampledSoundDevice(config.getMotherBoard(), name_, "PSG", 3, NATIVE_FREQ_INT, false)
481  , periphery(periphery_)
482  , debuggable(config.getMotherBoard(), getName())
483  , vibratoPercent(
484  config.getCommandController(), tmpStrCat(getName(), "_vibrato_percent"),
485  "controls strength of vibrato effect", 0.0, 0.0, 10.0)
486  , vibratoFrequency(
487  config.getCommandController(), tmpStrCat(getName(), "_vibrato_frequency"),
488  "frequency of vibrato effect in Hertz", 5, 1.0, 10.0)
489  , detunePercent(
490  config.getCommandController(), tmpStrCat(getName(), "_detune_percent"),
491  "controls strength of detune effect", 0.0, 0.0, 10.0)
492  , detuneFrequency(
493  config.getCommandController(), tmpStrCat(getName(), "_detune_frequency"),
494  "frequency of detune effect in Hertz", 5.0, 1.0, 100.0)
495  , directionsCallback(
496  config.getGlobalSettings().getInvalidPsgDirectionsSetting())
497  , amplitude(config)
498  , envelope(amplitude.getEnvVolTable())
499  , isAY8910(checkAY8910(config))
500  , ignorePortDirections(config.getChildDataAsBool("ignorePortDirections", true))
501 {
502  // (lazily) initialize detune stuff
503  detuneInitialized = false;
504  update(vibratoPercent);
505 
506  // make valgrind happy
507  memset(regs, 0, sizeof(regs));
508 
509  reset(time);
510  registerSound(config);
511 
512  // only attach once all initialization is successful
513  vibratoPercent.attach(*this);
514  detunePercent .attach(*this);
515 }
516 
518 {
519  vibratoPercent.detach(*this);
520  detunePercent .detach(*this);
521 
522  unregisterSound();
523 }
524 
525 void AY8910::reset(EmuTime::param time)
526 {
527  // Reset generators and envelope.
528  for (auto& t : tone) t.reset();
529  noise.reset();
530  envelope.reset();
531  // Reset registers and values derived from them.
532  for (auto reg : xrange(16)) {
533  wrtReg(reg, 0, time);
534  }
535 }
536 
537 
538 byte AY8910::readRegister(unsigned reg, EmuTime::param time)
539 {
540  if (reg >= 16) return 255;
541  switch (reg) {
542  case AY_PORTA:
543  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
544  regs[reg] = periphery.readA(time);
545  }
546  break;
547  case AY_PORTB:
548  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
549  regs[reg] = periphery.readB(time);
550  }
551  break;
552  }
553 
554  // TODO some AY8910 models have 1F as mask for registers 1, 3, 5
555  static constexpr byte regMask[16] = {
556  0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
557  0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
558  };
559  return isAY8910 ? regs[reg] & regMask[reg]
560  : regs[reg];
561 }
562 
563 byte AY8910::peekRegister(unsigned reg, EmuTime::param time) const
564 {
565  if (reg >= 16) return 255;
566  switch (reg) {
567  case AY_PORTA:
568  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
569  return periphery.readA(time);
570  }
571  break;
572  case AY_PORTB:
573  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
574  return periphery.readB(time);
575  }
576  break;
577  }
578  return regs[reg];
579 }
580 
581 
582 void AY8910::writeRegister(unsigned reg, byte value, EmuTime::param time)
583 {
584  if (reg >= 16) return;
585  if ((reg < AY_PORTA) && (reg == AY_ESHAPE || regs[reg] != value)) {
586  // Update the output buffer before changing the register.
587  updateStream(time);
588  }
589  wrtReg(reg, value, time);
590 }
591 void AY8910::wrtReg(unsigned reg, byte value, EmuTime::param time)
592 {
593  // Warn/force port directions
594  if (reg == AY_ENABLE) {
595  if (value & PORT_A_DIRECTION) {
596  directionsCallback.execute();
597  }
598  if (ignorePortDirections)
599  {
600  // portA -> input
601  // portB -> output
602  value = (value & ~PORT_A_DIRECTION) | PORT_B_DIRECTION;
603  }
604  }
605 
606  // Note: unused bits are stored as well; they can be read back.
607  byte diff = regs[reg] ^ value;
608  regs[reg] = value;
609 
610  switch (reg) {
611  case AY_AFINE:
612  case AY_ACOARSE:
613  case AY_BFINE:
614  case AY_BCOARSE:
615  case AY_CFINE:
616  case AY_CCOARSE:
617  tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
618  break;
619  case AY_NOISEPER:
620  // Half the frequency of tone generation.
621  //
622  // Verified on turboR GT: value=0 and value=1 sound the same.
623  //
624  // Likely in real AY8910 this is implemented by driving the
625  // noise generator at halve the frequency instead of
626  // multiplying the value by 2 (hence the correction for value=0
627  // here). But the effect is the same(?).
628  noise.setPeriod(2 * std::max(1, value & 0x1F));
629  break;
630  case AY_AVOL:
631  case AY_BVOL:
632  case AY_CVOL:
633  amplitude.setChannelVolume(reg - AY_AVOL, value);
634  break;
635  case AY_EFINE:
636  case AY_ECOARSE:
637  // also half the frequency of tone generation, but handled
638  // inside Envelope::setPeriod()
639  envelope.setPeriod(regs[AY_EFINE] + 256 * regs[AY_ECOARSE]);
640  break;
641  case AY_ESHAPE:
642  envelope.setShape(value);
643  break;
644  case AY_ENABLE:
645  if (diff & PORT_A_DIRECTION) {
646  // port A changed
647  if (value & PORT_A_DIRECTION) {
648  // from input to output
649  periphery.writeA(regs[AY_PORTA], time);
650  } else {
651  // from output to input
652  periphery.writeA(0xff, time);
653  }
654  }
655  if (diff & PORT_B_DIRECTION) {
656  // port B changed
657  if (value & PORT_B_DIRECTION) {
658  // from input to output
659  periphery.writeB(regs[AY_PORTB], time);
660  } else {
661  // from output to input
662  periphery.writeB(0xff, time);
663  }
664  }
665  break;
666  case AY_PORTA:
667  if (regs[AY_ENABLE] & PORT_A_DIRECTION) { // output
668  periphery.writeA(value, time);
669  }
670  break;
671  case AY_PORTB:
672  if (regs[AY_ENABLE] & PORT_B_DIRECTION) { // output
673  periphery.writeB(value, time);
674  }
675  break;
676  }
677 }
678 
679 void AY8910::generateChannels(float** bufs, unsigned num)
680 {
681  // Disable channels with volume 0: since the sample value doesn't matter,
682  // we can use the fastest path.
683  unsigned chanEnable = regs[AY_ENABLE];
684  for (auto chan : xrange(3)) {
685  if ((!amplitude.followsEnvelope(chan) &&
686  (amplitude.getVolume(chan) == 0.0f)) ||
687  (amplitude.followsEnvelope(chan) &&
688  !envelope.isChanging() &&
689  (envelope.getVolume() == 0.0f))) {
690  bufs[chan] = nullptr;
691  tone[chan].advance(num);
692  chanEnable |= 0x09 << chan;
693  }
694  }
695  // Noise disabled on all channels?
696  if ((chanEnable & 0x38) == 0x38) {
697  noise.advance(num);
698  }
699 
700  // Calculate samples.
701  // The 8910 has three outputs, each output is the mix of one of the
702  // three tone generators and of the (single) noise generator. The two
703  // are mixed BEFORE going into the DAC. The formula to mix each channel
704  // is:
705  // (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable),
706  // where ToneOn and NoiseOn are the current generator state
707  // and ToneDisable and NoiseDisable come from the enable reg.
708  // Note that this means that if both tone and noise are disabled, the
709  // output is 1, not 0, and can be modulated by changing the volume.
710  bool envelopeUpdated = false;
711  Envelope initialEnvelope = envelope;
712  NoiseGenerator initialNoise = noise;
713  for (unsigned chan = 0; chan < 3; ++chan, chanEnable >>= 1) {
714  auto* buf = bufs[chan];
715  if (!buf) continue;
716  ToneGenerator& t = tone[chan];
717  if (envelope.isChanging() && amplitude.followsEnvelope(chan)) {
718  envelopeUpdated = true;
719  envelope = initialEnvelope;
720  if ((chanEnable & 0x09) == 0x08) {
721  // no noise, square wave: alternating between 0 and 1.
722  auto val = t.getOutput() * envelope.getVolume();
723  unsigned remaining = num;
724  unsigned nextE = envelope.getNextEventTime();
725  unsigned nextT = t.getNextEventTime();
726  while ((nextT <= remaining) || (nextE <= remaining)) {
727  if (nextT < nextE) {
728  addFill(buf, val, nextT);
729  remaining -= nextT;
730  nextE -= nextT;
731  envelope.advanceFast(nextT);
732  t.doNextEvent(*this);
733  nextT = t.getNextEventTime();
734  } else if (nextE < nextT) {
735  addFill(buf, val, nextE);
736  remaining -= nextE;
737  nextT -= nextE;
738  t.advanceFast(nextE);
739  envelope.doNextEvent();
740  nextE = envelope.getNextEventTime();
741  } else {
742  assert(nextT == nextE);
743  addFill(buf, val, nextT);
744  remaining -= nextT;
745  t.doNextEvent(*this);
746  nextT = t.getNextEventTime();
747  envelope.doNextEvent();
748  nextE = envelope.getNextEventTime();
749  }
750  val = t.getOutput() * envelope.getVolume();
751  }
752  if (remaining) {
753  // last interval (without events)
754  addFill(buf, val, remaining);
755  t.advanceFast(remaining);
756  envelope.advanceFast(remaining);
757  }
758 
759  } else if ((chanEnable & 0x09) == 0x09) {
760  // no noise, channel disabled: always 1.
761  auto val = envelope.getVolume();
762  unsigned remaining = num;
763  unsigned next = envelope.getNextEventTime();
764  while (next <= remaining) {
765  addFill(buf, val, next);
766  remaining -= next;
767  envelope.doNextEvent();
768  val = envelope.getVolume();
769  next = envelope.getNextEventTime();
770  }
771  if (remaining) {
772  // last interval (without events)
773  addFill(buf, val, remaining);
774  envelope.advanceFast(remaining);
775  }
776  t.advance(num);
777 
778  } else if ((chanEnable & 0x09) == 0x00) {
779  // noise enabled, tone enabled
780  noise = initialNoise;
781  auto val = noise.getOutput() * t.getOutput() * envelope.getVolume();
782  unsigned remaining = num;
783  unsigned nextT = t.getNextEventTime();
784  unsigned nextN = noise.getNextEventTime();
785  unsigned nextE = envelope.getNextEventTime();
786  unsigned next = std::min(std::min(nextT, nextN), nextE);
787  while (next <= remaining) {
788  addFill(buf, val, next);
789  remaining -= next;
790  nextT -= next;
791  nextN -= next;
792  nextE -= next;
793  if (nextT) {
794  t.advanceFast(next);
795  } else {
796  t.doNextEvent(*this);
797  nextT = t.getNextEventTime();
798  }
799  if (nextN) {
800  noise.advanceFast(next);
801  } else {
802  noise.doNextEvent();
803  nextN = noise.getNextEventTime();
804  }
805  if (nextE) {
806  envelope.advanceFast(next);
807  } else {
808  envelope.doNextEvent();
809  nextE = envelope.getNextEventTime();
810  }
811  next = std::min(std::min(nextT, nextN), nextE);
812  val = noise.getOutput() * t.getOutput() * envelope.getVolume();
813  }
814  if (remaining) {
815  // last interval (without events)
816  addFill(buf, val, remaining);
817  t.advanceFast(remaining);
818  noise.advanceFast(remaining);
819  envelope.advanceFast(remaining);
820  }
821 
822  } else {
823  // noise enabled, tone disabled
824  noise = initialNoise;
825  auto val = noise.getOutput() * envelope.getVolume();
826  unsigned remaining = num;
827  unsigned nextE = envelope.getNextEventTime();
828  unsigned nextN = noise.getNextEventTime();
829  while ((nextN <= remaining) || (nextE <= remaining)) {
830  if (nextN < nextE) {
831  addFill(buf, val, nextN);
832  remaining -= nextN;
833  nextE -= nextN;
834  envelope.advanceFast(nextN);
835  noise.doNextEvent();
836  nextN = noise.getNextEventTime();
837  } else if (nextE < nextN) {
838  addFill(buf, val, nextE);
839  remaining -= nextE;
840  nextN -= nextE;
841  noise.advanceFast(nextE);
842  envelope.doNextEvent();
843  nextE = envelope.getNextEventTime();
844  } else {
845  assert(nextN == nextE);
846  addFill(buf, val, nextN);
847  remaining -= nextN;
848  noise.doNextEvent();
849  nextN = noise.getNextEventTime();
850  envelope.doNextEvent();
851  nextE = envelope.getNextEventTime();
852  }
853  val = noise.getOutput() * envelope.getVolume();
854  }
855  if (remaining) {
856  // last interval (without events)
857  addFill(buf, val, remaining);
858  noise.advanceFast(remaining);
859  envelope.advanceFast(remaining);
860  }
861  t.advance(num);
862  }
863  } else {
864  // no (changing) envelope on this channel
865  auto volume = amplitude.followsEnvelope(chan)
866  ? envelope.getVolume()
867  : amplitude.getVolume(chan);
868  if ((chanEnable & 0x09) == 0x08) {
869  // no noise, square wave: alternating between 0 and 1.
870  auto val = t.getOutput() * volume;
871  unsigned remaining = num;
872  unsigned next = t.getNextEventTime();
873  while (next <= remaining) {
874  addFill(buf, val, next);
875  val = volume - val;
876  remaining -= next;
877  t.doNextEvent(*this);
878  next = t.getNextEventTime();
879  }
880  if (remaining) {
881  // last interval (without events)
882  addFill(buf, val, remaining);
883  t.advanceFast(remaining);
884  }
885 
886  } else if ((chanEnable & 0x09) == 0x09) {
887  // no noise, channel disabled: always 1.
888  addFill(buf, volume, num);
889  t.advance(num);
890 
891  } else if ((chanEnable & 0x09) == 0x00) {
892  // noise enabled, tone enabled
893  noise = initialNoise;
894  auto val1 = t.getOutput() * volume;
895  auto val2 = val1 * noise.getOutput();
896  unsigned remaining = num;
897  unsigned nextN = noise.getNextEventTime();
898  unsigned nextT = t.getNextEventTime();
899  while ((nextN <= remaining) || (nextT <= remaining)) {
900  if (nextT < nextN) {
901  addFill(buf, val2, nextT);
902  remaining -= nextT;
903  nextN -= nextT;
904  noise.advanceFast(nextT);
905  t.doNextEvent(*this);
906  nextT = t.getNextEventTime();
907  val1 = volume - val1;
908  val2 = val1 * noise.getOutput();
909  } else if (nextN < nextT) {
910  addFill(buf, val2, nextN);
911  remaining -= nextN;
912  nextT -= nextN;
913  t.advanceFast(nextN);
914  noise.doNextEvent();
915  nextN = noise.getNextEventTime();
916  val2 = val1 * noise.getOutput();
917  } else {
918  assert(nextT == nextN);
919  addFill(buf, val2, nextT);
920  remaining -= nextT;
921  t.doNextEvent(*this);
922  nextT = t.getNextEventTime();
923  noise.doNextEvent();
924  nextN = noise.getNextEventTime();
925  val1 = volume - val1;
926  val2 = val1 * noise.getOutput();
927  }
928  }
929  if (remaining) {
930  // last interval (without events)
931  addFill(buf, val2, remaining);
932  t.advanceFast(remaining);
933  noise.advanceFast(remaining);
934  }
935 
936  } else {
937  // noise enabled, tone disabled
938  noise = initialNoise;
939  unsigned remaining = num;
940  auto val = noise.getOutput() * volume;
941  unsigned next = noise.getNextEventTime();
942  while (next <= remaining) {
943  addFill(buf, val, next);
944  remaining -= next;
945  noise.doNextEvent();
946  val = noise.getOutput() * volume;
947  next = noise.getNextEventTime();
948  }
949  if (remaining) {
950  // last interval (without events)
951  addFill(buf, val, remaining);
952  noise.advanceFast(remaining);
953  }
954  t.advance(num);
955  }
956  }
957  }
958 
959  // Envelope not yet updated?
960  if (envelope.isChanging() && !envelopeUpdated) {
961  envelope.advance(num);
962  }
963 }
964 
965 float AY8910::getAmplificationFactorImpl() const
966 {
967  return 1.0f;
968 }
969 
970 void AY8910::update(const Setting& setting) noexcept
971 {
972  if (&setting == one_of(&vibratoPercent, &detunePercent)) {
973  doDetune = (vibratoPercent.getDouble() != 0) ||
974  (detunePercent .getDouble() != 0);
975  if (doDetune && !detuneInitialized) {
976  detuneInitialized = true;
977  initDetune();
978  }
979  } else {
981  }
982 }
983 
984 
985 // Debuggable
986 
987 AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const std::string& name_)
988  : SimpleDebuggable(motherBoard_, name_ + " regs", "PSG", 0x10)
989 {
990 }
991 
992 byte AY8910::Debuggable::read(unsigned address, EmuTime::param time)
993 {
994  auto& ay8910 = OUTER(AY8910, debuggable);
995  return ay8910.readRegister(address, time);
996 }
997 
998 void AY8910::Debuggable::write(unsigned address, byte value, EmuTime::param time)
999 {
1000  auto& ay8910 = OUTER(AY8910, debuggable);
1001  return ay8910.writeRegister(address, value, time);
1002 }
1003 
1004 
1005 template<typename Archive>
1006 void AY8910::serialize(Archive& ar, unsigned /*version*/)
1007 {
1008  ar.serialize("toneGenerators", tone,
1009  "noiseGenerator", noise,
1010  "envelope", envelope,
1011  "registers", regs);
1012 
1013  // amplitude
1014  if constexpr (Archive::IS_LOADER) {
1015  for (auto i : xrange(3)) {
1016  amplitude.setChannelVolume(i, regs[i + AY_AVOL]);
1017  }
1018  }
1019 }
1021 
1022 // version 1: initial version
1023 // version 2: removed 'output' member variable
1024 template<typename Archive>
1025 void AY8910::Generator::serialize(Archive& ar, unsigned /*version*/)
1026 {
1027  ar.serialize("period", period,
1028  "count", count);
1029 }
1030 INSTANTIATE_SERIALIZE_METHODS(AY8910::Generator);
1031 
1032 // version 1: initial version
1033 // version 2: moved 'output' variable from base class to here
1034 template<typename Archive>
1035 void AY8910::ToneGenerator::serialize(Archive& ar, unsigned version)
1036 {
1037  ar.template serializeInlinedBase<Generator>(*this, version);
1038  ar.serialize("vibratoCount", vibratoCount,
1039  "detuneCount", detuneCount);
1040  if (ar.versionAtLeast(version, 2)) {
1041  ar.serialize("output", output);
1042  } else {
1043  // don't bother trying to restore this from the old location:
1044  // it doesn't influence any MSX-observable state, and the
1045  // difference in generated sound will likely be inaudible
1046  }
1047 }
1048 INSTANTIATE_SERIALIZE_METHODS(AY8910::ToneGenerator);
1049 
1050 // version 1: initial version
1051 // version 2: removed 'output' variable from base class, not stored here but
1052 // instead it's calculated from 'random' when needed
1053 template<typename Archive>
1054 void AY8910::NoiseGenerator::serialize(Archive& ar, unsigned version)
1055 {
1056  ar.template serializeInlinedBase<Generator>(*this, version);
1057  ar.serialize("random", random);
1058 }
1059 INSTANTIATE_SERIALIZE_METHODS(AY8910::NoiseGenerator);
1060 
1061 template<typename Archive>
1062 void AY8910::Envelope::serialize(Archive& ar, unsigned /*version*/)
1063 {
1064  ar.serialize("period", period,
1065  "count", count,
1066  "step", step,
1067  "attack", attack,
1068  "hold", hold,
1069  "alternate", alternate,
1070  "holding", holding);
1071 }
1072 INSTANTIATE_SERIALIZE_METHODS(AY8910::Envelope);
1073 
1074 } // namespace openmsx
BaseSetting * setting
Definition: Interpreter.cc:27
#define M_PI
Definition: Math.hh:27
TclObject t
Definition: one_of.hh:7
Models the general purpose I/O ports of the AY8910.
virtual void writeA(byte value, EmuTime::param time)
Writes to the peripheral on port A.
virtual byte readA(EmuTime::param time)
Reads the state of the peripheral on port A.
virtual byte readB(EmuTime::param time)
Similar to readA, but reads port B.
virtual void writeB(byte value, EmuTime::param time)
Similar to writeA, but writes port B.
This class implements the AY-3-8910 sound chip.
Definition: AY8910.hh:20
void reset(EmuTime::param time)
Definition: AY8910.cc:525
byte readRegister(unsigned reg, EmuTime::param time)
Definition: AY8910.cc:538
byte peekRegister(unsigned reg, EmuTime::param time) const
Definition: AY8910.cc:563
void serialize(Archive &ar, unsigned version)
Definition: AY8910.cc:1006
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:582
void update(const Setting &setting) noexcept override
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:139
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
Definition: SoundDevice.cc:44
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:134
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:89
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
TclObject execute()
Definition: TclCallback.cc:35
constexpr auto step
Definition: eeprom.cc:9
#define unlikely(x)
Definition: likely.hh:15
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition: lz4.cc:207
constexpr float cubicHermite(const float *y, float x)
Definition: Math.hh:266
constexpr double round(double x)
Definition: cstd.hh:355
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:741
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr auto volumeTab
Definition: AY8910.cc:72
constexpr int PORT_B_DIRECTION
Definition: AY8910.cc:38
constexpr int PORT_A_DIRECTION
Definition: AY8910.cc:37
constexpr auto YM2149EnvelopeTab
Definition: AY8910.cc:50
constexpr auto AY8910EnvelopeTab
Definition: AY8910.cc:61
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:118
void serialize(Archive &ar, T &t, unsigned version)
Register
Definition: AY8910.cc:40
@ AY_PORTA
Definition: AY8910.cc:44
@ AY_CVOL
Definition: AY8910.cc:43
@ AY_CFINE
Definition: AY8910.cc:42
@ AY_EFINE
Definition: AY8910.cc:43
@ AY_ECOARSE
Definition: AY8910.cc:44
@ AY_PORTB
Definition: AY8910.cc:44
@ AY_BVOL
Definition: AY8910.cc:43
@ AY_AFINE
Definition: AY8910.cc:41
@ AY_NOISEPER
Definition: AY8910.cc:42
@ AY_ENABLE
Definition: AY8910.cc:42
@ AY_ACOARSE
Definition: AY8910.cc:41
@ AY_CCOARSE
Definition: AY8910.cc:42
@ AY_BCOARSE
Definition: AY8910.cc:41
@ AY_AVOL
Definition: AY8910.cc:43
@ AY_BFINE
Definition: AY8910.cc:41
@ AY_ESHAPE
Definition: AY8910.cc:44
constexpr int NATIVE_FREQ_INT
Definition: AY8910.cc:35
constexpr float NATIVE_FREQ_FLOAT
Definition: AY8910.cc:34
void advance(octet_iterator &it, distance_type n, octet_iterator end)
uint32_t next(octet_iterator &it, octet_iterator end)
#define OUTER(type, member)
Definition: outer.hh:41
auto & global_urng()
Return reference to a (shared) global random number generator.
Definition: random.hh:8
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
constexpr auto xrange(T e)
Definition: xrange.hh:155