Lynx: Add a chip config for constant tone frequency

This commit is contained in:
Natt Akuma 2024-01-30 12:17:24 +07:00 committed by tildearrow
parent ec10046964
commit 463c9a89bb
3 changed files with 73 additions and 11 deletions

View file

@ -34,7 +34,6 @@
#define WRITE_ATTEN(ch,v) rWrite((0x40+ch),(v))
#define WRITE_STEREO(v) rWrite(0x50,(v))
#define CHIP_DIVIDER 64
#define CHIP_FREQBASE 16000000
static int32_t clamp(int32_t v, int32_t lo, int32_t hi)
@ -42,6 +41,41 @@ static int32_t clamp(int32_t v, int32_t lo, int32_t hi)
return v<lo?lo:(v>hi?hi:v);
}
const int DUTY_DIVIDERS[]={
1, 2, 4, 3, 6, 7, 7, 4, 8, 15, 6, 14, 15, 12, 14, 5,
10, 21, 31, 28, 31, 30, 12, 31, 21, 8, 30, 31, 28, 31, 31, 6,
12, 63, 14, 62, 9, 28, 62, 15, 14, 42, 8, 21, 62, 63, 15, 60,
63, 20, 42, 63, 28, 12, 63, 62, 62, 63, 21, 24, 15, 62, 60, 7,
16, 63, 30, 254, 217, 84, 186, 217, 12, 254, 28, 15, 254, 51, 255, 84,
217, 120, 210, 63, 60, 255, 255, 254, 254, 217, 63, 252, 17, 42, 124, 85,
30, 254, 24, 217, 210, 21, 255, 252, 28, 217, 10, 186, 63, 124, 254, 255,
186, 255, 255, 56, 255, 70, 36, 30, 255, 60, 254, 85, 124, 85, 21, 254,
22,1533,2047, 868,1953,2046, 420, 651,1533,2044,2046,2047,1016,1785, 105, 126,
595, 630, 204,1533,1302,2047,2047,2044,2044, 119, 372,1778,1953, 120, 682,1905,
595, 868, 510,2047,2044, 762, 279, 682, 210,1953, 595,1524,1533, 210, 248, 635,
60,2047,2047, 62,1905,2044,2046, 42,2047, 510, 252,1905,1778,2047,1533,1016,
1953,1860,1778, 219, 48,1905,2047,1778, 682,2047, 465,1020,1785, 126,2044, 434,
2044, 63,2047,2046,2047, 280, 30,1953, 210, 682, 868, 89,2046,2047,2047,1524,
1302,1785,2047,1860,2047,2046,1016, 372,2047, 84, 630, 70, 252,2047,1533, 682,
1905,2046, 372,1905, 90,1533, 217, 168, 340,2047,2047,1302,1533, 28, 186,2047,
24,3255, 126,1190, 45,4092, 178, 315, 28, 438, 124, 315,3570, 255,1023,2044,
819,1016, 930,3937,1260,1302, 511,4094,3810, 819, 195,2604,1023,4094, 84,1365,
18,3810, 56,1023, 42,3937, 819,4092, 124,4095, 30,4094, 85,1524,3906, 63,
3906,1023,3255, 120,4095,3570,1020,3937,2667,3556,4094, 195,2044,4095,4095, 762,
28,1302, 84,4095,3906,1023, 255, 292, 16,1085, 42,3066, 315,3556,4094,4095,
3066,4095,3937, 372, 511,4094, 620,1302, 273, 504,1190, 819,4092,4095,1023, 210,
124,1023, 126,3570,3937, 252, 438,4095, 30,4094, 120,4095,4094, 255, 63,1020,
4095,1364, 558,2667, 420,4095, 105,1270,1190,3255, 93,1016, 91, 372,4092,3937,
3255, 44,3066,1365,1736, 510, 91,4094,1302, 511,3937, 420, 105,3906,4092, 819,
252, 585, 255, 210,3937,1016,3570,1023, 455,3066,2044,1365,4094, 126,2667,4092,
3810, 93, 63,1364,1365,3906, 120, 28,1023,2044, 238,4095,3556, 255,4095, 372,
511,1190,1260,1023,3066, 255, 819, 408,2044, 255,1302,4094, 455,2604,4094, 585,
438, 511, 455, 292, 455, 434,1364, 102,1085, 204,3570, 255, 240,4095, 93,4094,
3937,4094, 84, 63,4094, 315, 819, 140,1260,3937,4095,3066,2667, 680,4094, 511,
4095,2044, 178,1023,1020, 819, 21,3810,4094, 20,1023,3556,3937, 210,2040, 273,
252,2667,4095,3906, 63, 124, 930, 455, 510,4094,4092, 511,3570,4095, 30, 744
};
const char* regCheatSheetLynx[]={
"AUDIO0_VOLCNTRL", "20",
"AUDIO0_FEEDBACK", "21",
@ -134,6 +168,7 @@ void DivPlatformLynx::tick(bool sysTick) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
double CHIP_DIVIDER=tuned?DUTY_DIVIDERS[chan[i].duty.val&0x1ff]*8:64;
chan[i].actualNote=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(chan[i].actualNote);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].actualNote);
@ -194,28 +229,35 @@ void DivPlatformLynx::tick(bool sysTick) {
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
chan[i].lfsr=-1;
}
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
if (!chan[i].pcm) {
WRITE_FEEDBACK(i, chan[i].duty.feedback);
}
}
double divider=tuned?DUTY_DIVIDERS[chan[i].duty.val&0x1ff]*8:64;
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,divider);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
WRITE_BACKUP( i, chan[i].fd.backup );
}
chan[i].freqChanged=false;
} else if (chan[i].std.duty.had) {
chan[i].duty = chan[i].std.duty.val;
chan[i].duty=chan[i].std.duty.val;
if (!chan[i].pcm) {
if (tuned) {
double divider=DUTY_DIVIDERS[chan[i].duty.val&0x1ff]*8;
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,divider);
}
WRITE_FEEDBACK(i, chan[i].duty.feedback);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
if (tuned) WRITE_BACKUP( i, chan[i].fd.backup );
}
}
}
}
int DivPlatformLynx::dispatch(DivCommand c) {
double CHIP_DIVIDER=tuned?DUTY_DIVIDERS[chan[c.chan].duty.val&0x1ff]*8:64;
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
bool prevPCM=chan[c.chan].pcm;
@ -455,6 +497,16 @@ bool DivPlatformLynx::getLegacyAlwaysSetVolume() {
// return 12;
//}
void DivPlatformLynx::setFlags(const DivConfig& flags) {
tuned=flags.getBool("tuned",false);
chipClock=16000000;
CHECK_CUSTOM_CLOCK;
rate=chipClock/128;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformLynx::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
@ -478,14 +530,7 @@ int DivPlatformLynx::init(DivEngine* p, int channels, int sugRate, const DivConf
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
chipClock = 16000000;
CHECK_CUSTOM_CLOCK;
rate = chipClock/128;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
setFlags(flags);
reset();
return 4;
@ -533,6 +578,7 @@ DivPlatformLynx::MikeyDuty::MikeyDuty(int duty) {
//1: f1
//0: f0
val=duty;
//f7 moved to bit 7 and int moved to bit 5
int_feedback7=((duty&0x40)<<1)|((duty&0x200)>>4);
//f11 and f10 moved to bits 7 & 6

View file

@ -35,6 +35,7 @@ class DivPlatformLynx: public DivDispatch {
struct MikeyDuty {
unsigned char int_feedback7;
unsigned char feedback;
int val;
MikeyDuty(int duty);
};
@ -64,6 +65,7 @@ class DivPlatformLynx: public DivDispatch {
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
bool tuned;
std::unique_ptr<Lynx::Mikey> mikey;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
@ -86,6 +88,7 @@ class DivPlatformLynx: public DivDispatch {
bool keyOffAffectsPorta(int ch);
bool getLegacyAlwaysSetVolume();
//int getPortaFloor(int ch);
void setFlags(const DivConfig& flags);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -1596,6 +1596,19 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
}
break;
}
case DIV_SYSTEM_LYNX: {
bool tuned=flags.getBool("tuned",false);
if (ImGui::Checkbox("Consistent frequency across all duties",&tuned)) {
altered=true;
e->lockSave([&]() {
flags.set("tuned",tuned);
});
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("note: only works for an initial LFSR value of 0!");
}
break;
}
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL_DRUMS:
case DIV_SYSTEM_OPL2: