GUI: better DPCM mapping, part 2

This commit is contained in:
tildearrow 2023-10-13 19:57:36 -05:00
parent e51ca07acb
commit 10172e0489
11 changed files with 180 additions and 16 deletions

View file

@ -184,6 +184,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.snNoLowPeriods=true;
ds.disableSampleMacro=true;
ds.preNoteNoEffect=true;
ds.oldDPCM=true;
ds.delayBehavior=0;
ds.jumpTreatment=2;
@ -1856,6 +1857,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<168) {
ds.preNoteNoEffect=true;
}
if (ds.version<183) {
ds.oldDPCM=true;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -2374,7 +2378,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<5; i++) {
if (ds.version>=183) {
ds.oldDPCM=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<4; i++) {
reader.readC();
}
}
@ -5438,7 +5447,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
w->writeC(song.brokenPortaLegato);
w->writeC(song.brokenFMOff);
w->writeC(song.preNoteNoEffect);
for (int i=0; i<5; i++) {
w->writeC(song.oldDPCM);
for (int i=0; i<4; i++) {
w->writeC(0);
}

View file

@ -486,8 +486,8 @@ struct DivInstrumentAmiga {
}
/**
* get the sample frequency at specified note.
* @return the frequency, or -1 if not using note map.
* get the sample playback note at specified note.
* @return the note, or -1 if not using note map.
*/
inline int getFreq(int note) {
if (useNoteMap) {
@ -498,6 +498,32 @@ struct DivInstrumentAmiga {
return note;
}
/**
* get the DPCM pitch at specified note.
* @return the pitch, or -1 if not using note map.
*/
inline signed char getDPCMFreq(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].dpcmFreq;
}
return -1;
}
/**
* get the DPCM delta counter value at specified note.
* @return the delta counter value, or -1 if not using note map.
*/
inline signed char getDPCMDelta(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].dpcmDelta;
}
return -1;
}
DivInstrumentAmiga():
initSample(0),
useNoteMap(false),

View file

@ -343,6 +343,10 @@ void DivPlatformNES::tick(bool sysTick) {
} else {
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
}
if (nextDPCMDelta>=0) {
rWrite(0x4011,nextDPCMDelta);
nextDPCMDelta=-1;
}
rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31);
@ -373,11 +377,38 @@ int DivPlatformNES::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.chan==4) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_NES);
if (ins->type==DIV_INS_AMIGA || (ins->type==DIV_INS_NES && !parent->song.oldDPCM)) {
if (ins->type==DIV_INS_NES) {
if (!dpcmMode) {
dpcmMode=true;
if (dumpWrites) addWrite(0xffff0002,0);
dacSample=-1;
rWrite(0x4015,15);
rWrite(0x4010,0);
rWrite(0x4012,0);
rWrite(0x4013,0);
rWrite(0x4015,31);
}
if (ins->amiga.useNoteMap) {
nextDPCMFreq=ins->amiga.getDPCMFreq(c.value);
if (nextDPCMFreq<0 || nextDPCMFreq>15) nextDPCMFreq=lastDPCMFreq;
lastDPCMFreq=nextDPCMFreq;
nextDPCMDelta=ins->amiga.getDPCMDelta(c.value);
} else {
if (c.value==DIV_NOTE_NULL) {
nextDPCMFreq=lastDPCMFreq;
} else {
nextDPCMFreq=c.value&15;
}
}
}
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
if (ins->type==DIV_INS_AMIGA) {
c.value=ins->amiga.getFreq(c.value);
}
}
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
@ -452,7 +483,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -614,7 +645,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
@ -700,6 +731,8 @@ void DivPlatformNES::reset() {
goingToLoop=false;
countMode=false;
nextDPCMFreq=-1;
nextDPCMDelta=-1;
lastDPCMFreq=15;
linearCount=255;
if (useNP) {

View file

@ -54,6 +54,8 @@ class DivPlatformNES: public DivDispatch {
unsigned char apuType;
unsigned char linearCount;
signed char nextDPCMFreq;
signed char nextDPCMDelta;
signed char lastDPCMFreq;
bool dpcmMode;
bool dpcmModeDefault;
bool dacAntiClickOn;

View file

@ -377,6 +377,7 @@ struct DivSong {
bool brokenPortaLegato;
bool brokenFMOff;
bool preNoteNoEffect;
bool oldDPCM;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -496,7 +497,8 @@ struct DivSong {
patchbayAuto(true),
brokenPortaLegato(false),
brokenFMOff(false),
preNoteNoEffect(false) {
preNoteNoEffect(false),
oldDPCM(false) {
for (int i=0; i<DIV_MAX_CHIPS; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=1.0;

View file

@ -188,6 +188,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6pre9");
}
ImGui::Checkbox("Disable new NES DPCM features",&e->song.oldDPCM);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6.1");
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(".mod import")) {

View file

@ -2313,6 +2313,16 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
if (ins->type==DIV_INS_SU) sampleTabName="Sound Unit";
if (ins->type==DIV_INS_NES) sampleTabName="DPCM";
if (ImGui::BeginTabItem(sampleTabName)) {
if (ins->type==DIV_INS_NES && e->song.oldDPCM) {
ImGui::Text("new DPCM features disabled (compatibility)!");
if (ImGui::Button("click here to enable them.")) {
e->song.oldDPCM=false;
MARK_MODIFIED;
}
ImGui::EndTabItem();
return;
}
String sName;
bool wannaOpenSMPopup=false;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@ -2401,7 +2411,7 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
ImGui::Text("#");
if (ins->type==DIV_INS_NES) {
ImGui::TableNextColumn();
ImGui::Text("freq");
ImGui::Text("pitch");
ImGui::TableNextColumn();
ImGui::Text("delta");
} else {
@ -2674,9 +2684,21 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
if (ImGui::BeginPopup("SampleMapUtils",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
if (sampleMapSelStart==sampleMapSelEnd && sampleMapSelStart>=0 && sampleMapSelStart<120) {
if (ins->type==DIV_INS_NES) {
if (ImGui::MenuItem("set entire map to this frequency")) {
if (ImGui::MenuItem("set entire map to this pitch")) {
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
for (int i=0; i<120; i++) {
if (i==sampleMapSelStart) continue;
ins->amiga.noteMap[i].dpcmFreq=ins->amiga.noteMap[sampleMapSelStart].dpcmFreq;
}
}
}
if (ImGui::MenuItem("set entire map to this delta counter value")) {
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
for (int i=0; i<120; i++) {
if (i==sampleMapSelStart) continue;
ins->amiga.noteMap[i].dpcmDelta=ins->amiga.noteMap[sampleMapSelStart].dpcmDelta;
}
}
}
} else {
if (ImGui::MenuItem("set entire map to this note")) {
@ -2698,9 +2720,15 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
}
}
if (ins->type==DIV_INS_NES) {
if (ImGui::MenuItem("clear frequencies")) {
if (ImGui::MenuItem("reset pitches")) {
for (int i=0; i<120; i++) {
ins->amiga.noteMap[i].dpcmFreq=15;
}
}
if (ImGui::MenuItem("clear delta counter values")) {
for (int i=0; i<120; i++) {
ins->amiga.noteMap[i].dpcmDelta=-1;
}
}
} else {
if (ImGui::MenuItem("reset notes")) {