Add per-note map reversed playback support

Prepare for pcm changed flag, 8 bit panning command
Reduce register spamming in volume update routine

Progress report:
* Emulation core update, Dispatch update:
  * Volume is unsigned like original chip, for reduce overflow.
* Add multi-mode macro support for radio button
This commit is contained in:
cam900 2022-04-30 23:45:05 +09:00
parent 72e8bb89a7
commit 53a52788e2
5 changed files with 109 additions and 41 deletions

View File

@ -300,10 +300,12 @@ struct DivInstrumentAmiga {
struct NoteMap {
int freq;
short ind;
bool reversed;
NoteMap():
freq(0),
ind(-1) {}
ind(-1),
reversed(false) {}
};
short initSample;

View File

@ -236,21 +236,30 @@ void DivPlatformES5506::tick(bool sysTick) {
DivInstrument* ins=parent->getIns(chan[i].ins);
// volume/panning macros
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&0xff)*MIN(0xffff,chan[i].std.vol.val))/0xff;
if (!isMuted[i]) {
chan[i].volChanged=true;
const unsigned int nextVol=((chan[i].vol&0xff)*MIN(0xffff,chan[i].std.vol.val))/0xff;
if (chan[i].outVol!=nextVol) {
chan[i].outVol=nextVol;
if (!isMuted[i]) {
chan[i].volChanged.changed=0xff;
}
}
}
if (chan[i].std.panL.had) {
chan[i].outLVol=(((ins->es5506.lVol*(chan[i].lVol&0xf))/0xf)*MIN(0xffff,chan[i].std.panL.val))/0xffff;
if (!isMuted[i]) {
chan[i].volChanged=true;
const unsigned int nextLVol=(((ins->es5506.lVol*(chan[i].lVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panL.val))/0xffff;
if (chan[i].outLVol!=nextLVol) {
chan[i].outLVol=nextLVol;
if (!isMuted[i]) {
chan[i].volChanged.lVol=1;
}
}
}
if (chan[i].std.panR.had) {
chan[i].outRVol=(((ins->es5506.rVol*(chan[i].rVol&0xf))/0xf)*MIN(0xffff,chan[i].std.panR.val))/0xffff;
if (!isMuted[i]) {
chan[i].volChanged=true;
const unsigned int nextRVol=(((ins->es5506.rVol*(chan[i].rVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panR.val))/0xffff;
if (chan[i].outRVol!=nextRVol) {
chan[i].outRVol=nextRVol;
if (!isMuted[i]) {
chan[i].volChanged.rVol=1;
}
}
}
// arpeggio/pitch macros, frequency related
@ -306,7 +315,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
break;
case 2: { // delta
signed int next_k1=CLAMP_VAL(chan[i].k1Offs+chan[i].std.ex1.val,-65535,65535);
const signed int next_k1=CLAMP_VAL(chan[i].k1Offs+chan[i].std.ex1.val,-65535,65535);
if (chan[i].k1Offs!=next_k1) {
chan[i].k1Offs=next_k1;
chan[i].filterChanged.k1=1;
@ -332,7 +341,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
break;
case 2: { // delta
signed int next_k2=CLAMP_VAL(chan[i].k2Offs+chan[i].std.ex2.val,-65535,65535);
const signed int next_k2=CLAMP_VAL(chan[i].k2Offs+chan[i].std.ex2.val,-65535,65535);
if (chan[i].k2Offs!=next_k2) {
chan[i].k2Offs=next_k2;
chan[i].filterChanged.k2=1;
@ -385,19 +394,54 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
// update registers
if (chan[i].volChanged) {
if (chan[i].volChanged.changed) {
if (!isMuted[i]) { // calculate volume (16 bit)
chan[i].resLVol=(chan[i].outVol*chan[i].outLVol)/0xffff;
chan[i].resRVol=(chan[i].outVol*chan[i].outRVol)/0xffff;
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x02,chan[i].resLVol);
pageWrite(0x00|i,0x04,chan[i].resRVol);
if (chan[i].volChanged.lVol) {
chan[i].resLVol=(chan[i].outVol*chan[i].outLVol)/0xffff;
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x02,chan[i].resLVol);
}
}
if (chan[i].volChanged.rVol) {
chan[i].resRVol=(chan[i].outVol*chan[i].outRVol)/0xffff;
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x04,chan[i].resRVol);
}
}
} else { // mute
pageWrite(0x00|i,0x02,0);
pageWrite(0x00|i,0x04,0);
}
chan[i].volChanged=false;
chan[i].volChanged.changed=0;
}
if (chan[i].pcmChanged) {
DivInstrument* ins=parent->getIns(chan[i].ins);
if (!ins->amiga.useNoteMap) {
double off=1.0;
if (chan[i].pcm.next>=0 && chan[i].pcm.next<parent->song.sampleLen) {
chan[i].pcm.index=chan[i].pcm.next;
DivSample* s=parent->getSample(chan[i].pcm.next);
if (s->centerRate<1) {
off=1.0;
} else {
off=(double)s->centerRate/8363.0;
}
const unsigned int start=s->offES5506<<10;
const unsigned int length=s->samples-1;
const unsigned int end=start+(length<<11);
chan[i].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
chan[i].pcm.freqOffs=off;
chan[i].pcm.reversed=ins->amiga.reversed;
chan[i].pcm.bank=(s->offES5506>>22)&3;
chan[i].pcm.start=start;
chan[i].pcm.end=end;
chan[i].pcm.length=length;
chan[i].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
chan[i].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
chan[i].keyOn=true;
}
}
chan[i].pcmChanged=false;
}
if (chan[i].filterChanged.changed) {
if (!chan[i].keyOn) {
@ -527,7 +571,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
const unsigned int end=start+(length<<11);
chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
chan[c.chan].pcm.freqOffs=off;
chan[c.chan].pcm.reversed=ins->amiga.reversed;
chan[c.chan].pcm.reversed=ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].reversed:ins->amiga.reversed;
chan[c.chan].pcm.bank=(s->offES5506>>22)&3;
chan[c.chan].pcm.start=start;
chan[c.chan].pcm.end=end;
@ -545,17 +589,17 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].volChanged=true;
chan[c.chan].volChanged.changed=0xff;
chan[c.chan].note=c.value;
}
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=(0xffff*chan[c.chan].vol)/0xff;
}
if (!chan[c.chan].std.panL.will) {
chan[c.chan].outLVol=(ins->es5506.lVol*chan[c.chan].lVol)/0xf;
chan[c.chan].outLVol=(ins->es5506.lVol*chan[c.chan].lVol)/0xff;
}
if (!chan[c.chan].std.panR.will) {
chan[c.chan].outRVol=(ins->es5506.rVol*chan[c.chan].rVol)/0xf;
chan[c.chan].outRVol=(ins->es5506.rVol*chan[c.chan].rVol)/0xff;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
@ -580,12 +624,12 @@ int DivPlatformES5506::dispatch(DivCommand c) {
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
if (chan[c.chan].vol!=(unsigned int)(c.value)) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=(0xffff*c.value)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged=true;
chan[c.chan].volChanged.changed=0xff;
}
}
}
@ -600,24 +644,24 @@ int DivPlatformES5506::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
// 08LR, each nibble means volume multipler for each channels
// Left volume
unsigned char lVol=(c.value>>4)&0xf;
const unsigned int lVol=(0xff*((c.value>>4)&0xf))/0xf;
if (chan[c.chan].lVol!=lVol) {
chan[c.chan].lVol=lVol;
if (!chan[c.chan].std.panL.has) {
chan[c.chan].outLVol=(ins->es5506.lVol*lVol)/0xf;
chan[c.chan].outLVol=(ins->es5506.lVol*lVol)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged=true;
chan[c.chan].volChanged.lVol=1;
}
}
}
// Right volume
unsigned char rVol=(c.value>>0)&0xf;
const unsigned int rVol=(0xff*((c.value>>0)&0xf))/0xf;
if (chan[c.chan].rVol!=rVol) {
chan[c.chan].rVol=rVol;
if (!chan[c.chan].std.panR.has) {
chan[c.chan].outRVol=(ins->es5506.rVol*rVol)/0xf;
chan[c.chan].outRVol=(ins->es5506.rVol*rVol)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged=true;
chan[c.chan].volChanged.rVol=1;
}
}
}
@ -667,7 +711,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].envChanged.k2Ramp=1;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_ES5506(c.chan,c.value2);
const int destFreq=NOTE_ES5506(c.chan,c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
@ -704,7 +748,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_POS: {
if (chan[c.chan].useWave) break;
if (chan[c.chan].active) {
unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value;
const unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value;
if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos<chan[c.chan].pcm.length)) {
pageWrite(0x20|c.chan,0x03,chan[c.chan].pcm.start+(pos<<11));
}
@ -732,7 +776,7 @@ void DivPlatformES5506::forceIns() {
for (int i=0; i<=chanMax; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].volChanged=true;
chan[i].volChanged.changed=0xff;
chan[i].filterChanged.changed=0xff;
chan[i].envChanged.changed=0xff;
chan[i].sample=-1;

View File

@ -32,7 +32,7 @@
class DivPlatformES5506: public DivDispatch, public es550x_intf {
struct Channel {
struct PCM {
int index;
int index, next;
double freqOffs;
bool reversed;
unsigned int bank;
@ -44,6 +44,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
DivSampleLoopMode loopMode;
PCM():
index(-1),
next(-1),
freqOffs(1.0),
reversed(false),
bank(0),
@ -55,7 +56,21 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
} pcm;
int freq, baseFreq, pitch, pitch2, note, ins, sample, wave;
bool active, insChanged, freqChanged, volChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
struct VolChanged { // Volume changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char lVol: 1; // left volume
unsigned char rVol: 1; // right volume
unsigned char dummy: 6; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
VolChanged() :
changed(0) {}
} volChanged;
struct FilterChanged { // Filter changed flags
union { // pack flag bits in single byte
@ -118,7 +133,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
active(false),
insChanged(true),
freqChanged(false),
volChanged(false),
pcmChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
@ -127,8 +142,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
k1Offs(0),
k2Offs(0),
vol(0xff),
lVol(0xf),
rVol(0xf),
lVol(0xff),
rVol(0xff),
outVol(0xffff),
outLVol(0xffff),
outRVol(0xffff),

View File

@ -378,7 +378,8 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::TextColored(ch->active?colorOn:colorOff,">> Active");
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged");
ImGui::TextColored(ch->volChanged?colorOn:colorOff,">> VolChanged");
ImGui::TextColored(ch->volChanged.lVol?colorOn:colorOff,">> LVolChanged");
ImGui::TextColored(ch->volChanged.rVol?colorOn:colorOff,">> RVolChanged");
ImGui::TextColored(ch->filterChanged.mode?colorOn:colorOff,">> FilterModeChanged");
ImGui::TextColored(ch->filterChanged.k1?colorOn:colorOff,">> FilterK1Changed");
ImGui::TextColored(ch->filterChanged.k2?colorOn:colorOff,">> FilterK2Changed");
@ -387,6 +388,7 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::TextColored(ch->envChanged.rVRamp?colorOn:colorOff,">> EnvRVRampChanged");
ImGui::TextColored(ch->envChanged.k1Ramp?colorOn:colorOff,">> EnvK1RampChanged");
ImGui::TextColored(ch->envChanged.k2Ramp?colorOn:colorOff,">> EnvK2RampChanged");
ImGui::TextColored(ch->pcmChanged?colorOn:colorOff,">> PCMChanged");
ImGui::TextColored(ch->keyOn?colorOn:colorOff,">> KeyOn");
ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff");
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");

View File

@ -2513,10 +2513,11 @@ void FurnaceGUI::drawInsEdit() {
ImGui::BeginDisabled(ins->amiga.useWave);
P(ImGui::Checkbox("Use sample map (does not work yet!)",&ins->amiga.useNoteMap));
if (ins->amiga.useNoteMap) {
if (ImGui::BeginTable("NoteMap",3,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
if (ImGui::BeginTable("NoteMap",4,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupScrollFreeze(0,1);
@ -2526,6 +2527,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Text("Sample");
ImGui::TableNextColumn();
ImGui::Text("Frequency");
ImGui::TableNextColumn();
ImGui::Text("Reversed");
for (int i=0; i<120; i++) {
ImGui::TableNextRow();
ImGui::PushID(fmt::sprintf("NM_%d",i).c_str());
@ -2559,6 +2562,8 @@ void FurnaceGUI::drawInsEdit() {
if (ins->amiga.noteMap[i].freq<0) ins->amiga.noteMap[i].freq=0;
if (ins->amiga.noteMap[i].freq>262144) ins->amiga.noteMap[i].freq=262144;
}
ImGui::TableNextColumn();
P(ImGui::Checkbox("##SR",&ins->amiga.noteMap[i].reversed));
ImGui::PopID();
}
ImGui::EndTable();