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 "one_of.hh"
21 #include "outer.hh"
22 #include "random.hh"
23 #include "xrange.hh"
24 #include <array>
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 * Math::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 (ay8910.doDetune) [[unlikely]] {
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  : envVolTable(envVolTable_)
352  , period(1)
353  , count(0)
354  , step(0)
355  , attack(0)
356  , hold(false)
357  , alternate(false)
358  , holding(false)
359 {
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  , detuneInitialized(false) // (lazily) initialize detune stuff
502 {
503  update(vibratoPercent);
504 
505  // make valgrind happy
506  memset(regs, 0, sizeof(regs));
507 
508  reset(time);
509  registerSound(config);
510 
511  // only attach once all initialization is successful
512  vibratoPercent.attach(*this);
513  detunePercent .attach(*this);
514 }
515 
517 {
518  vibratoPercent.detach(*this);
519  detunePercent .detach(*this);
520 
521  unregisterSound();
522 }
523 
524 void AY8910::reset(EmuTime::param time)
525 {
526  // Reset generators and envelope.
527  for (auto& t : tone) t.reset();
528  noise.reset();
529  envelope.reset();
530  // Reset registers and values derived from them.
531  for (auto reg : xrange(16)) {
532  wrtReg(reg, 0, time);
533  }
534 }
535 
536 
537 byte AY8910::readRegister(unsigned reg, EmuTime::param time)
538 {
539  if (reg >= 16) return 255;
540  switch (reg) {
541  case AY_PORTA:
542  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
543  regs[reg] = periphery.readA(time);
544  }
545  break;
546  case AY_PORTB:
547  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
548  regs[reg] = periphery.readB(time);
549  }
550  break;
551  }
552 
553  // TODO some AY8910 models have 1F as mask for registers 1, 3, 5
554  static constexpr byte regMask[16] = {
555  0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
556  0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
557  };
558  return isAY8910 ? regs[reg] & regMask[reg]
559  : regs[reg];
560 }
561 
562 byte AY8910::peekRegister(unsigned reg, EmuTime::param time) const
563 {
564  if (reg >= 16) return 255;
565  switch (reg) {
566  case AY_PORTA:
567  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
568  return periphery.readA(time);
569  }
570  break;
571  case AY_PORTB:
572  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
573  return periphery.readB(time);
574  }
575  break;
576  }
577  return regs[reg];
578 }
579 
580 
581 void AY8910::writeRegister(unsigned reg, byte value, EmuTime::param time)
582 {
583  if (reg >= 16) return;
584  if ((reg < AY_PORTA) && (reg == AY_ESHAPE || regs[reg] != value)) {
585  // Update the output buffer before changing the register.
586  updateStream(time);
587  }
588  wrtReg(reg, value, time);
589 }
590 void AY8910::wrtReg(unsigned reg, byte value, EmuTime::param time)
591 {
592  // Warn/force port directions
593  if (reg == AY_ENABLE) {
594  if (value & PORT_A_DIRECTION) {
595  directionsCallback.execute();
596  }
597  if (ignorePortDirections)
598  {
599  // portA -> input
600  // portB -> output
601  value = (value & ~PORT_A_DIRECTION) | PORT_B_DIRECTION;
602  }
603  }
604 
605  // Note: unused bits are stored as well; they can be read back.
606  byte diff = regs[reg] ^ value;
607  regs[reg] = value;
608 
609  switch (reg) {
610  case AY_AFINE:
611  case AY_ACOARSE:
612  case AY_BFINE:
613  case AY_BCOARSE:
614  case AY_CFINE:
615  case AY_CCOARSE:
616  tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
617  break;
618  case AY_NOISEPER:
619  // Half the frequency of tone generation.
620  //
621  // Verified on turboR GT: value=0 and value=1 sound the same.
622  //
623  // Likely in real AY8910 this is implemented by driving the
624  // noise generator at halve the frequency instead of
625  // multiplying the value by 2 (hence the correction for value=0
626  // here). But the effect is the same(?).
627  noise.setPeriod(2 * std::max(1, value & 0x1F));
628  break;
629  case AY_AVOL:
630  case AY_BVOL:
631  case AY_CVOL:
632  amplitude.setChannelVolume(reg - AY_AVOL, value);
633  break;
634  case AY_EFINE:
635  case AY_ECOARSE:
636  // also half the frequency of tone generation, but handled
637  // inside Envelope::setPeriod()
638  envelope.setPeriod(regs[AY_EFINE] + 256 * regs[AY_ECOARSE]);
639  break;
640  case AY_ESHAPE:
641  envelope.setShape(value);
642  break;
643  case AY_ENABLE:
644  if (diff & PORT_A_DIRECTION) {
645  // port A changed
646  if (value & PORT_A_DIRECTION) {
647  // from input to output
648  periphery.writeA(regs[AY_PORTA], time);
649  } else {
650  // from output to input
651  periphery.writeA(0xff, time);
652  }
653  }
654  if (diff & PORT_B_DIRECTION) {
655  // port B changed
656  if (value & PORT_B_DIRECTION) {
657  // from input to output
658  periphery.writeB(regs[AY_PORTB], time);
659  } else {
660  // from output to input
661  periphery.writeB(0xff, time);
662  }
663  }
664  break;
665  case AY_PORTA:
666  if (regs[AY_ENABLE] & PORT_A_DIRECTION) { // output
667  periphery.writeA(value, time);
668  }
669  break;
670  case AY_PORTB:
671  if (regs[AY_ENABLE] & PORT_B_DIRECTION) { // output
672  periphery.writeB(value, time);
673  }
674  break;
675  }
676 }
677 
678 void AY8910::generateChannels(float** bufs, unsigned num)
679 {
680  // Disable channels with volume 0: since the sample value doesn't matter,
681  // we can use the fastest path.
682  unsigned chanEnable = regs[AY_ENABLE];
683  for (auto chan : xrange(3)) {
684  if ((!amplitude.followsEnvelope(chan) &&
685  (amplitude.getVolume(chan) == 0.0f)) ||
686  (amplitude.followsEnvelope(chan) &&
687  !envelope.isChanging() &&
688  (envelope.getVolume() == 0.0f))) {
689  bufs[chan] = nullptr;
690  tone[chan].advance(num);
691  chanEnable |= 0x09 << chan;
692  }
693  }
694  // Noise disabled on all channels?
695  if ((chanEnable & 0x38) == 0x38) {
696  noise.advance(num);
697  }
698 
699  // Calculate samples.
700  // The 8910 has three outputs, each output is the mix of one of the
701  // three tone generators and of the (single) noise generator. The two
702  // are mixed BEFORE going into the DAC. The formula to mix each channel
703  // is:
704  // (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable),
705  // where ToneOn and NoiseOn are the current generator state
706  // and ToneDisable and NoiseDisable come from the enable reg.
707  // Note that this means that if both tone and noise are disabled, the
708  // output is 1, not 0, and can be modulated by changing the volume.
709  bool envelopeUpdated = false;
710  Envelope initialEnvelope = envelope;
711  NoiseGenerator initialNoise = noise;
712  for (unsigned chan = 0; chan < 3; ++chan, chanEnable >>= 1) {
713  auto* buf = bufs[chan];
714  if (!buf) continue;
715  ToneGenerator& t = tone[chan];
716  if (envelope.isChanging() && amplitude.followsEnvelope(chan)) {
717  envelopeUpdated = true;
718  envelope = initialEnvelope;
719  if ((chanEnable & 0x09) == 0x08) {
720  // no noise, square wave: alternating between 0 and 1.
721  auto val = t.getOutput() * envelope.getVolume();
722  unsigned remaining = num;
723  unsigned nextE = envelope.getNextEventTime();
724  unsigned nextT = t.getNextEventTime();
725  while ((nextT <= remaining) || (nextE <= remaining)) {
726  if (nextT < nextE) {
727  addFill(buf, val, nextT);
728  remaining -= nextT;
729  nextE -= nextT;
730  envelope.advanceFast(nextT);
731  t.doNextEvent(*this);
732  nextT = t.getNextEventTime();
733  } else if (nextE < nextT) {
734  addFill(buf, val, nextE);
735  remaining -= nextE;
736  nextT -= nextE;
737  t.advanceFast(nextE);
738  envelope.doNextEvent();
739  nextE = envelope.getNextEventTime();
740  } else {
741  assert(nextT == nextE);
742  addFill(buf, val, nextT);
743  remaining -= nextT;
744  t.doNextEvent(*this);
745  nextT = t.getNextEventTime();
746  envelope.doNextEvent();
747  nextE = envelope.getNextEventTime();
748  }
749  val = t.getOutput() * envelope.getVolume();
750  }
751  if (remaining) {
752  // last interval (without events)
753  addFill(buf, val, remaining);
754  t.advanceFast(remaining);
755  envelope.advanceFast(remaining);
756  }
757 
758  } else if ((chanEnable & 0x09) == 0x09) {
759  // no noise, channel disabled: always 1.
760  auto val = envelope.getVolume();
761  unsigned remaining = num;
762  unsigned next = envelope.getNextEventTime();
763  while (next <= remaining) {
764  addFill(buf, val, next);
765  remaining -= next;
766  envelope.doNextEvent();
767  val = envelope.getVolume();
768  next = envelope.getNextEventTime();
769  }
770  if (remaining) {
771  // last interval (without events)
772  addFill(buf, val, remaining);
773  envelope.advanceFast(remaining);
774  }
775  t.advance(num);
776 
777  } else if ((chanEnable & 0x09) == 0x00) {
778  // noise enabled, tone enabled
779  noise = initialNoise;
780  auto val = noise.getOutput() * t.getOutput() * envelope.getVolume();
781  unsigned remaining = num;
782  unsigned nextT = t.getNextEventTime();
783  unsigned nextN = noise.getNextEventTime();
784  unsigned nextE = envelope.getNextEventTime();
785  unsigned next = std::min(std::min(nextT, nextN), nextE);
786  while (next <= remaining) {
787  addFill(buf, val, next);
788  remaining -= next;
789  nextT -= next;
790  nextN -= next;
791  nextE -= next;
792  if (nextT) {
793  t.advanceFast(next);
794  } else {
795  t.doNextEvent(*this);
796  nextT = t.getNextEventTime();
797  }
798  if (nextN) {
799  noise.advanceFast(next);
800  } else {
801  noise.doNextEvent();
802  nextN = noise.getNextEventTime();
803  }
804  if (nextE) {
805  envelope.advanceFast(next);
806  } else {
807  envelope.doNextEvent();
808  nextE = envelope.getNextEventTime();
809  }
810  next = std::min(std::min(nextT, nextN), nextE);
811  val = noise.getOutput() * t.getOutput() * envelope.getVolume();
812  }
813  if (remaining) {
814  // last interval (without events)
815  addFill(buf, val, remaining);
816  t.advanceFast(remaining);
817  noise.advanceFast(remaining);
818  envelope.advanceFast(remaining);
819  }
820 
821  } else {
822  // noise enabled, tone disabled
823  noise = initialNoise;
824  auto val = noise.getOutput() * envelope.getVolume();
825  unsigned remaining = num;
826  unsigned nextE = envelope.getNextEventTime();
827  unsigned nextN = noise.getNextEventTime();
828  while ((nextN <= remaining) || (nextE <= remaining)) {
829  if (nextN < nextE) {
830  addFill(buf, val, nextN);
831  remaining -= nextN;
832  nextE -= nextN;
833  envelope.advanceFast(nextN);
834  noise.doNextEvent();
835  nextN = noise.getNextEventTime();
836  } else if (nextE < nextN) {
837  addFill(buf, val, nextE);
838  remaining -= nextE;
839  nextN -= nextE;
840  noise.advanceFast(nextE);
841  envelope.doNextEvent();
842  nextE = envelope.getNextEventTime();
843  } else {
844  assert(nextN == nextE);
845  addFill(buf, val, nextN);
846  remaining -= nextN;
847  noise.doNextEvent();
848  nextN = noise.getNextEventTime();
849  envelope.doNextEvent();
850  nextE = envelope.getNextEventTime();
851  }
852  val = noise.getOutput() * envelope.getVolume();
853  }
854  if (remaining) {
855  // last interval (without events)
856  addFill(buf, val, remaining);
857  noise.advanceFast(remaining);
858  envelope.advanceFast(remaining);
859  }
860  t.advance(num);
861  }
862  } else {
863  // no (changing) envelope on this channel
864  auto volume = amplitude.followsEnvelope(chan)
865  ? envelope.getVolume()
866  : amplitude.getVolume(chan);
867  if ((chanEnable & 0x09) == 0x08) {
868  // no noise, square wave: alternating between 0 and 1.
869  auto val = t.getOutput() * volume;
870  unsigned remaining = num;
871  unsigned next = t.getNextEventTime();
872  while (next <= remaining) {
873  addFill(buf, val, next);
874  val = volume - val;
875  remaining -= next;
876  t.doNextEvent(*this);
877  next = t.getNextEventTime();
878  }
879  if (remaining) {
880  // last interval (without events)
881  addFill(buf, val, remaining);
882  t.advanceFast(remaining);
883  }
884 
885  } else if ((chanEnable & 0x09) == 0x09) {
886  // no noise, channel disabled: always 1.
887  addFill(buf, volume, num);
888  t.advance(num);
889 
890  } else if ((chanEnable & 0x09) == 0x00) {
891  // noise enabled, tone enabled
892  noise = initialNoise;
893  auto val1 = t.getOutput() * volume;
894  auto val2 = val1 * noise.getOutput();
895  unsigned remaining = num;
896  unsigned nextN = noise.getNextEventTime();
897  unsigned nextT = t.getNextEventTime();
898  while ((nextN <= remaining) || (nextT <= remaining)) {
899  if (nextT < nextN) {
900  addFill(buf, val2, nextT);
901  remaining -= nextT;
902  nextN -= nextT;
903  noise.advanceFast(nextT);
904  t.doNextEvent(*this);
905  nextT = t.getNextEventTime();
906  val1 = volume - val1;
907  val2 = val1 * noise.getOutput();
908  } else if (nextN < nextT) {
909  addFill(buf, val2, nextN);
910  remaining -= nextN;
911  nextT -= nextN;
912  t.advanceFast(nextN);
913  noise.doNextEvent();
914  nextN = noise.getNextEventTime();
915  val2 = val1 * noise.getOutput();
916  } else {
917  assert(nextT == nextN);
918  addFill(buf, val2, nextT);
919  remaining -= nextT;
920  t.doNextEvent(*this);
921  nextT = t.getNextEventTime();
922  noise.doNextEvent();
923  nextN = noise.getNextEventTime();
924  val1 = volume - val1;
925  val2 = val1 * noise.getOutput();
926  }
927  }
928  if (remaining) {
929  // last interval (without events)
930  addFill(buf, val2, remaining);
931  t.advanceFast(remaining);
932  noise.advanceFast(remaining);
933  }
934 
935  } else {
936  // noise enabled, tone disabled
937  noise = initialNoise;
938  unsigned remaining = num;
939  auto val = noise.getOutput() * volume;
940  unsigned next = noise.getNextEventTime();
941  while (next <= remaining) {
942  addFill(buf, val, next);
943  remaining -= next;
944  noise.doNextEvent();
945  val = noise.getOutput() * volume;
946  next = noise.getNextEventTime();
947  }
948  if (remaining) {
949  // last interval (without events)
950  addFill(buf, val, remaining);
951  noise.advanceFast(remaining);
952  }
953  t.advance(num);
954  }
955  }
956  }
957 
958  // Envelope not yet updated?
959  if (envelope.isChanging() && !envelopeUpdated) {
960  envelope.advance(num);
961  }
962 }
963 
964 float AY8910::getAmplificationFactorImpl() const
965 {
966  return 1.0f;
967 }
968 
969 void AY8910::update(const Setting& setting) noexcept
970 {
971  if (&setting == one_of(&vibratoPercent, &detunePercent)) {
972  doDetune = (vibratoPercent.getDouble() != 0) ||
973  (detunePercent .getDouble() != 0);
974  if (doDetune && !detuneInitialized) {
975  detuneInitialized = true;
976  initDetune();
977  }
978  } else {
980  }
981 }
982 
983 
984 // Debuggable
985 
986 AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const std::string& name_)
987  : SimpleDebuggable(motherBoard_, name_ + " regs", "PSG", 0x10)
988 {
989 }
990 
991 byte AY8910::Debuggable::read(unsigned address, EmuTime::param time)
992 {
993  auto& ay8910 = OUTER(AY8910, debuggable);
994  return ay8910.readRegister(address, time);
995 }
996 
997 void AY8910::Debuggable::write(unsigned address, byte value, EmuTime::param time)
998 {
999  auto& ay8910 = OUTER(AY8910, debuggable);
1000  return ay8910.writeRegister(address, value, time);
1001 }
1002 
1003 
1004 template<typename Archive>
1005 void AY8910::serialize(Archive& ar, unsigned /*version*/)
1006 {
1007  ar.serialize("toneGenerators", tone,
1008  "noiseGenerator", noise,
1009  "envelope", envelope,
1010  "registers", regs);
1011 
1012  // amplitude
1013  if constexpr (Archive::IS_LOADER) {
1014  for (auto i : xrange(3)) {
1015  amplitude.setChannelVolume(i, regs[i + AY_AVOL]);
1016  }
1017  }
1018 }
1020 
1021 // version 1: initial version
1022 // version 2: removed 'output' member variable
1023 template<typename Archive>
1024 void AY8910::Generator::serialize(Archive& ar, unsigned /*version*/)
1025 {
1026  ar.serialize("period", period,
1027  "count", count);
1028 }
1029 INSTANTIATE_SERIALIZE_METHODS(AY8910::Generator);
1030 
1031 // version 1: initial version
1032 // version 2: moved 'output' variable from base class to here
1033 template<typename Archive>
1034 void AY8910::ToneGenerator::serialize(Archive& ar, unsigned version)
1035 {
1036  ar.template serializeInlinedBase<Generator>(*this, version);
1037  ar.serialize("vibratoCount", vibratoCount,
1038  "detuneCount", detuneCount);
1039  if (ar.versionAtLeast(version, 2)) {
1040  ar.serialize("output", output);
1041  } else {
1042  // don't bother trying to restore this from the old location:
1043  // it doesn't influence any MSX-observable state, and the
1044  // difference in generated sound will likely be inaudible
1045  }
1046 }
1047 INSTANTIATE_SERIALIZE_METHODS(AY8910::ToneGenerator);
1048 
1049 // version 1: initial version
1050 // version 2: removed 'output' variable from base class, not stored here but
1051 // instead it's calculated from 'random' when needed
1052 template<typename Archive>
1053 void AY8910::NoiseGenerator::serialize(Archive& ar, unsigned version)
1054 {
1055  ar.template serializeInlinedBase<Generator>(*this, version);
1056  ar.serialize("random", random);
1057 }
1058 INSTANTIATE_SERIALIZE_METHODS(AY8910::NoiseGenerator);
1059 
1060 template<typename Archive>
1061 void AY8910::Envelope::serialize(Archive& ar, unsigned /*version*/)
1062 {
1063  ar.serialize("period", period,
1064  "count", count,
1065  "step", step,
1066  "attack", attack,
1067  "hold", hold,
1068  "alternate", alternate,
1069  "holding", holding);
1070 }
1071 INSTANTIATE_SERIALIZE_METHODS(AY8910::Envelope);
1072 
1073 } // namespace openmsx
BaseSetting * setting
Definition: Interpreter.cc: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:524
byte readRegister(unsigned reg, EmuTime::param time)
Definition: AY8910.cc:537
byte peekRegister(unsigned reg, EmuTime::param time) const
Definition: AY8910.cc:562
void serialize(Archive &ar, unsigned version)
Definition: AY8910.cc:1005
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:581
void update(const Setting &setting) noexcept override
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:138
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
Definition: SoundDevice.cc:43
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:133
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:88
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
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition: lz4.cc:146
constexpr double pi
Definition: Math.hh:21
constexpr float cubicHermite(const float *y, float x)
Definition: Math.hh:162
constexpr double e
Definition: Math.hh:18
constexpr double round(double x)
Definition: cstd.hh:246
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:258
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:276
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:727
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:127
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:1009
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617
constexpr auto xrange(T e)
Definition: xrange.hh:133