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