Namco 163: add waveform position latch option

for FamiTracker compatibility
its default value will be decided by a poll

issue #2476
This commit is contained in:
tildearrow 2026-01-18 18:31:37 -05:00
parent 2da316b346
commit 344f8d3a22
4 changed files with 55 additions and 11 deletions

View file

@ -711,6 +711,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
ds.system[systemID] = DIV_SYSTEM_N163;
ds.systemFlags[systemID].set("channels", (int)n163Chans - 1);
ds.systemChans[systemID]=CLAMP(n163Chans,1,8);
ds.systemFlags[systemID].set("posLatch",true);
systemID++;
for (int ch = 0; ch < (int)n163Chans; ch++) {
@ -1948,8 +1949,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
// - in FamiTracker this is in bytes
// - a value of 7F has special meaning
if (pat->newData[row][DIV_PAT_FXVAL(j)]==0x7f) {
pat->newData[row][DIV_PAT_FX(j)]=-1;
pat->newData[row][DIV_PAT_FXVAL(j)]=-1;
pat->newData[row][DIV_PAT_FXVAL(j)]=0xff;
} else {
pat->newData[row][DIV_PAT_FXVAL(j)]=MIN(pat->newData[row][DIV_PAT_FXVAL(j)]<<1,0xff);
}

View file

@ -20,6 +20,7 @@
#include "n163.h"
#include "../engine.h"
#include "../../ta-log.h"
#include "IconsFontAwesome4.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
@ -206,7 +207,7 @@ void DivPlatformN163::tick(bool sysTick) {
}
chan[i].freqChanged=true;
}
if (chan[i].std.duty.had) {
if (chan[i].std.duty.had && !chan[i].wavePosLatch) {
if (chan[i].curWavePos!=chan[i].std.duty.val) {
chan[i].curWavePos=chan[i].std.duty.val;
chan[i].waveChanged=true;
@ -230,7 +231,7 @@ void DivPlatformN163::tick(bool sysTick) {
}
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
if (chan[i].std.ex1.had && !chan[i].wavePosLatch) {
if (chan[i].curWaveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].curWaveLen=chan[i].std.ex1.val&0xfc;
chan[i].freqChanged=true;
@ -310,11 +311,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
if (ins->n163.wave>=0) {
chan[c.chan].wave=ins->n163.wave;
}
chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
if (!chan[c.chan].wavePosLatch) {
chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
}
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,true);
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
@ -425,6 +428,23 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].waveUpdated=true;
}
}
// wave position latching.
// if enabled in chip flags, setting wave pos through effects will "lock" it
// to a specific value until a wave pos effect with value FE or FF is used.
if (posLatch) {
chan[c.chan].wavePosLatch=true;
if (c.value>=0xfe) {
chan[c.chan].wavePosLatch=false;
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163);
chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
}
}
break;
case DIV_CMD_N163_WAVE_LENGTH:
if (c.value2&1) {
@ -543,6 +563,20 @@ DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) {
return oscBuf[ch];
}
DivChannelModeHints DivPlatformN163::getModeHints(int ch) {
DivChannelModeHints ret;
if (!posLatch) return ret;
ret.count=1;
ret.hint[0]=ICON_FA_LOCK;
ret.type[0]=0;
if (chan[ch].wavePosLatch) {
ret.type[0]=21;
}
return ret;
}
unsigned char* DivPlatformN163::getRegisterPool() {
return regPool;
}
@ -601,6 +635,7 @@ void DivPlatformN163::setFlags(const DivConfig& flags) {
CHECK_CUSTOM_CLOCK;
initChanMax=chanMax=flags.getInt("channels",7)&7;
multiplex=!flags.getBool("multiplex",false); // not accurate in real hardware
posLatch=flags.getBool("posLatch",false);
rate=chipClock;
rate/=15;
n163.set_multiplex(multiplex);

View file

@ -31,9 +31,10 @@ class DivPlatformN163: public DivDispatch {
short wave, wavePos, waveLen;
short curWavePos, curWaveLen;
bool waveMode;
bool wavePosLatch;
bool volumeChanged;
bool waveChanged, waveUpdated;
DivWaveSynth ws;
DivWaveSynth ws;
Channel():
SharedChannel<signed char>(15),
resVol(15),
@ -43,6 +44,7 @@ class DivPlatformN163: public DivDispatch {
curWavePos(0),
curWaveLen(0),
waveMode(0),
wavePosLatch(false),
volumeChanged(false),
waveChanged(false),
waveUpdated(false) {}
@ -61,7 +63,7 @@ class DivPlatformN163: public DivDispatch {
unsigned char initChanMax;
unsigned char chanMax;
short loadWave, loadPos;
bool multiplex, lenCompensate;
bool multiplex, lenCompensate, posLatch;
n163_core n163;
unsigned char regPool[128];
@ -83,6 +85,7 @@ class DivPlatformN163: public DivDispatch {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
DivChannelModeHints getModeHints(int chan);
const DivMemoryComposition* getMemCompo(int index);
void setFlags(const DivConfig& flags);
void notifyWaveChange(int wave);

View file

@ -1259,6 +1259,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
int channels=flags.getInt("channels",7)+1;
bool multiplex=flags.getBool("multiplex",false);
bool lenCompensate=flags.getBool("lenCompensate",false);
bool posLatch=flags.getBool("posLatch",false);
ImGui::Text(_("Clock rate:"));
ImGui::Indent();
@ -1304,6 +1305,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
if (ImGui::Checkbox(_("Scale frequency to wave length"),&lenCompensate)) {
altered=true;
}
if (ImGui::Checkbox(_("Waveform position latch"),&posLatch)) {
altered=true;
}
ImGui::SetItemTooltip(_("when enabled, a waveform position effect will lock the position, preventing instrument changes from changing it.\nuse a wave position effect with value FE or FF to unlock it."));
if (altered) {
e->lockSave([&]() {
@ -1311,6 +1316,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
flags.set("channels",channels-1);
flags.set("multiplex",multiplex);
flags.set("lenCompensate",lenCompensate);
flags.set("posLatch",posLatch);
});
}
break;