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 { struct NoteMap {
int freq; int freq;
short ind; short ind;
bool reversed;
NoteMap(): NoteMap():
freq(0), freq(0),
ind(-1) {} ind(-1),
reversed(false) {}
}; };
short initSample; short initSample;

View file

@ -236,21 +236,30 @@ void DivPlatformES5506::tick(bool sysTick) {
DivInstrument* ins=parent->getIns(chan[i].ins); DivInstrument* ins=parent->getIns(chan[i].ins);
// volume/panning macros // volume/panning macros
if (chan[i].std.vol.had) { if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&0xff)*MIN(0xffff,chan[i].std.vol.val))/0xff; const unsigned int nextVol=((chan[i].vol&0xff)*MIN(0xffff,chan[i].std.vol.val))/0xff;
if (!isMuted[i]) { if (chan[i].outVol!=nextVol) {
chan[i].volChanged=true; chan[i].outVol=nextVol;
if (!isMuted[i]) {
chan[i].volChanged.changed=0xff;
}
} }
} }
if (chan[i].std.panL.had) { 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; const unsigned int nextLVol=(((ins->es5506.lVol*(chan[i].lVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panL.val))/0xffff;
if (!isMuted[i]) { if (chan[i].outLVol!=nextLVol) {
chan[i].volChanged=true; chan[i].outLVol=nextLVol;
if (!isMuted[i]) {
chan[i].volChanged.lVol=1;
}
} }
} }
if (chan[i].std.panR.had) { 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; const unsigned int nextRVol=(((ins->es5506.rVol*(chan[i].rVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panR.val))/0xffff;
if (!isMuted[i]) { if (chan[i].outRVol!=nextRVol) {
chan[i].volChanged=true; chan[i].outRVol=nextRVol;
if (!isMuted[i]) {
chan[i].volChanged.rVol=1;
}
} }
} }
// arpeggio/pitch macros, frequency related // arpeggio/pitch macros, frequency related
@ -306,7 +315,7 @@ void DivPlatformES5506::tick(bool sysTick) {
} }
break; break;
case 2: { // delta 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) { if (chan[i].k1Offs!=next_k1) {
chan[i].k1Offs=next_k1; chan[i].k1Offs=next_k1;
chan[i].filterChanged.k1=1; chan[i].filterChanged.k1=1;
@ -332,7 +341,7 @@ void DivPlatformES5506::tick(bool sysTick) {
} }
break; break;
case 2: { // delta 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) { if (chan[i].k2Offs!=next_k2) {
chan[i].k2Offs=next_k2; chan[i].k2Offs=next_k2;
chan[i].filterChanged.k2=1; chan[i].filterChanged.k2=1;
@ -385,19 +394,54 @@ void DivPlatformES5506::tick(bool sysTick) {
} }
} }
// update registers // update registers
if (chan[i].volChanged) { if (chan[i].volChanged.changed) {
if (!isMuted[i]) { // calculate volume (16 bit) if (!isMuted[i]) { // calculate volume (16 bit)
chan[i].resLVol=(chan[i].outVol*chan[i].outLVol)/0xffff; if (chan[i].volChanged.lVol) {
chan[i].resRVol=(chan[i].outVol*chan[i].outRVol)/0xffff; chan[i].resLVol=(chan[i].outVol*chan[i].outLVol)/0xffff;
if (!chan[i].keyOn) { if (!chan[i].keyOn) {
pageWrite(0x00|i,0x02,chan[i].resLVol); pageWrite(0x00|i,0x02,chan[i].resLVol);
pageWrite(0x00|i,0x04,chan[i].resRVol); }
}
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 } else { // mute
pageWrite(0x00|i,0x02,0); pageWrite(0x00|i,0x02,0);
pageWrite(0x00|i,0x04,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].filterChanged.changed) {
if (!chan[i].keyOn) { if (!chan[i].keyOn) {
@ -527,7 +571,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
const unsigned int end=start+(length<<11); const unsigned int end=start+(length<<11);
chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT; chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
chan[c.chan].pcm.freqOffs=off; 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.bank=(s->offES5506>>22)&3;
chan[c.chan].pcm.start=start; chan[c.chan].pcm.start=start;
chan[c.chan].pcm.end=end; chan[c.chan].pcm.end=end;
@ -545,17 +589,17 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value); chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].volChanged=true; chan[c.chan].volChanged.changed=0xff;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
} }
if (!chan[c.chan].std.vol.will) { if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=(0xffff*chan[c.chan].vol)/0xff; chan[c.chan].outVol=(0xffff*chan[c.chan].vol)/0xff;
} }
if (!chan[c.chan].std.panL.will) { 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) { 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].active=true;
chan[c.chan].keyOn=true; chan[c.chan].keyOn=true;
@ -580,12 +624,12 @@ int DivPlatformES5506::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_VOLUME: 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; chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) { if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=(0xffff*c.value)/0xff; chan[c.chan].outVol=(0xffff*c.value)/0xff;
if (!isMuted[c.chan]) { 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); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
// 08LR, each nibble means volume multipler for each channels // 08LR, each nibble means volume multipler for each channels
// Left volume // 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) { if (chan[c.chan].lVol!=lVol) {
chan[c.chan].lVol=lVol; chan[c.chan].lVol=lVol;
if (!chan[c.chan].std.panL.has) { 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]) { if (!isMuted[c.chan]) {
chan[c.chan].volChanged=true; chan[c.chan].volChanged.lVol=1;
} }
} }
} }
// Right volume // 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) { if (chan[c.chan].rVol!=rVol) {
chan[c.chan].rVol=rVol; chan[c.chan].rVol=rVol;
if (!chan[c.chan].std.panR.has) { 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]) { 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; chan[c.chan].envChanged.k2Ramp=1;
break; break;
case DIV_CMD_NOTE_PORTA: { 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; bool return2=false;
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value; chan[c.chan].baseFreq+=c.value;
@ -704,7 +748,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_POS: { case DIV_CMD_SAMPLE_POS: {
if (chan[c.chan].useWave) break; if (chan[c.chan].useWave) break;
if (chan[c.chan].active) { 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)) { 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)); 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++) { for (int i=0; i<=chanMax; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;
chan[i].volChanged=true; chan[i].volChanged.changed=0xff;
chan[i].filterChanged.changed=0xff; chan[i].filterChanged.changed=0xff;
chan[i].envChanged.changed=0xff; chan[i].envChanged.changed=0xff;
chan[i].sample=-1; chan[i].sample=-1;

View file

@ -32,7 +32,7 @@
class DivPlatformES5506: public DivDispatch, public es550x_intf { class DivPlatformES5506: public DivDispatch, public es550x_intf {
struct Channel { struct Channel {
struct PCM { struct PCM {
int index; int index, next;
double freqOffs; double freqOffs;
bool reversed; bool reversed;
unsigned int bank; unsigned int bank;
@ -44,6 +44,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
DivSampleLoopMode loopMode; DivSampleLoopMode loopMode;
PCM(): PCM():
index(-1), index(-1),
next(-1),
freqOffs(1.0), freqOffs(1.0),
reversed(false), reversed(false),
bank(0), bank(0),
@ -55,7 +56,21 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {} loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
} pcm; } pcm;
int freq, baseFreq, pitch, pitch2, note, ins, sample, wave; 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 struct FilterChanged { // Filter changed flags
union { // pack flag bits in single byte union { // pack flag bits in single byte
@ -118,7 +133,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
active(false), active(false),
insChanged(true), insChanged(true),
freqChanged(false), freqChanged(false),
volChanged(false), pcmChanged(false),
keyOn(false), keyOn(false),
keyOff(false), keyOff(false),
inPorta(false), inPorta(false),
@ -127,8 +142,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
k1Offs(0), k1Offs(0),
k2Offs(0), k2Offs(0),
vol(0xff), vol(0xff),
lVol(0xf), lVol(0xff),
rVol(0xf), rVol(0xff),
outVol(0xffff), outVol(0xffff),
outLVol(0xffff), outLVol(0xffff),
outRVol(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->active?colorOn:colorOff,">> Active");
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged"); ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged"); 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.mode?colorOn:colorOff,">> FilterModeChanged");
ImGui::TextColored(ch->filterChanged.k1?colorOn:colorOff,">> FilterK1Changed"); ImGui::TextColored(ch->filterChanged.k1?colorOn:colorOff,">> FilterK1Changed");
ImGui::TextColored(ch->filterChanged.k2?colorOn:colorOff,">> FilterK2Changed"); 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.rVRamp?colorOn:colorOff,">> EnvRVRampChanged");
ImGui::TextColored(ch->envChanged.k1Ramp?colorOn:colorOff,">> EnvK1RampChanged"); ImGui::TextColored(ch->envChanged.k1Ramp?colorOn:colorOff,">> EnvK1RampChanged");
ImGui::TextColored(ch->envChanged.k2Ramp?colorOn:colorOff,">> EnvK2RampChanged"); 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->keyOn?colorOn:colorOff,">> KeyOn");
ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff"); ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff");
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta"); ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");

View file

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