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