diff --git a/demos/amiga/furnace0.6crk.fur b/demos/amiga/furnace0.6crk.fur new file mode 100644 index 000000000..0966f17b0 Binary files /dev/null and b/demos/amiga/furnace0.6crk.fur differ diff --git a/demos/misc/rf5wapianoroll.fur b/demos/misc/rf5wapianoroll.fur new file mode 100644 index 000000000..4b2919f3f Binary files /dev/null and b/demos/misc/rf5wapianoroll.fur differ diff --git a/demos/sms/thunderblade-type-ii.fur b/demos/sms/thunderblade-type-ii.fur new file mode 100644 index 000000000..d2f922fa9 Binary files /dev/null and b/demos/sms/thunderblade-type-ii.fur differ diff --git a/demos/specs2/object.fur b/demos/specs2/object.fur new file mode 100644 index 000000000..c9a6e8ea4 Binary files /dev/null and b/demos/specs2/object.fur differ diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 769a1814b..6ab448e6d 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -83,6 +83,8 @@ return 2; \ } +#define IS_EXTCH_MUTED (isOpMuted[0] && isOpMuted[1] && isOpMuted[2] && isOpMuted[3]) + class DivPlatformOPN: public DivPlatformFMBase { protected: const unsigned short ADDR_MULT_DT=0x30; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 6443729ff..8e16071af 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -25,7 +25,6 @@ #define CHIP_DIVIDER fmDivBase #define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6]))) -#define IS_EXTCH_MUTED (isOpMuted[0] && isOpMuted[1] && isOpMuted[2] && isOpMuted[3]) int DivPlatformGenesisExt::dispatch(DivCommand c) { if (c.chan<2) { diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index aa19773d5..ce15f115c 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -529,10 +529,10 @@ void DivPlatformK007232::renderSamples(int sysID) { } const int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT); - int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-1,length); + int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-2,length); if (actualLength>0) { - if (actualLength>131072-1) { - actualLength=131072-1; + if (actualLength>131072-2) { + actualLength=131072-2; } if ((memPos&0xfe0000)!=((memPos+actualLength+1)&0xfe0000)) { memPos=(memPos+0x1ffff)&0xfe0000; diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index f1f61122c..292229a18 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -356,7 +356,7 @@ void DivPlatformMSM6295::renderSamples(int sysID) { // sample data size_t memPos=128*8; int sampleCount=parent->song.sampleLen; - if (sampleCount>128) sampleCount=128; + if (sampleCount>127) sampleCount=127; for (int i=0; isong.sample[i]; if (!s->renderOn[0][sysID]) { diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index add360b06..b8c40f68c 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -41,15 +41,30 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + + if (opChan[ch].insChanged) { + chan[2].state.alg=ins->fm.alg; + chan[2].state.fb=ins->fm.fb; + chan[2].state.op[ordch]=ins->fm.op[ordch]; + } + + if (noExtMacros) { + opChan[ch].macroInit(NULL); + } else { + opChan[ch].macroInit(ins); + } + if (!opChan[ch].std.vol.will) { + opChan[ch].outVol=opChan[ch].vol; + } unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; // TODO: how does this work?! if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -62,13 +77,14 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { opChan[ch].mask=op.enable; } if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); + rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); } opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; + opChan[ch].note=c.value; opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -80,15 +96,28 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { opChan[ch].keyOn=false; opChan[ch].active=false; break; + case DIV_CMD_NOTE_OFF_ENV: + if (noExtMacros) break; + opChan[ch].keyOff=true; + opChan[ch].keyOn=false; + opChan[ch].active=false; + opChan[ch].std.release(); + break; + case DIV_CMD_ENV_RELEASE: + if (noExtMacros) break; + opChan[ch].std.release(); + break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + if (!opChan[ch].std.vol.has) { + opChan[ch].outVol=c.value; + } unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } break; } diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 179a632a8..f08bdfad4 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -41,15 +41,32 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + + if (opChan[ch].insChanged) { + chan[2].state.alg=ins->fm.alg; + chan[2].state.fb=ins->fm.fb; + chan[2].state.fms=ins->fm.fms; + chan[2].state.ams=ins->fm.ams; + chan[2].state.op[ordch]=ins->fm.op[ordch]; + } + + if (noExtMacros) { + opChan[ch].macroInit(NULL); + } else { + opChan[ch].macroInit(ins); + } + if (!opChan[ch].std.vol.will) { + opChan[ch].outVol=opChan[ch].vol; + } unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; // TODO: how does this work?! if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -62,14 +79,15 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { opChan[ch].mask=op.enable; } if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); - rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); } opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; + opChan[ch].note=c.value; opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -81,15 +99,28 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { opChan[ch].keyOn=false; opChan[ch].active=false; break; + case DIV_CMD_NOTE_OFF_ENV: + if (noExtMacros) break; + opChan[ch].keyOff=true; + opChan[ch].keyOn=false; + opChan[ch].active=false; + opChan[ch].std.release(); + break; + case DIV_CMD_ENV_RELEASE: + if (noExtMacros) break; + opChan[ch].std.release(); + break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + if (!opChan[ch].std.vol.has) { + opChan[ch].outVol=c.value; + } unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } break; } diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index adde670a7..a68747f3a 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -37,15 +37,32 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + + if (opChan[ch].insChanged) { + chan[extChanOffs].state.alg=ins->fm.alg; + chan[extChanOffs].state.fb=ins->fm.fb; + chan[extChanOffs].state.fms=ins->fm.fms; + chan[extChanOffs].state.ams=ins->fm.ams; + chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; + } + + if (noExtMacros) { + opChan[ch].macroInit(NULL); + } else { + opChan[ch].macroInit(ins); + } + if (!opChan[ch].std.vol.will) { + opChan[ch].outVol=opChan[ch].vol; + } unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; // TODO: how does this work?! if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -58,14 +75,15 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { opChan[ch].mask=op.enable; } if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[extChanOffs]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); - rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); } opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; + opChan[ch].note=c.value; opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -77,15 +95,28 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { opChan[ch].keyOn=false; opChan[ch].active=false; break; + case DIV_CMD_NOTE_OFF_ENV: + if (noExtMacros) break; + opChan[ch].keyOff=true; + opChan[ch].keyOn=false; + opChan[ch].active=false; + opChan[ch].std.release(); + break; + case DIV_CMD_ENV_RELEASE: + if (noExtMacros) break; + opChan[ch].std.release(); + break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + if (!opChan[ch].std.vol.has) { + opChan[ch].outVol=c.value; + } unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } break; } diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index a86df5858..4bc15389c 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -37,15 +37,32 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + + if (opChan[ch].insChanged) { + chan[extChanOffs].state.alg=ins->fm.alg; + chan[extChanOffs].state.fb=ins->fm.fb; + chan[extChanOffs].state.fms=ins->fm.fms; + chan[extChanOffs].state.ams=ins->fm.ams; + chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; + } + + if (noExtMacros) { + opChan[ch].macroInit(NULL); + } else { + opChan[ch].macroInit(ins); + } + if (!opChan[ch].std.vol.will) { + opChan[ch].outVol=opChan[ch].vol; + } unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; // TODO: how does this work?! if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -58,14 +75,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].mask=op.enable; } if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[extChanOffs]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); - rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); } opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; + opChan[ch].note=c.value; opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -77,15 +95,28 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].keyOn=false; opChan[ch].active=false; break; + case DIV_CMD_NOTE_OFF_ENV: + if (noExtMacros) break; + opChan[ch].keyOff=true; + opChan[ch].keyOn=false; + opChan[ch].active=false; + opChan[ch].std.release(); + break; + case DIV_CMD_ENV_RELEASE: + if (noExtMacros) break; + opChan[ch].std.release(); + break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + if (!opChan[ch].std.vol.has) { + opChan[ch].outVol=c.value; + } unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator op=ins->fm.op[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); } break; } diff --git a/src/gui/about.cpp b/src/gui/about.cpp index b840d7c7d..0d14465dd 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -71,6 +71,7 @@ const char* aboutLine[]={ "DeMOSic", "DevEd", "Dippy", + "dumbut", "FΛDE", "Forte", "Fragmare", @@ -78,6 +79,7 @@ const char* aboutLine[]={ "iyatemu", "JayBOB18", "Jimmy-DS", + "Kagamiin~", "kleeder", "jaezu", "Laggy", diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 2f80a04f8..43fdac722 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -227,11 +227,11 @@ void FurnaceGUI::drawSampleEdit() { if (sampleDepths[i]==NULL) continue; if (ImGui::Selectable(sampleDepths[i])) { sample->prepareUndo(true); - e->lockEngine([sample]() { + e->lockEngine([this,sample,i]() { sample->render(); + sample->depth=(DivSampleDepth)i; + e->renderSamples(); }); - sample->depth=(DivSampleDepth)i; - e->renderSamplesP(); updateSampleTex=true; MARK_MODIFIED; }