53 std::array<float, 32> result = {};
55 double factor = cstd::pow<5, 3>(0.5, 0.25);
56 for (
int i = 31; i > 0; --i) {
57 result[i] = float(out);
65 std::array<float, 32> result = {};
68 for (
int i = 2; i < 32; i += 2) {
75 std::array<float, 16> result = {};
77 for (
auto i :
xrange(1, 16)) {
86 static float noiseTab[256 + 3];
88 static void initDetune()
91 std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
93 for (
auto i :
xrange(256)) {
94 noiseTab[i] = distribution(generator);
96 noiseTab[256] = noiseTab[0];
97 noiseTab[257] = noiseTab[1];
98 noiseTab[258] = noiseTab[2];
100 static float noiseValue(
float x)
113 inline void AY8910::Generator::reset()
118 inline void AY8910::Generator::setPeriod(
int value)
131 inline unsigned AY8910::Generator::getNextEventTime()
const
133 assert(
count < period);
134 return period -
count;
137 inline void AY8910::Generator::advanceFast(
unsigned duration)
140 assert(
count < period);
146 AY8910::ToneGenerator::ToneGenerator()
151 inline void AY8910::ToneGenerator::reset()
157 int AY8910::ToneGenerator::getDetune(
AY8910& ay8910)
160 float vibPerc = ay8910.vibratoPercent.getDouble();
161 if (vibPerc != 0.0f) {
162 int vibratoPeriod = int(
164 float(ay8910.vibratoFrequency.getDouble()));
165 vibratoCount += period;
166 vibratoCount %= vibratoPeriod;
168 sinf((
float(2 *
M_PI) * vibratoCount) / vibratoPeriod)
169 * vibPerc * 0.01f * period);
171 float detunePerc = ay8910.detunePercent.getDouble();
172 if (detunePerc != 0.0f) {
174 float(ay8910.detuneFrequency.getDouble());
175 detuneCount += period;
176 float noiseIdx = detuneCount / detunePeriod;
177 float detuneNoise = noiseValue( noiseIdx)
178 + noiseValue(2.0f * noiseIdx) / 2.0f;
179 result += int(detuneNoise * detunePerc * 0.01f * period);
181 return std::min(result, period - 1);
186 assert(
count < period);
188 if (
count >= period) {
190 int cycles =
count / period;
191 count -= period * cycles;
192 output ^= cycles & 1;
196 inline void AY8910::ToneGenerator::doNextEvent(
AY8910& ay8910)
199 count = getDetune(ay8910);
209 AY8910::NoiseGenerator::NoiseGenerator()
214 inline void AY8910::NoiseGenerator::reset()
220 inline void AY8910::NoiseGenerator::doNextEvent()
235 random = (random >> 1) ^ ((random & 1) << 13) ^ ((random & 1) << 16);
240 assert(
count < period);
242 int cycles =
count / period;
243 count -= cycles * period;
249 for (; cycles >= 4585; cycles -= 4585) {
250 random = ((random & 0x1f) << 12)
251 ^ ((random & 0x1f) << 9)
255 for (; cycles >= 275; cycles -= 275) {
256 random = ((random & 0x03f) << 11)
257 ^ ((random & 0x1c0) << 8)
258 ^ ((random & 0x1ff) << 5)
263 for (; cycles >= 68; cycles -= 68) {
264 random = ((random & 0xfff) << 5)
265 ^ ((random & 0xfff) << 2)
269 for (; cycles >= 8; cycles -= 8) {
270 random = ((random & 0xff) << 9)
271 ^ ((random & 0xff) << 6)
274 for (; cycles >= 1; cycles -= 1) {
275 random = ((random & 1) << 16)
276 ^ ((random & 1) << 13)
284 static bool checkAY8910(
const DeviceConfig& config)
286 auto type = config.getChildData(
"type",
"ay8910");
288 if (cmp(type,
"ay8910")) {
290 }
else if (cmp(type,
"ym2149")) {
293 throw FatalError(
"Unknown PSG type: ", type);
296 AY8910::Amplitude::Amplitude(
const DeviceConfig& config)
297 : isAY8910(checkAY8910(config))
299 vol[0] = vol[1] = vol[2] = 0.0f;
306 std::cout <<
"YM2149Envelope:";
308 std::cout <<
' ' << std::hexfloat << e;
310 std::cout <<
"\nAY8910Envelope:";
312 std::cout <<
' ' << std::hexfloat << e;
314 std::cout <<
"\nvolume:";
316 std::cout <<
' ' << std::hexfloat << e;
322 const float* AY8910::Amplitude::getEnvVolTable()
const
327 inline float AY8910::Amplitude::getVolume(
unsigned chan)
const
329 assert(!followsEnvelope(chan));
333 inline void AY8910::Amplitude::setChannelVolume(
unsigned chan,
unsigned value)
335 envChan[chan] = (value & 0x10) != 0;
339 inline bool AY8910::Amplitude::followsEnvelope(
unsigned chan)
const
341 return envChan[chan];
352 inline AY8910::Envelope::Envelope(
const float* envVolTable_)
354 envVolTable = envVolTable_;
364 inline void AY8910::Envelope::reset()
369 inline void AY8910::Envelope::setPeriod(
int value)
377 inline float AY8910::Envelope::getVolume()
const
379 return envVolTable[
step ^ attack];
382 inline void AY8910::Envelope::setShape(
unsigned shape)
399 attack = (shape & 0x04) ? 0x1F : 0x00;
400 if ((shape & 0x08) == 0) {
404 alternate = attack != 0;
406 hold = (shape & 0x01) != 0;
407 alternate = (shape & 0x02) != 0;
414 inline bool AY8910::Envelope::isChanging()
const
419 inline void AY8910::Envelope::doSteps(
int steps)
433 if (alternate) attack ^= 0x1F;
439 if (alternate && (
step & 0x10)) {
449 assert(
count < period);
450 count += duration * 2;
451 if (
count >= period) {
452 int steps =
count / period;
453 count -= steps * period;
458 inline void AY8910::Envelope::doNextEvent()
461 doSteps(period == 1 ? 2 : 1);
464 inline unsigned AY8910::Envelope::getNextEventTime()
const
466 assert(
count < period);
467 return (period -
count + 1) / 2;
470 inline void AY8910::Envelope::advanceFast(
unsigned duration)
472 count += 2 * duration;
473 assert(
count < period);
483 , periphery(periphery_)
484 , debuggable(config.getMotherBoard(),
getName())
487 "controls strength of vibrato effect", 0.0, 0.0, 10.0)
490 "frequency of vibrato effect in Hertz", 5, 1.0, 10.0)
493 "controls strength of detune effect", 0.0, 0.0, 10.0)
496 "frequency of detune effect in Hertz", 5.0, 1.0, 100.0)
497 , directionsCallback(
498 config.getGlobalSettings().getInvalidPsgDirectionsSetting())
500 , envelope(amplitude.getEnvVolTable())
501 , isAY8910(checkAY8910(config))
502 , ignorePortDirections(config.getChildDataAsBool(
"ignorePortDirections", true))
505 detuneInitialized =
false;
506 update(vibratoPercent);
509 memset(regs, 0,
sizeof(regs));
515 vibratoPercent.
attach(*
this);
516 detunePercent .
attach(*
this);
521 vibratoPercent.
detach(*
this);
522 detunePercent .
detach(*
this);
530 for (
auto&
t : tone)
t.reset();
534 for (
auto reg :
xrange(16)) {
535 wrtReg(reg, 0, time);
542 if (reg >= 16)
return 255;
546 regs[reg] = periphery.
readA(time);
551 regs[reg] = periphery.
readB(time);
557 static constexpr
byte regMask[16] = {
558 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
559 0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
561 return isAY8910 ? regs[reg] & regMask[reg]
567 if (reg >= 16)
return 255;
571 return periphery.
readA(time);
576 return periphery.
readB(time);
586 if (reg >= 16)
return;
591 wrtReg(reg, value, time);
593 void AY8910::wrtReg(
unsigned reg,
byte value, EmuTime::param time)
600 if (ignorePortDirections)
609 byte diff = regs[reg] ^ value;
619 tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
630 noise.setPeriod(2 *
std::max(1, value & 0x1F));
635 amplitude.setChannelVolume(reg -
AY_AVOL, value);
644 envelope.setShape(value);
654 periphery.
writeA(0xff, time);
664 periphery.
writeB(0xff, time);
670 periphery.
writeA(value, time);
675 periphery.
writeB(value, time);
681 void AY8910::generateChannels(
float** bufs,
unsigned num)
686 for (
auto chan :
xrange(3)) {
687 if ((!amplitude.followsEnvelope(chan) &&
688 (amplitude.getVolume(chan) == 0.0f)) ||
689 (amplitude.followsEnvelope(chan) &&
690 !envelope.isChanging() &&
691 (envelope.getVolume() == 0.0f))) {
692 bufs[chan] =
nullptr;
693 tone[chan].advance(num);
694 chanEnable |= 0x09 << chan;
698 if ((chanEnable & 0x38) == 0x38) {
712 bool envelopeUpdated =
false;
713 Envelope initialEnvelope = envelope;
714 NoiseGenerator initialNoise = noise;
715 for (
unsigned chan = 0; chan < 3; ++chan, chanEnable >>= 1) {
716 auto* buf = bufs[chan];
718 ToneGenerator&
t = tone[chan];
719 if (envelope.isChanging() && amplitude.followsEnvelope(chan)) {
720 envelopeUpdated =
true;
721 envelope = initialEnvelope;
722 if ((chanEnable & 0x09) == 0x08) {
724 auto val =
t.getOutput() * envelope.getVolume();
725 unsigned remaining = num;
726 unsigned nextE = envelope.getNextEventTime();
727 unsigned nextT =
t.getNextEventTime();
728 while ((nextT <= remaining) || (nextE <= remaining)) {
733 envelope.advanceFast(nextT);
734 t.doNextEvent(*
this);
735 nextT =
t.getNextEventTime();
736 }
else if (nextE < nextT) {
740 t.advanceFast(nextE);
741 envelope.doNextEvent();
742 nextE = envelope.getNextEventTime();
744 assert(nextT == nextE);
747 t.doNextEvent(*
this);
748 nextT =
t.getNextEventTime();
749 envelope.doNextEvent();
750 nextE = envelope.getNextEventTime();
752 val =
t.getOutput() * envelope.getVolume();
757 t.advanceFast(remaining);
758 envelope.advanceFast(remaining);
761 }
else if ((chanEnable & 0x09) == 0x09) {
763 auto val = envelope.getVolume();
764 unsigned remaining = num;
765 unsigned next = envelope.getNextEventTime();
766 while (next <= remaining) {
769 envelope.doNextEvent();
770 val = envelope.getVolume();
771 next = envelope.getNextEventTime();
776 envelope.advanceFast(remaining);
780 }
else if ((chanEnable & 0x09) == 0x00) {
782 noise = initialNoise;
783 auto val = noise.getOutput() *
t.getOutput() * envelope.getVolume();
784 unsigned remaining = num;
785 unsigned nextT =
t.getNextEventTime();
786 unsigned nextN = noise.getNextEventTime();
787 unsigned nextE = envelope.getNextEventTime();
789 while (next <= remaining) {
798 t.doNextEvent(*
this);
799 nextT =
t.getNextEventTime();
802 noise.advanceFast(next);
805 nextN = noise.getNextEventTime();
808 envelope.advanceFast(next);
810 envelope.doNextEvent();
811 nextE = envelope.getNextEventTime();
814 val = noise.getOutput() *
t.getOutput() * envelope.getVolume();
819 t.advanceFast(remaining);
820 noise.advanceFast(remaining);
821 envelope.advanceFast(remaining);
826 noise = initialNoise;
827 auto val = noise.getOutput() * envelope.getVolume();
828 unsigned remaining = num;
829 unsigned nextE = envelope.getNextEventTime();
830 unsigned nextN = noise.getNextEventTime();
831 while ((nextN <= remaining) || (nextE <= remaining)) {
836 envelope.advanceFast(nextN);
838 nextN = noise.getNextEventTime();
839 }
else if (nextE < nextN) {
843 noise.advanceFast(nextE);
844 envelope.doNextEvent();
845 nextE = envelope.getNextEventTime();
847 assert(nextN == nextE);
851 nextN = noise.getNextEventTime();
852 envelope.doNextEvent();
853 nextE = envelope.getNextEventTime();
855 val = noise.getOutput() * envelope.getVolume();
860 noise.advanceFast(remaining);
861 envelope.advanceFast(remaining);
867 auto volume = amplitude.followsEnvelope(chan)
868 ? envelope.getVolume()
869 : amplitude.getVolume(chan);
870 if ((chanEnable & 0x09) == 0x08) {
872 auto val =
t.getOutput() * volume;
873 unsigned remaining = num;
874 unsigned next =
t.getNextEventTime();
875 while (next <= remaining) {
879 t.doNextEvent(*
this);
880 next =
t.getNextEventTime();
885 t.advanceFast(remaining);
888 }
else if ((chanEnable & 0x09) == 0x09) {
893 }
else if ((chanEnable & 0x09) == 0x00) {
895 noise = initialNoise;
896 auto val1 =
t.getOutput() * volume;
897 auto val2 = val1 * noise.getOutput();
898 unsigned remaining = num;
899 unsigned nextN = noise.getNextEventTime();
900 unsigned nextT =
t.getNextEventTime();
901 while ((nextN <= remaining) || (nextT <= remaining)) {
906 noise.advanceFast(nextT);
907 t.doNextEvent(*
this);
908 nextT =
t.getNextEventTime();
909 val1 = volume - val1;
910 val2 = val1 * noise.getOutput();
911 }
else if (nextN < nextT) {
915 t.advanceFast(nextN);
917 nextN = noise.getNextEventTime();
918 val2 = val1 * noise.getOutput();
920 assert(nextT == nextN);
923 t.doNextEvent(*
this);
924 nextT =
t.getNextEventTime();
926 nextN = noise.getNextEventTime();
927 val1 = volume - val1;
928 val2 = val1 * noise.getOutput();
934 t.advanceFast(remaining);
935 noise.advanceFast(remaining);
940 noise = initialNoise;
941 unsigned remaining = num;
942 auto val = noise.getOutput() * volume;
943 unsigned next = noise.getNextEventTime();
944 while (next <= remaining) {
948 val = noise.getOutput() * volume;
949 next = noise.getNextEventTime();
954 noise.advanceFast(remaining);
962 if (envelope.isChanging() && !envelopeUpdated) {
963 envelope.advance(num);
967 float AY8910::getAmplificationFactorImpl()
const
972 void AY8910::update(
const Setting& setting) noexcept
974 if (&setting ==
one_of(&vibratoPercent, &detunePercent)) {
975 doDetune = (vibratoPercent.getDouble() != 0) ||
976 (detunePercent .getDouble() != 0);
977 if (doDetune && !detuneInitialized) {
978 detuneInitialized =
true;
989 AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
const string& name_)
990 : SimpleDebuggable(motherBoard_, name_ +
" regs",
"PSG", 0x10)
994 byte AY8910::Debuggable::read(
unsigned address, EmuTime::param time)
997 return ay8910.readRegister(address, time);
1000 void AY8910::Debuggable::write(
unsigned address,
byte value, EmuTime::param time)
1003 return ay8910.writeRegister(address, value, time);
1007 template<
typename Archive>
1010 ar.serialize(
"toneGenerators", tone,
1011 "noiseGenerator", noise,
1012 "envelope", envelope,
1016 if (ar.isLoader()) {
1017 for (
auto i :
xrange(3)) {
1018 amplitude.setChannelVolume(i, regs[i +
AY_AVOL]);
1026 template<
typename Archive>
1029 ar.serialize(
"period", period,
1036 template<
typename Archive>
1039 ar.template serializeInlinedBase<Generator>(*
this, version);
1040 ar.serialize(
"vibratoCount", vibratoCount,
1041 "detuneCount", detuneCount);
1042 if (ar.versionAtLeast(version, 2)) {
1043 ar.serialize(
"output", output);
1055 template<
typename Archive>
1058 ar.template serializeInlinedBase<Generator>(*
this, version);
1059 ar.serialize(
"random", random);
1063 template<
typename Archive>
1066 ar.serialize(
"period", period,
1071 "alternate", alternate,
1072 "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)
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)