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