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