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