29 constexpr
unsigned MOD = 0;
30 constexpr
unsigned CAR = 1;
160 static constexpr
unsigned DB_POS(
int x)
167 static constexpr
unsigned DB_NEG(
int x)
184 std::array<unsigned, EG_MUTE> result = {};
186 auto log_eg_mute = cstd::log<6, 5>(
EG_MUTE);
188 result[i] = (
EG_MUTE - 1 -
EG_MUTE * cstd::log<6, 5>(i) / log_eg_mute) / 2;
189 assert(0 <=
int(result[i]));
195 std::array<unsigned, EG_MUTE + 1> result = {};
199 assert(0 <=
int(result[i]));
208 std::array<int, (2 *
DB_MUTE) * 2> result = {};
211 cstd::pow<7, 3>(10, -
double(i) *
DB_STEP / 20.0));
213 assert(result[
DB_MUTE - 1] == 0);
218 result[i + 2 *
DB_MUTE] = -result[i];
228 auto lin2db = [](
double d) {
232 int tmp = -int(20.0 * cstd::log10<6, 2>(d) /
DB_STEP);
239 std::array<unsigned, PG_WIDTH> result = {};
244 result[
PG_WIDTH / 2 - 1 - i] = result[i];
254 std::array<std::array<int, PM_PG_WIDTH>, 2> result = {};
266 constexpr
unsigned klTable[16] = {
267 0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56
271 constexpr
unsigned shift[4] = { 31, 1, 2, 0 };
273 std::array<std::array<int, 4>, 16 * 8> result = {};
274 for (
auto freq :
xrange(16 * 8u)) {
275 unsigned fnum = freq % 16;
276 unsigned block = freq / 16;
277 int tmp = 4 * klTable[fnum] - 32 * (7 - block);
278 for (
auto KL :
xrange(4)) {
279 result[freq][KL] = (tmp <= 0) ? 0 : (tmp >> shift[KL]);
287 std::array<std::array<Y8950::EnvPhaseIndex, 16>, 16> result = {};
288 for (
auto Rks :
xrange(16)) {
290 for (
auto AR :
xrange(1, 15)) {
291 unsigned RM =
std::min(AR + (Rks >> 2), 15);
292 unsigned RL = Rks & 3;
303 std::array<std::array<Y8950::EnvPhaseIndex, 16>, 16> result = {};
304 for (
auto Rks :
xrange(16)) {
306 for (
auto DR :
xrange(1, 16)) {
307 unsigned RM =
std::min(DR + (Rks >> 2), 15);
308 unsigned RL = Rks & 3;
319 Y8950::Patch::Patch()
324 void Y8950::Patch::reset()
336 setKeyScaleRate(
false);
343 void Y8950::Slot::reset()
358 void Y8950::Slot::updatePG(
unsigned freq)
360 static constexpr
int mlTable[16] = {
361 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2 , 7*2,
362 8*2, 9*2, 10*2, 10*2, 12*2, 12*2, 15*2, 15*2
365 unsigned fnum = freq % 1024;
366 unsigned block = freq / 1024;
370 void Y8950::Slot::updateTLL(
unsigned freq)
375 void Y8950::Slot::updateRKS(
unsigned freq)
377 unsigned rks = freq >> patch.KR;
383 void Y8950::Slot::updateEG()
387 eg_dPhase = dPhaseARTableRks[patch.AR];
390 eg_dPhase = dPhaseDRTableRks[patch.DR];
394 eg_dPhase = dPhaseDRTableRks[patch.RR];
402 void Y8950::Slot::updateAll(
unsigned freq)
410 bool Y8950::Slot::isActive()
const
416 void Y8950::Slot::slotOn(KeyPart part)
427 void Y8950::Slot::slotOff(KeyPart part)
443 Y8950::Channel::Channel()
448 void Y8950::Channel::reset()
457 void Y8950::Channel::setFreq(
unsigned freq_)
462 void Y8950::Channel::keyOn(KeyPart part)
464 slot[
MOD].slotOn(part);
465 slot[
CAR].slotOn(part);
468 void Y8950::Channel::keyOff(KeyPart part)
470 slot[
MOD].slotOff(part);
471 slot[
CAR].slotOff(part);
477 unsigned sampleRam, EmuTime::param time,
MSXAudio& audio)
478 :
ResampledSoundDevice(config.getMotherBoard(), name_,
"MSX-AUDIO", 9 + 5 + 1, INPUT_RATE, false)
479 , motherBoard(config.getMotherBoard())
480 , periphery(audio.createPeriphery(
getName()))
481 , adpcm(*this, config, name_, sampleRam)
482 , connector(motherBoard.getPluggingController())
483 , dac13(name_ +
" DAC",
"MSX-AUDIO 13-bit DAC", config)
484 , debuggable(motherBoard,
getName())
485 , timer1(
EmuTimer::createOPL3_1(motherBoard.getScheduler(), *this))
486 , timer2(
EmuTimer::createOPL3_2(motherBoard.getScheduler(), *this))
487 , irq(motherBoard,
getName() +
".IRQ")
494 std::cout <<
pmTable[0][i] <<
' '
505 for (
const auto&
e :
dB2LinTab) std::cout <<
e <<
'\n';
508 for (
auto i :
xrange(16 * 8)) {
509 for (
auto j :
xrange(4)) {
516 for (
const auto&
e :
sinTable) std::cout <<
e <<
'\n';
519 for (
auto i :
xrange(16)) {
520 for (
auto j :
xrange(16)) {
527 for (
auto i :
xrange(16)) {
528 for (
auto j :
xrange(16)) {
553 for (
auto& c : ch) c.reset();
581 void Y8950::keyOn_BD() { ch[6].keyOn(KEY_RHYTHM); }
582 void Y8950::keyOn_HH() { ch[7].slot[
MOD].slotOn(KEY_RHYTHM); }
583 void Y8950::keyOn_SD() { ch[7].slot[
CAR].slotOn(KEY_RHYTHM); }
584 void Y8950::keyOn_TOM() { ch[8].slot[
MOD].slotOn(KEY_RHYTHM); }
585 void Y8950::keyOn_CYM() { ch[8].slot[
CAR].slotOn(KEY_RHYTHM); }
588 void Y8950::keyOff_BD() { ch[6].keyOff(KEY_RHYTHM); }
589 void Y8950::keyOff_HH() { ch[7].slot[
MOD].slotOff(KEY_RHYTHM); }
590 void Y8950::keyOff_SD() { ch[7].slot[
CAR].slotOff(KEY_RHYTHM); }
591 void Y8950::keyOff_TOM(){ ch[8].slot[
MOD].slotOff(KEY_RHYTHM); }
592 void Y8950::keyOff_CYM(){ ch[8].slot[
CAR].slotOff(KEY_RHYTHM); }
595 void Y8950::setRythmMode(
int data)
597 bool newMode = (data & 32) != 0;
598 if (rythm_mode != newMode) {
599 rythm_mode = newMode;
612 void Y8950::update_key_status()
615 int main = (reg[0xb0 + i] & 0x20) ? KEY_MAIN : 0;
620 ch[6].slot[
MOD].key |= (reg[0xbd] & 0x10) ? KEY_RHYTHM : 0;
621 ch[6].slot[
CAR].key |= (reg[0xbd] & 0x10) ? KEY_RHYTHM : 0;
622 ch[7].slot[
MOD].key |= (reg[0xbd] & 0x01) ? KEY_RHYTHM : 0;
623 ch[7].slot[
CAR].key |= (reg[0xbd] & 0x08) ? KEY_RHYTHM : 0;
624 ch[8].slot[
MOD].key |= (reg[0xbd] & 0x04) ? KEY_RHYTHM : 0;
625 ch[8].slot[
CAR].key |= (reg[0xbd] & 0x02) ? KEY_RHYTHM : 0;
635 static constexpr
int wave2_8pi(
int e)
638 return (shift > 0) ? (
e >> shift) : (
e << -shift);
641 unsigned Y8950::Slot::calc_phase(
int lfo_pm)
651 static constexpr
auto S2E(
int x) {
655 S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21),
656 S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93)
658 unsigned Y8950::Slot::calc_envelope(
int lfo_am)
663 eg_phase += eg_dPhase;
675 eg_phase += eg_dPhase;
676 if (eg_phase >=
SL[patch.SL]) {
677 eg_phase =
SL[patch.SL];
681 egOut = eg_phase.toInt();
686 eg_phase += eg_dPhase;
688 egOut = eg_phase.toInt();
697 eg_phase += eg_dPhase;
698 egOut = eg_phase.toInt();
715 return std::min<unsigned>(egOut,
DB_MUTE - 1);
718 int Y8950::Slot::calc_slot_car(
int lfo_pm,
int lfo_am,
int fm)
720 unsigned egOut = calc_envelope(lfo_am);
721 int pgout = calc_phase(lfo_pm) + wave2_8pi(fm);
725 int Y8950::Slot::calc_slot_mod(
int lfo_pm,
int lfo_am)
727 unsigned egOut = calc_envelope(lfo_am);
728 unsigned pgout = calc_phase(lfo_pm);
731 pgout += wave2_8pi(feedback) >> patch.FB;
734 feedback = (output + newOutput) >> 1;
739 int Y8950::Slot::calc_slot_tom(
int lfo_pm,
int lfo_am)
741 unsigned egOut = calc_envelope(lfo_am);
742 unsigned pgout = calc_phase(lfo_pm);
746 int Y8950::Slot::calc_slot_snare(
int lfo_pm,
int lfo_am,
int whitenoise)
748 unsigned egOut = calc_envelope(lfo_am);
749 unsigned pgout = calc_phase(lfo_pm);
754 int Y8950::Slot::calc_slot_cym(
int lfo_am,
int a,
int b)
756 unsigned egOut = calc_envelope(lfo_am);
761 int Y8950::Slot::calc_slot_hat(
int lfo_am,
int a,
int b,
int whitenoise)
763 unsigned egOut = calc_envelope(lfo_am);
769 float Y8950::getAmplificationFactorImpl()
const
780 bool Y8950::checkMuteHelper()
785 for (
auto i :
xrange(6)) {
786 if (ch[i].slot[
CAR].isActive())
return false;
789 for (
auto i :
xrange(6, 9)) {
790 if (ch[i].slot[
CAR].isActive())
return false;
793 if (ch[6].slot[
CAR].isActive())
return false;
794 if (ch[7].slot[
MOD].isActive())
return false;
795 if (ch[7].slot[
CAR].isActive())
return false;
796 if (ch[8].slot[
MOD].isActive())
return false;
797 if (ch[8].slot[
CAR].isActive())
return false;
803 void Y8950::generateChannels(
float** bufs,
unsigned num)
806 if (checkMuteHelper()) {
810 std::fill_n(bufs, 9 + 5 + 1,
nullptr);
814 for (
auto sample :
xrange(num)) {
822 int lfo_am = am_mode ? tmp : tmp / 4;
827 if (noise_seed & 1) {
828 noise_seed ^= 0x24000;
831 int whitenoise = noise_seed & 1 ? DB_POS(6) : DB_NEG(6);
833 noiseA_phase += noiseA_dPhase;
834 noiseA_phase &= (0x40 << 11) - 1;
835 if ((noiseA_phase >> 11) == 0x3f) {
838 int noiseA = noiseA_phase & (0x03 << 11) ? DB_POS(6) : DB_NEG(6);
840 noiseB_phase += noiseB_dPhase;
841 noiseB_phase &= (0x10 << 11) - 1;
842 int noiseB = noiseB_phase & (0x0A << 11) ? DB_POS(6) : DB_NEG(6);
844 for (
auto i :
xrange(rythm_mode ? 6 : 9)) {
845 if (ch[i].slot[
CAR].isActive()) {
846 bufs[i][sample] += ch[i].alg
847 ? ch[i].slot[
CAR].calc_slot_car(lfo_pm, lfo_am, 0) +
848 ch[i].slot[
MOD].calc_slot_mod(lfo_pm, lfo_am)
849 : ch[i].slot[
CAR].calc_slot_car(lfo_pm, lfo_am,
850 ch[i].slot[
MOD].calc_slot_mod(lfo_pm, lfo_am));
861 (void)ch[7].slot[
MOD].calc_phase(lfo_pm);
862 (void)ch[8].slot[
CAR].calc_phase(lfo_pm);
864 bufs[ 9][sample] += (ch[6].slot[
CAR].isActive())
865 ? 2 * ch[6].slot[
CAR].calc_slot_car(lfo_pm, lfo_am,
866 ch[6].slot[
MOD].calc_slot_mod(lfo_pm, lfo_am))
868 bufs[10][sample] += (ch[7].slot[
CAR].isActive())
869 ? 2 * ch[7].slot[
CAR].calc_slot_snare(lfo_pm, lfo_am, whitenoise)
871 bufs[11][sample] += (ch[8].slot[
CAR].isActive())
872 ? 2 * ch[8].slot[
CAR].calc_slot_cym(lfo_am, noiseA, noiseB)
874 bufs[12][sample] += (ch[7].slot[
MOD].isActive())
875 ? 2 * ch[7].slot[
MOD].calc_slot_hat(lfo_am, noiseA, noiseB, whitenoise)
877 bufs[13][sample] += (ch[8].slot[
MOD].isActive())
878 ? 2 * ch[8].slot[
MOD].calc_slot_tom(lfo_pm, lfo_am)
899 0, 2, 4, 1, 3, 5, -1, -1,
900 6, 8, 10, 7, 9, 11, -1, -1,
901 12, 14, 16, 13, 15, 17, -1, -1,
902 -1, -1, -1, -1, -1, -1, -1, -1
947 timer1->setValue(data);
952 timer2->setValue(data);
960 changeStatusMask((~data) & 0x78);
969 connector.
write(data, time);
974 periphery.
setSPOFF((data & 8) != 0, time);
995 if (reg[0x08] & 0x04) {
996 int tmp =
static_cast<signed char>(reg[0x15]) * 256
998 tmp = (tmp * 4) >> (7 - reg[0x17]);
1004 reg[rg] = data & 0xC0;
1007 reg[rg] = data & 0x07;
1014 periphery.
write(reg[0x18], reg[0x19], time);
1019 periphery.
write(reg[0x18], reg[0x19], time);
1025 int s = sTbl[rg & 0x1f];
1027 auto& chan = ch[s / 2];
1028 auto& slot = chan.slot[s & 1];
1029 slot.patch.AM = (data >> 7) & 1;
1030 slot.patch.PM = (data >> 6) & 1;
1031 slot.patch.EG = (data >> 5) & 1;
1032 slot.patch.setKeyScaleRate((data & 0x10) != 0);
1033 slot.patch.ML = (data >> 0) & 15;
1034 slot.updateAll(chan.freq);
1040 int s = sTbl[rg & 0x1f];
1042 auto& chan = ch[s / 2];
1043 auto& slot = chan.slot[s & 1];
1044 slot.patch.KL = (data >> 6) & 3;
1045 slot.patch.TL = (data >> 0) & 63;
1046 slot.updateAll(chan.freq);
1052 int s = sTbl[rg & 0x1f];
1054 auto& slot = ch[s / 2].slot[s & 1];
1055 slot.patch.AR = (data >> 4) & 15;
1056 slot.patch.DR = (data >> 0) & 15;
1063 int s = sTbl[rg & 0x1f];
1065 auto& slot = ch[s / 2].slot[s & 1];
1066 slot.patch.SL = (data >> 4) & 15;
1067 slot.patch.RR = (data >> 0) & 15;
1075 am_mode = (data & 0x80) != 0;
1076 pm_mode = (data & 0x40) != 0;
1080 if (data & 0x10) keyOn_BD();
else keyOff_BD();
1081 if (data & 0x08) keyOn_SD();
else keyOff_SD();
1082 if (data & 0x04) keyOn_TOM();
else keyOff_TOM();
1083 if (data & 0x02) keyOn_CYM();
else keyOff_CYM();
1084 if (data & 0x01) keyOn_HH();
else keyOff_HH();
1086 ch[6].slot[
MOD].updateAll(ch[6].freq);
1087 ch[6].slot[
CAR].updateAll(ch[6].freq);
1088 ch[7].slot[
MOD].updateAll(ch[7].freq);
1089 ch[7].slot[
CAR].updateAll(ch[7].freq);
1090 ch[8].slot[
MOD].updateAll(ch[8].freq);
1091 ch[8].slot[
CAR].updateAll(ch[8].freq);
1096 unsigned c = rg & 0x0f;
1101 unsigned freq = [&] {
1104 return data | ((reg[rg + 0x10] & 0x1F) << 8);
1108 ch[c].keyOn (KEY_MAIN);
1110 ch[c].keyOff(KEY_MAIN);
1112 return reg[rg - 0x10] | ((data & 0x1F) << 8);
1115 ch[c].setFreq(freq);
1116 unsigned fNum = freq % 1024;
1117 unsigned block = freq / 1024;
1119 case 7: noiseA_dPhase = fNum << block;
1121 case 8: noiseB_dPhase = fNum << block;
1124 ch[c].slot[
CAR].updateAll(freq);
1125 ch[c].slot[
MOD].updateAll(freq);
1133 ch[c].slot[
MOD].patch.setFeedbackShift((data >> 1) & 7);
1134 ch[c].alg = data & 1;
1149 return adpcm.
readReg(rg, time);
1159 return connector.
peek(time);
1165 return adpcm.
peekReg(rg, time);
1168 byte input = periphery.
read(time);
1169 byte output = reg[0x19];
1170 byte enable = reg[0x18];
1171 return (output & enable) | (input & ~enable) | 0xF0;
1188 return (status & (0x87 | statusMask)) | 0x06;
1191 void Y8950::callback(
byte flag)
1199 if (status & statusMask) {
1207 if (!(status & statusMask)) {
1216 void Y8950::changeStatusMask(
byte newMask)
1218 statusMask = newMask;
1219 status &= 0x87 | statusMask;
1220 if (status & statusMask) {
1230 template<
typename Archive>
1233 ar.serialize(
"AM", AM,
1247 static constexpr std::initializer_list<enum_string<Y8950::EnvelopeState>> envelopeStateInfo = {
1261 template<
typename Archive>
1264 ar.serialize(
"feedback", feedback,
1267 "eg_phase", eg_phase,
1269 if (ar.versionAtLeast(version, 3)) {
1270 ar.serialize(
"eg_mode", eg_mode);
1272 assert(Archive::IS_LOADER);
1274 ar.serialize(
"eg_mode", tmp);
1276 case 0: eg_mode =
ATTACK;
break;
1277 case 1: eg_mode =
DECAY;
break;
1278 case 2: eg_mode =
SUSTAIN;
break;
1279 case 3: eg_mode =
SUSTAIN;
break;
1280 case 4: eg_mode =
RELEASE;
break;
1281 default: eg_mode =
FINISH;
break;
1291 template<
typename Archive>
1294 ar.serialize(
"mod", slot[
MOD],
1299 if constexpr (Archive::IS_LOADER) {
1300 slot[
MOD].updateAll(freq);
1301 slot[
CAR].updateAll(freq);
1305 template<
typename Archive>
1308 ar.serialize(
"keyboardConnector", connector,
1313 ar.serialize_blob(
"registers", reg,
sizeof(reg));
1314 ar.serialize(
"pm_phase", pm_phase,
1315 "am_phase", am_phase,
1316 "noise_seed", noise_seed,
1317 "noiseA_phase", noiseA_phase,
1318 "noiseB_phase", noiseB_phase,
1319 "noiseA_dphase", noiseA_dPhase,
1320 "noiseB_dphase", noiseB_dPhase,
1323 "statusMask", statusMask,
1324 "rythm_mode", rythm_mode,
1327 "enabled", enabled);
1329 if constexpr (Archive::IS_LOADER) {
1331 static constexpr
byte rewriteRegs[] = {
1336 update_key_status();
1338 for (
auto r : rewriteRegs) {
1348 const std::string& name_)
1353 byte Y8950::Debuggable::read(
unsigned address, EmuTime::param time)
1356 return y8950.peekReg(address, time);
1359 void Y8950::Debuggable::write(
unsigned address,
byte value, EmuTime::param time)
1362 y8950.writeReg(address, value, time);
void writeDAC(int16_t value, EmuTime::param time)
void set()
Set the interrupt request on the bus.
void reset()
Reset the interrupt request on the bus.
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
void updateStream(EmuTime::param time)
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
byte readReg(byte rg, EmuTime::param time)
byte peekReg(byte rg, EmuTime::param time) const
void writeReg(byte rg, byte data, EmuTime::param time)
void reset(EmuTime::param time)
void write(byte data, EmuTime::param time)
byte peek(EmuTime::param time) const
virtual void write(nibble outputs, nibble values, EmuTime::param time)=0
Write to (some of) the pins.
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
virtual nibble read(EmuTime::param time)=0
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
void reset(EmuTime::param time)
byte peekRawStatus() const
byte readStatus(EmuTime::param time) const
void setEnabled(bool enabled, EmuTime::param time)
static constexpr int CLOCK_FREQ_DIV
Y8950(const std::string &name, const DeviceConfig &config, unsigned sampleRam, EmuTime::param time, MSXAudio &audio)
static constexpr int R04_IRQ_RESET
FixedPoint< EG_DP_BITS - EG_BITS > EnvPhaseIndex
byte peekReg(byte rg, EmuTime::param time) const
static constexpr int R04_ST2
void serialize(Archive &ar, unsigned version)
void resetStatus(byte flags)
static constexpr int R04_ST1
void writeReg(byte rg, byte data, EmuTime::param time)
static constexpr int CLOCK_FREQ
static constexpr int EG_BITS
byte readReg(byte rg, EmuTime::param time)
void setStatus(byte flags)
byte peekStatus(EmuTime::param time) const
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
int main(int argc, char **argv)
int16_t clipIntToShort(int x)
Clip x to range [-32768,32767].
constexpr double round(double x)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
std::string getName(KeyCode keyCode)
Translate key code to key name.
constexpr uint8_t mlTable[16]
This file implemented 3 utility functions:
constexpr Y8950::EnvPhaseIndex EG_DP_MAX
constexpr unsigned TL_PER_EG
constexpr unsigned LFO_AM_TAB_ELEMENTS
constexpr int PM_PG_WIDTH
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
constexpr int DP_BASE_BITS
constexpr double PM_DEPTH2
constexpr int AM_PG_WIDTH
constexpr Y8950::EnvPhaseIndex SL[16]
constexpr unsigned PM_DPHASE
constexpr unsigned EG_PER_DB
constexpr double PM_DEPTH
constexpr unsigned SL_PER_EG
constexpr int SLOT_AMP_BITS
constexpr int PM_DP_WIDTH
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
constexpr KeyMatrixPosition x
Keyboard bindings.
void serialize(Archive &ar, T &t, unsigned version)
constexpr int DB2LIN_AMP_BITS
constexpr auto dPhaseArTable
constexpr int AM_DP_WIDTH
constexpr double PM_SPEED
constexpr int PM_AMP_BITS
constexpr byte lfo_am_table[LFO_AM_TAB_ELEMENTS]
constexpr unsigned EG_MUTE
constexpr auto dPhaseDrTable
constexpr void fill(ForwardRange &&range, const T &value)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)