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