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 static const float NATIVE_FREQ_FLOAT = (3579545.0f / 2) / 8;
35 static const int NATIVE_FREQ_INT = lrintf(NATIVE_FREQ_FLOAT);
36 
37 static const int PORT_A_DIRECTION = 0x40;
38 static const 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) {
77  tables.AY8910Envelope[i + 0] = tables.YM2149Envelope[i + 1];
78  tables.AY8910Envelope[i + 1] = tables.YM2149Envelope[i + 1];
79  }
80 
81  return tables;
82 }
83 static 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  float n0 = noiseTab[xi + 0];
110  float n1 = noiseTab[xi + 1];
111  float n2 = noiseTab[xi + 2];
112  float n3 = noiseTab[xi + 3];
113  float a = n3 - n2 + n1 - n0;
114  float b = n0 - n1 - a;
115  float c = n2 - n0;
116  float d = n1;
117  return ((a * xf + b) * xf + c) * xf + d;
118 }
119 
120 
121 // Generator:
122 
123 inline void AY8910::Generator::reset()
124 {
125  count = 0;
126 }
127 
128 inline void AY8910::Generator::setPeriod(int value)
129 {
130  // Careful studies of the chip output prove that it instead counts up from
131  // 0 until the counter becomes greater or equal to the period. This is an
132  // important difference when the program is rapidly changing the period to
133  // modulate the sound.
134  // Also, note that period = 0 is the same as period = 1. This is mentioned
135  // in the YM2203 data sheets. However, this does NOT apply to the Envelope
136  // period. In that case, period = 0 is half as period = 1.
137  period = std::max(1, value);
138  count = std::min(count, period - 1);
139 }
140 
141 inline unsigned AY8910::Generator::getNextEventTime() const
142 {
143  assert(count < period);
144  return period - count;
145 }
146 
147 inline void AY8910::Generator::advanceFast(unsigned duration)
148 {
149  count += duration;
150  assert(count < period);
151 }
152 
153 
154 // ToneGenerator:
155 
156 AY8910::ToneGenerator::ToneGenerator()
157 {
158  reset();
159 }
160 
161 inline void AY8910::ToneGenerator::reset()
162 {
163  Generator::reset();
164  output = false;
165 }
166 
167 int AY8910::ToneGenerator::getDetune(AY8910& ay8910)
168 {
169  int result = 0;
170  float vibPerc = ay8910.vibratoPercent.getDouble();
171  if (vibPerc != 0.0f) {
172  int vibratoPeriod = int(
173  NATIVE_FREQ_FLOAT /
174  float(ay8910.vibratoFrequency.getDouble()));
175  vibratoCount += period;
176  vibratoCount %= vibratoPeriod;
177  result += int(
178  sinf((float(2 * M_PI) * vibratoCount) / vibratoPeriod)
179  * vibPerc * 0.01f * period);
180  }
181  float detunePerc = ay8910.detunePercent.getDouble();
182  if (detunePerc != 0.0f) {
183  float detunePeriod = NATIVE_FREQ_FLOAT /
184  float(ay8910.detuneFrequency.getDouble());
185  detuneCount += period;
186  float noiseIdx = detuneCount / detunePeriod;
187  float detuneNoise = noiseValue( noiseIdx)
188  + noiseValue(2.0f * noiseIdx) / 2.0f;
189  result += int(detuneNoise * detunePerc * 0.01f * period);
190  }
191  return std::min(result, period - 1);
192 }
193 
194 inline void AY8910::ToneGenerator::advance(int duration)
195 {
196  assert(count < period);
197  count += duration;
198  if (count >= period) {
199  // Calculate number of output transitions.
200  int cycles = count / period;
201  count -= period * cycles; // equivalent to count %= period;
202  output ^= cycles & 1;
203  }
204 }
205 
206 inline void AY8910::ToneGenerator::doNextEvent(AY8910& ay8910)
207 {
208  if (unlikely(ay8910.doDetune)) {
209  count = getDetune(ay8910);
210  } else {
211  count = 0;
212  }
213  output ^= 1;
214 }
215 
216 
217 // NoiseGenerator:
218 
219 AY8910::NoiseGenerator::NoiseGenerator()
220 {
221  reset();
222 }
223 
224 inline void AY8910::NoiseGenerator::reset()
225 {
226  Generator::reset();
227  random = 1;
228 }
229 
230 inline void AY8910::NoiseGenerator::doNextEvent()
231 {
232  count = 0;
233 
234  // The Random Number Generator of the 8910 is a 17-bit shift register.
235  // The input to the shift register is bit0 XOR bit3 (bit0 is the
236  // output). Verified on real AY8910 and YM2149 chips.
237  //
238  // Fibonacci configuartion:
239  // random ^= ((random & 1) ^ ((random >> 3) & 1)) << 17;
240  // random >>= 1;
241  // Galois configuration:
242  // if (random & 1) random ^= 0x24000;
243  // random >>= 1;
244  // or alternatively:
245  random = (random >> 1) ^ ((random & 1) << 13) ^ ((random & 1) << 16);
246 }
247 
248 inline void AY8910::NoiseGenerator::advance(int duration)
249 {
250  assert(count < period);
251  count += duration;
252  int cycles = count / period;
253  count -= cycles * period; // equivalent to count %= period
254 
255  // The following loops advance the random state N steps at once. The
256  // values for N (4585, 275, 68, 8, 1) are chosen so that:
257  // - The formulas are relatively simple.
258  // - The ratio between the step sizes is roughly the same.
259  for (; cycles >= 4585; cycles -= 4585) {
260  random = ((random & 0x1f) << 12)
261  ^ ((random & 0x1f) << 9)
262  ^ random
263  ^ ( random >> 5);
264  }
265  for (; cycles >= 275; cycles -= 275) {
266  random = ((random & 0x03f) << 11)
267  ^ ((random & 0x1c0) << 8)
268  ^ ((random & 0x1ff) << 5)
269  ^ random
270  ^ ( random >> 6)
271  ^ ( random >> 9);
272  }
273  for (; cycles >= 68; cycles -= 68) {
274  random = ((random & 0xfff) << 5)
275  ^ ((random & 0xfff) << 2)
276  ^ random
277  ^ ( random >> 12);
278  }
279  for (; cycles >= 8; cycles -= 8) {
280  random = ((random & 0xff) << 9)
281  ^ ((random & 0xff) << 6)
282  ^ ( random >> 8);
283  }
284  for (; cycles >= 1; cycles -= 1) {
285  random = ((random & 1) << 16)
286  ^ ((random & 1) << 13)
287  ^ ( random >> 1);
288  }
289 }
290 
291 
292 // Amplitude:
293 
294 static bool checkAY8910(const DeviceConfig& config)
295 {
296  string type = StringOp::toLower(config.getChildData("type", "ay8910"));
297  if (type == "ay8910") {
298  return true;
299  } else if (type == "ym2149") {
300  return false;
301  } else {
302  throw FatalError("Unknown PSG type: ", type);
303  }
304 }
305 
306 AY8910::Amplitude::Amplitude(const DeviceConfig& config)
307  : isAY8910(checkAY8910(config))
308 {
309  vol[0] = vol[1] = vol[2] = 0.0f;
310  envChan[0] = false;
311  envChan[1] = false;
312  envChan[2] = false;
313  envVolTable = isAY8910 ? tables.AY8910Envelope : tables.YM2149Envelope;
314 
315  if (0) {
316  std::cout << "YM2149Envelope:";
317  for (int i = 0; i < 32; ++i) {
318  std::cout << ' ' << std::hexfloat << tables.YM2149Envelope[i];
319  }
320  std::cout << "\nAY8910Envelope:";
321  for (int i = 0; i < 32; ++i) {
322  std::cout << ' ' << std::hexfloat << tables.AY8910Envelope[i];
323  }
324  std::cout << "\nvolume:";
325  for (int i = 0; i < 16; ++i) {
326  std::cout << ' ' << std::hexfloat << tables.volume[i];
327  }
328  std::cout << '\n';
329  }
330 }
331 
332 const float* AY8910::Amplitude::getEnvVolTable() const
333 {
334  return envVolTable;
335 }
336 
337 inline float AY8910::Amplitude::getVolume(unsigned chan) const
338 {
339  assert(!followsEnvelope(chan));
340  return vol[chan];
341 }
342 
343 inline void AY8910::Amplitude::setChannelVolume(unsigned chan, unsigned value)
344 {
345  envChan[chan] = (value & 0x10) != 0;
346  vol[chan] = tables.volume[value & 0x0F];
347 }
348 
349 inline bool AY8910::Amplitude::followsEnvelope(unsigned chan) const
350 {
351  return envChan[chan];
352 }
353 
354 
355 // Envelope:
356 
357 // AY8910 and YM2149 behave different here:
358 // YM2149 envelope goes twice as fast and has twice as many levels. Here
359 // we implement the YM2149 behaviour, but to get the AY8910 behaviour we
360 // repeat every level twice in the envVolTable
361 
362 inline AY8910::Envelope::Envelope(const float* envVolTable_)
363 {
364  envVolTable = envVolTable_;
365  period = 1;
366  count = 0;
367  step = 0;
368  attack = 0;
369  hold = false;
370  alternate = false;
371  holding = false;
372 }
373 
374 inline void AY8910::Envelope::reset()
375 {
376  count = 0;
377 }
378 
379 inline void AY8910::Envelope::setPeriod(int value)
380 {
381  // twice as fast as AY8910
382  // see also Generator::setPeriod()
383  period = std::max(1, 2 * value);
384  count = std::min(count, period - 1);
385 }
386 
387 inline float AY8910::Envelope::getVolume() const
388 {
389  return envVolTable[step ^ attack];
390 }
391 
392 inline void AY8910::Envelope::setShape(unsigned shape)
393 {
394  // do 32 steps for both AY8910 and YM2149
395  /*
396  envelope shapes:
397  C AtAlH
398  0 0 x x \___
399  0 1 x x /___
400  1 0 0 0 \\\\
401  1 0 0 1 \___
402  1 0 1 0 \/\/
403  1 0 1 1 \
404  1 1 0 0 ////
405  1 1 0 1 /
406  1 1 1 0 /\/\
407  1 1 1 1 /___
408  */
409  attack = (shape & 0x04) ? 0x1F : 0x00;
410  if ((shape & 0x08) == 0) {
411  // If Continue = 0, map the shape to the equivalent one
412  // which has Continue = 1.
413  hold = true;
414  alternate = attack != 0;
415  } else {
416  hold = (shape & 0x01) != 0;
417  alternate = (shape & 0x02) != 0;
418  }
419  count = 0;
420  step = 0x1F;
421  holding = false;
422 }
423 
424 inline bool AY8910::Envelope::isChanging() const
425 {
426  return !holding;
427 }
428 
429 inline void AY8910::Envelope::doSteps(int steps)
430 {
431  // For best performance callers should check upfront whether
432  // isChanging() == true
433  // Though we can't assert on it because the condition might change
434  // in the inner loop(s) of generateChannels().
435  //assert(!holding);
436 
437  if (holding) return;
438  step -= steps;
439 
440  // Check current envelope position.
441  if (step < 0) {
442  if (hold) {
443  if (alternate) attack ^= 0x1F;
444  holding = true;
445  step = 0;
446  } else {
447  // If step has looped an odd number of times
448  // (usually 1), invert the output.
449  if (alternate && (step & 0x10)) {
450  attack ^= 0x1F;
451  }
452  step &= 0x1F;
453  }
454  }
455 }
456 
457 inline void AY8910::Envelope::advance(int duration)
458 {
459  assert(count < period);
460  count += duration * 2;
461  if (count >= period) {
462  int steps = count / period;
463  count -= steps * period; // equivalent to count %= period;
464  doSteps(steps);
465  }
466 }
467 
468 inline void AY8910::Envelope::doNextEvent()
469 {
470  count = 0;
471  doSteps(period == 1 ? 2 : 1);
472 }
473 
474 inline unsigned AY8910::Envelope::getNextEventTime() const
475 {
476  assert(count < period);
477  return (period - count + 1) / 2;
478 }
479 
480 inline void AY8910::Envelope::advanceFast(unsigned duration)
481 {
482  count += 2 * duration;
483  assert(count < period);
484 }
485 
486 
487 
488 // AY8910 main class:
489 
490 AY8910::AY8910(const std::string& name_, AY8910Periphery& periphery_,
491  const DeviceConfig& config, EmuTime::param time)
492  : ResampledSoundDevice(config.getMotherBoard(), name_, "PSG", 3, NATIVE_FREQ_INT, false)
493  , periphery(periphery_)
494  , debuggable(config.getMotherBoard(), getName())
495  , vibratoPercent(
496  config.getCommandController(), getName() + "_vibrato_percent",
497  "controls strength of vibrato effect", 0.0, 0.0, 10.0)
498  , vibratoFrequency(
499  config.getCommandController(), getName() + "_vibrato_frequency",
500  "frequency of vibrato effect in Hertz", 5, 1.0, 10.0)
501  , detunePercent(
502  config.getCommandController(), getName() + "_detune_percent",
503  "controls strength of detune effect", 0.0, 0.0, 10.0)
504  , detuneFrequency(
505  config.getCommandController(), getName() + "_detune_frequency",
506  "frequency of detune effect in Hertz", 5.0, 1.0, 100.0)
507  , directionsCallback(
508  config.getGlobalSettings().getInvalidPsgDirectionsSetting())
509  , amplitude(config)
510  , envelope(amplitude.getEnvVolTable())
511  , isAY8910(checkAY8910(config))
512  , ignorePortDirections(config.getChildDataAsBool("ignorePortDirections", true))
513 {
514  // (lazily) initialize detune stuff
515  detuneInitialized = false;
516  update(vibratoPercent);
517 
518  // make valgrind happy
519  memset(regs, 0, sizeof(regs));
520 
521  reset(time);
522  registerSound(config);
523 
524  // only attach once all initialization is successful
525  vibratoPercent.attach(*this);
526  detunePercent .attach(*this);
527 }
528 
530 {
531  vibratoPercent.detach(*this);
532  detunePercent .detach(*this);
533 
534  unregisterSound();
535 }
536 
537 void AY8910::reset(EmuTime::param time)
538 {
539  // Reset generators and envelope.
540  for (auto& t : tone) t.reset();
541  noise.reset();
542  envelope.reset();
543  // Reset registers and values derived from them.
544  for (unsigned reg = 0; reg <= 15; ++reg) {
545  wrtReg(reg, 0, time);
546  }
547 }
548 
549 
550 byte AY8910::readRegister(unsigned reg, EmuTime::param time)
551 {
552  if (reg >= 16) return 255;
553  switch (reg) {
554  case AY_PORTA:
555  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
556  regs[reg] = periphery.readA(time);
557  }
558  break;
559  case AY_PORTB:
560  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
561  regs[reg] = periphery.readB(time);
562  }
563  break;
564  }
565 
566  // TODO some AY8910 models have 1F as mask for registers 1, 3, 5
567  static const byte regMask[16] = {
568  0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
569  0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
570  };
571  return isAY8910 ? regs[reg] & regMask[reg]
572  : regs[reg];
573 }
574 
575 byte AY8910::peekRegister(unsigned reg, EmuTime::param time) const
576 {
577  if (reg >= 16) return 255;
578  switch (reg) {
579  case AY_PORTA:
580  if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
581  return periphery.readA(time);
582  }
583  break;
584  case AY_PORTB:
585  if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
586  return periphery.readB(time);
587  }
588  break;
589  }
590  return regs[reg];
591 }
592 
593 
594 void AY8910::writeRegister(unsigned reg, byte value, EmuTime::param time)
595 {
596  if (reg >= 16) return;
597  if ((reg < AY_PORTA) && (reg == AY_ESHAPE || regs[reg] != value)) {
598  // Update the output buffer before changing the register.
599  updateStream(time);
600  }
601  wrtReg(reg, value, time);
602 }
603 void AY8910::wrtReg(unsigned reg, byte value, EmuTime::param time)
604 {
605  // Warn/force port directions
606  if (reg == AY_ENABLE) {
607  if (value & PORT_A_DIRECTION) {
608  directionsCallback.execute();
609  }
610  if (ignorePortDirections)
611  {
612  // portA -> input
613  // portB -> output
614  value = (value & ~PORT_A_DIRECTION) | PORT_B_DIRECTION;
615  }
616  }
617 
618  // Note: unused bits are stored as well; they can be read back.
619  byte diff = regs[reg] ^ value;
620  regs[reg] = value;
621 
622  switch (reg) {
623  case AY_AFINE:
624  case AY_ACOARSE:
625  case AY_BFINE:
626  case AY_BCOARSE:
627  case AY_CFINE:
628  case AY_CCOARSE:
629  tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
630  break;
631  case AY_NOISEPER:
632  // half the frequency of tone generation
633  noise.setPeriod(2 * (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 == &vibratoPercent) ||
978  (&setting == &detunePercent)) {
979  doDetune = (vibratoPercent.getDouble() != 0) ||
980  (detunePercent .getDouble() != 0);
981  if (doDetune && !detuneInitialized) {
982  detuneInitialized = true;
983  initDetune();
984  }
985  } else {
987  }
988 }
989 
990 
991 // Debuggable
992 
993 AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const string& name_)
994  : SimpleDebuggable(motherBoard_, name_ + " regs", "PSG", 0x10)
995 {
996 }
997 
998 byte AY8910::Debuggable::read(unsigned address, EmuTime::param time)
999 {
1000  auto& ay8910 = OUTER(AY8910, debuggable);
1001  return ay8910.readRegister(address, time);
1002 }
1003 
1004 void AY8910::Debuggable::write(unsigned address, byte value, EmuTime::param time)
1005 {
1006  auto& ay8910 = OUTER(AY8910, debuggable);
1007  return ay8910.writeRegister(address, value, time);
1008 }
1009 
1010 
1011 template<typename Archive>
1012 void AY8910::serialize(Archive& ar, unsigned /*version*/)
1013 {
1014  ar.serialize("toneGenerators", tone,
1015  "noiseGenerator", noise,
1016  "envelope", envelope,
1017  "registers", regs);
1018 
1019  // amplitude
1020  if (ar.isLoader()) {
1021  for (int i = 0; i < 3; ++i) {
1022  amplitude.setChannelVolume(i, regs[i + AY_AVOL]);
1023  }
1024  }
1025 }
1027 
1028 // version 1: initial version
1029 // version 2: removed 'output' member variable
1030 template<typename Archive>
1031 void AY8910::Generator::serialize(Archive& ar, unsigned /*version*/)
1032 {
1033  ar.serialize("period", period,
1034  "count", count);
1035 }
1036 INSTANTIATE_SERIALIZE_METHODS(AY8910::Generator);
1037 
1038 // version 1: initial version
1039 // version 2: moved 'output' variable from base class to here
1040 template<typename Archive>
1041 void AY8910::ToneGenerator::serialize(Archive& ar, unsigned version)
1042 {
1043  ar.template serializeInlinedBase<Generator>(*this, version);
1044  ar.serialize("vibratoCount", vibratoCount,
1045  "detuneCount", detuneCount);
1046  if (ar.versionAtLeast(version, 2)) {
1047  ar.serialize("output", output);
1048  } else {
1049  // don't bother trying to restore this from the old location:
1050  // it doesn't influence any MSX-observable state, and the
1051  // difference in generated sound will likely be inaudible
1052  }
1053 }
1054 INSTANTIATE_SERIALIZE_METHODS(AY8910::ToneGenerator);
1055 
1056 // version 1: initial version
1057 // version 2: removed 'output' variable from base class, not stored here but
1058 // instead it's calculated from 'random' when needed
1059 template<typename Archive>
1060 void AY8910::NoiseGenerator::serialize(Archive& ar, unsigned version)
1061 {
1062  ar.template serializeInlinedBase<Generator>(*this, version);
1063  ar.serialize("random", random);
1064 }
1065 INSTANTIATE_SERIALIZE_METHODS(AY8910::NoiseGenerator);
1066 
1067 template<typename Archive>
1068 void AY8910::Envelope::serialize(Archive& ar, unsigned /*version*/)
1069 {
1070  ar.serialize("period", period,
1071  "count", count,
1072  "step", step,
1073  "attack", attack,
1074  "hold", hold,
1075  "alternate", alternate,
1076  "holding", holding);
1077 }
1078 INSTANTIATE_SERIALIZE_METHODS(AY8910::Envelope);
1079 
1080 } // namespace openmsx
float AY8910Envelope[32]
Definition: AY8910.cc:49
float YM2149Envelope[32]
Definition: AY8910.cc:48
virtual void writeB(byte value, EmuTime::param time)
Similar to writeA, but writes port B.
const std::string & getChildData(string_view name) const
Definition: DeviceConfig.cc:43
#define unlikely(x)
Definition: likely.hh:15
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
Definition: SoundDevice.cc:45
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
virtual void writeA(byte value, EmuTime::param time)
Writes to the peripheral on port A.
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:131
This class implements the AY-3-8910 sound chip.
Definition: AY8910.hh:19
#define M_PI
Definition: Math.hh:26
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
TclObject execute()
Definition: TclCallback.cc:40
mat3 n3(vec3(1, 0, 3), vec3(4, 5, 6), vec3(7, 8, 9))
virtual byte readB(EmuTime::param time)
Similar to readA, but reads port B.
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
Register
Definition: AY8910.cc:40
void serialize(Archive &ar, unsigned version)
Definition: AY8910.cc:1012
double getDouble() const noexcept
Definition: FloatSetting.hh:20
void advance(octet_iterator &it, distance_type n, octet_iterator end)
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:136
void attach(Observer< T > &observer)
Definition: Subject.hh:49
virtual byte readA(EmuTime::param time)
Reads the state of the peripheral on port A.
float volume[16]
Definition: AY8910.cc:50
byte peekRegister(unsigned reg, EmuTime::param time) const
Definition: AY8910.cc:575
string toLower(string_view str)
Definition: StringOp.cc:62
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
auto count(InputRange &&range, const T &value)
Definition: ranges.hh:209
void update(const Setting &setting) override
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
void detach(Observer< T > &observer)
Definition: Subject.hh:55
byte readRegister(unsigned reg, EmuTime::param time)
Definition: AY8910.cc:550
#define OUTER(type, member)
Definition: outer.hh:38
AY8910(const std::string &name, AY8910Periphery &periphery, const DeviceConfig &config, EmuTime::param time)
Definition: AY8910.cc:490
void reset(EmuTime::param time)
Definition: AY8910.cc:537
TclObject t
Models the general purpose I/O ports of the AY8910.
auto & global_urng()
Return reference to a (shared) global random number generator.
Definition: random.hh:8
void serialize(Archive &ar, T &t, unsigned version)
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:594
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:90