51 std::array<float, 32> result = {};
53 double factor = cstd::pow<5, 3>(0.5, 0.25);
54 for (
int i = 31; i > 0; --i) {
55 result[i] = float(out);
63 std::array<float, 32> result = {};
66 for (
int i = 2; i < 32; i += 2) {
73 std::array<float, 16> result = {};
75 for (
auto i :
xrange(1, 16)) {
84 static float noiseTab[256 + 3];
86 static void initDetune()
89 std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
91 for (
auto i :
xrange(256)) {
92 noiseTab[i] = distribution(generator);
94 noiseTab[256] = noiseTab[0];
95 noiseTab[257] = noiseTab[1];
96 noiseTab[258] = noiseTab[2];
98 static float noiseValue(
float x)
111 inline void AY8910::Generator::reset()
116 inline void AY8910::Generator::setPeriod(
int value)
129 inline unsigned AY8910::Generator::getNextEventTime()
const
131 assert(
count < period);
132 return period -
count;
135 inline void AY8910::Generator::advanceFast(
unsigned duration)
138 assert(
count < period);
144 AY8910::ToneGenerator::ToneGenerator()
149 inline void AY8910::ToneGenerator::reset()
155 int AY8910::ToneGenerator::getDetune(
AY8910& ay8910)
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;
166 sinf((
float(2 *
Math::pi) * vibratoCount) / vibratoPeriod)
167 * vibPerc * 0.01f * period);
169 float detunePerc = ay8910.detunePercent.getDouble();
170 if (detunePerc != 0.0f) {
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);
179 return std::min(result, period - 1);
184 assert(
count < period);
186 if (
count >= period) {
188 int cycles =
count / period;
189 count -= period * cycles;
190 output ^= cycles & 1;
194 inline void AY8910::ToneGenerator::doNextEvent(
AY8910& ay8910)
196 if (ay8910.doDetune) [[unlikely]] {
197 count = getDetune(ay8910);
207 AY8910::NoiseGenerator::NoiseGenerator()
212 inline void AY8910::NoiseGenerator::reset()
218 inline void AY8910::NoiseGenerator::doNextEvent()
233 random = (random >> 1) ^ ((random & 1) << 13) ^ ((random & 1) << 16);
238 assert(
count < period);
240 int cycles =
count / period;
241 count -= cycles * period;
247 for (; cycles >= 4585; cycles -= 4585) {
248 random = ((random & 0x1f) << 12)
249 ^ ((random & 0x1f) << 9)
253 for (; cycles >= 275; cycles -= 275) {
254 random = ((random & 0x03f) << 11)
255 ^ ((random & 0x1c0) << 8)
256 ^ ((random & 0x1ff) << 5)
261 for (; cycles >= 68; cycles -= 68) {
262 random = ((random & 0xfff) << 5)
263 ^ ((random & 0xfff) << 2)
267 for (; cycles >= 8; cycles -= 8) {
268 random = ((random & 0xff) << 9)
269 ^ ((random & 0xff) << 6)
272 for (; cycles >= 1; cycles -= 1) {
273 random = ((random & 1) << 16)
274 ^ ((random & 1) << 13)
282 static bool checkAY8910(
const DeviceConfig& config)
284 auto type = config.getChildData(
"type",
"ay8910");
286 if (cmp(type,
"ay8910")) {
288 }
else if (cmp(type,
"ym2149")) {
291 throw FatalError(
"Unknown PSG type: ", type);
294 AY8910::Amplitude::Amplitude(
const DeviceConfig& config)
295 : isAY8910(checkAY8910(config))
297 vol[0] = vol[1] = vol[2] = 0.0f;
304 std::cout <<
"YM2149Envelope:";
306 std::cout <<
' ' << std::hexfloat <<
e;
308 std::cout <<
"\nAY8910Envelope:";
310 std::cout <<
' ' << std::hexfloat <<
e;
312 std::cout <<
"\nvolume:";
314 std::cout <<
' ' << std::hexfloat <<
e;
320 const float* AY8910::Amplitude::getEnvVolTable()
const
325 inline float AY8910::Amplitude::getVolume(
unsigned chan)
const
327 assert(!followsEnvelope(chan));
331 inline void AY8910::Amplitude::setChannelVolume(
unsigned chan,
unsigned value)
333 envChan[chan] = (value & 0x10) != 0;
337 inline bool AY8910::Amplitude::followsEnvelope(
unsigned chan)
const
339 return envChan[chan];
350 inline AY8910::Envelope::Envelope(
const float* envVolTable_)
351 : envVolTable(envVolTable_)
362 inline void AY8910::Envelope::reset()
367 inline void AY8910::Envelope::setPeriod(
int value)
375 inline float AY8910::Envelope::getVolume()
const
377 return envVolTable[
step ^ attack];
380 inline void AY8910::Envelope::setShape(
unsigned shape)
397 attack = (shape & 0x04) ? 0x1F : 0x00;
398 if ((shape & 0x08) == 0) {
402 alternate = attack != 0;
404 hold = (shape & 0x01) != 0;
405 alternate = (shape & 0x02) != 0;
412 inline bool AY8910::Envelope::isChanging()
const
417 inline void AY8910::Envelope::doSteps(
int steps)
431 if (alternate) attack ^= 0x1F;
437 if (alternate && (
step & 0x10)) {
447 assert(
count < period);
448 count += duration * 2;
449 if (
count >= period) {
450 int steps =
count / period;
451 count -= steps * period;
456 inline void AY8910::Envelope::doNextEvent()
459 doSteps(period == 1 ? 2 : 1);
462 inline unsigned AY8910::Envelope::getNextEventTime()
const
464 assert(
count < period);
465 return (period -
count + 1) / 2;
468 inline void AY8910::Envelope::advanceFast(
unsigned duration)
470 count += 2 * duration;
471 assert(
count < period);
481 , periphery(periphery_)
482 , debuggable(config.getMotherBoard(),
getName())
485 "controls strength of vibrato effect", 0.0, 0.0, 10.0)
488 "frequency of vibrato effect in Hertz", 5, 1.0, 10.0)
491 "controls strength of detune effect", 0.0, 0.0, 10.0)
494 "frequency of detune effect in Hertz", 5.0, 1.0, 100.0)
495 , directionsCallback(
496 config.getGlobalSettings().getInvalidPsgDirectionsSetting())
498 , envelope(amplitude.getEnvVolTable())
499 , isAY8910(checkAY8910(config))
500 , ignorePortDirections(config.getChildDataAsBool(
"ignorePortDirections", true))
501 , detuneInitialized(false)
503 update(vibratoPercent);
506 memset(regs, 0,
sizeof(regs));
512 vibratoPercent.
attach(*
this);
513 detunePercent .
attach(*
this);
518 vibratoPercent.
detach(*
this);
519 detunePercent .
detach(*
this);
527 for (
auto&
t : tone)
t.reset();
531 for (
auto reg :
xrange(16)) {
532 wrtReg(reg, 0, time);
539 if (reg >= 16)
return 255;
543 regs[reg] = periphery.
readA(time);
548 regs[reg] = periphery.
readB(time);
554 static constexpr
byte regMask[16] = {
555 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
556 0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
558 return isAY8910 ? regs[reg] & regMask[reg]
564 if (reg >= 16)
return 255;
568 return periphery.
readA(time);
573 return periphery.
readB(time);
583 if (reg >= 16)
return;
588 wrtReg(reg, value, time);
590 void AY8910::wrtReg(
unsigned reg,
byte value, EmuTime::param time)
597 if (ignorePortDirections)
606 byte diff = regs[reg] ^ value;
616 tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
627 noise.setPeriod(2 *
std::max(1, value & 0x1F));
632 amplitude.setChannelVolume(reg -
AY_AVOL, value);
641 envelope.setShape(value);
651 periphery.
writeA(0xff, time);
661 periphery.
writeB(0xff, time);
667 periphery.
writeA(value, time);
672 periphery.
writeB(value, time);
678 void AY8910::generateChannels(
float** bufs,
unsigned num)
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;
695 if ((chanEnable & 0x38) == 0x38) {
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];
715 ToneGenerator&
t = tone[chan];
716 if (envelope.isChanging() && amplitude.followsEnvelope(chan)) {
717 envelopeUpdated =
true;
718 envelope = initialEnvelope;
719 if ((chanEnable & 0x09) == 0x08) {
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)) {
730 envelope.advanceFast(nextT);
731 t.doNextEvent(*
this);
732 nextT =
t.getNextEventTime();
733 }
else if (nextE < nextT) {
737 t.advanceFast(nextE);
738 envelope.doNextEvent();
739 nextE = envelope.getNextEventTime();
741 assert(nextT == nextE);
744 t.doNextEvent(*
this);
745 nextT =
t.getNextEventTime();
746 envelope.doNextEvent();
747 nextE = envelope.getNextEventTime();
749 val =
t.getOutput() * envelope.getVolume();
754 t.advanceFast(remaining);
755 envelope.advanceFast(remaining);
758 }
else if ((chanEnable & 0x09) == 0x09) {
760 auto val = envelope.getVolume();
761 unsigned remaining = num;
762 unsigned next = envelope.getNextEventTime();
763 while (next <= remaining) {
766 envelope.doNextEvent();
767 val = envelope.getVolume();
768 next = envelope.getNextEventTime();
773 envelope.advanceFast(remaining);
777 }
else if ((chanEnable & 0x09) == 0x00) {
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();
786 while (next <= remaining) {
795 t.doNextEvent(*
this);
796 nextT =
t.getNextEventTime();
799 noise.advanceFast(next);
802 nextN = noise.getNextEventTime();
805 envelope.advanceFast(next);
807 envelope.doNextEvent();
808 nextE = envelope.getNextEventTime();
811 val = noise.getOutput() *
t.getOutput() * envelope.getVolume();
816 t.advanceFast(remaining);
817 noise.advanceFast(remaining);
818 envelope.advanceFast(remaining);
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)) {
833 envelope.advanceFast(nextN);
835 nextN = noise.getNextEventTime();
836 }
else if (nextE < nextN) {
840 noise.advanceFast(nextE);
841 envelope.doNextEvent();
842 nextE = envelope.getNextEventTime();
844 assert(nextN == nextE);
848 nextN = noise.getNextEventTime();
849 envelope.doNextEvent();
850 nextE = envelope.getNextEventTime();
852 val = noise.getOutput() * envelope.getVolume();
857 noise.advanceFast(remaining);
858 envelope.advanceFast(remaining);
864 auto volume = amplitude.followsEnvelope(chan)
865 ? envelope.getVolume()
866 : amplitude.getVolume(chan);
867 if ((chanEnable & 0x09) == 0x08) {
869 auto val =
t.getOutput() * volume;
870 unsigned remaining = num;
871 unsigned next =
t.getNextEventTime();
872 while (next <= remaining) {
876 t.doNextEvent(*
this);
877 next =
t.getNextEventTime();
882 t.advanceFast(remaining);
885 }
else if ((chanEnable & 0x09) == 0x09) {
890 }
else if ((chanEnable & 0x09) == 0x00) {
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)) {
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) {
912 t.advanceFast(nextN);
914 nextN = noise.getNextEventTime();
915 val2 = val1 * noise.getOutput();
917 assert(nextT == nextN);
920 t.doNextEvent(*
this);
921 nextT =
t.getNextEventTime();
923 nextN = noise.getNextEventTime();
924 val1 = volume - val1;
925 val2 = val1 * noise.getOutput();
931 t.advanceFast(remaining);
932 noise.advanceFast(remaining);
937 noise = initialNoise;
938 unsigned remaining = num;
939 auto val = noise.getOutput() * volume;
940 unsigned next = noise.getNextEventTime();
941 while (next <= remaining) {
945 val = noise.getOutput() * volume;
946 next = noise.getNextEventTime();
951 noise.advanceFast(remaining);
959 if (envelope.isChanging() && !envelopeUpdated) {
960 envelope.advance(num);
964 float AY8910::getAmplificationFactorImpl()
const
969 void AY8910::update(
const Setting&
setting) noexcept
972 doDetune = (vibratoPercent.getDouble() != 0) ||
973 (detunePercent .getDouble() != 0);
974 if (doDetune && !detuneInitialized) {
975 detuneInitialized =
true;
986 AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
const std::string& name_)
987 : SimpleDebuggable(motherBoard_, name_ +
" regs",
"PSG", 0x10)
991 byte AY8910::Debuggable::read(
unsigned address, EmuTime::param time)
994 return ay8910.readRegister(address, time);
997 void AY8910::Debuggable::write(
unsigned address,
byte value, EmuTime::param time)
1000 return ay8910.writeRegister(address, value, time);
1004 template<
typename Archive>
1007 ar.serialize(
"toneGenerators", tone,
1008 "noiseGenerator", noise,
1009 "envelope", envelope,
1013 if constexpr (Archive::IS_LOADER) {
1014 for (
auto i :
xrange(3)) {
1015 amplitude.setChannelVolume(i, regs[i +
AY_AVOL]);
1023 template<
typename Archive>
1026 ar.serialize(
"period", period,
1033 template<
typename Archive>
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);
1052 template<
typename Archive>
1055 ar.template serializeInlinedBase<Generator>(*
this, version);
1056 ar.serialize(
"random", random);
1060 template<
typename Archive>
1063 ar.serialize(
"period", period,
1068 "alternate", alternate,
1069 "holding", holding);
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.
void reset(EmuTime::param time)
byte readRegister(unsigned reg, EmuTime::param time)
byte peekRegister(unsigned reg, EmuTime::param time) const
void serialize(Archive &ar, unsigned version)
void writeRegister(unsigned reg, byte value, EmuTime::param time)
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)
void attach(Observer< T > &observer)
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
constexpr float cubicHermite(const float *y, float x)
constexpr double round(double x)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
std::string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
constexpr int PORT_B_DIRECTION
constexpr int PORT_A_DIRECTION
constexpr auto YM2149EnvelopeTab
constexpr auto AY8910EnvelopeTab
constexpr KeyMatrixPosition x
Keyboard bindings.
void serialize(Archive &ar, T &t, unsigned version)
constexpr int NATIVE_FREQ_INT
constexpr float NATIVE_FREQ_FLOAT
void advance(octet_iterator &it, distance_type n, octet_iterator end)
uint32_t next(octet_iterator &it, octet_iterator end)
#define OUTER(type, member)
auto & global_urng()
Return reference to a (shared) global random number generator.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
TemporaryString tmpStrCat(Ts &&... ts)
constexpr auto xrange(T e)