add delayed release effects

FCxx - note release
E7xx - macro release
This commit is contained in:
tildearrow 2024-03-15 13:45:57 -05:00
parent 18b7b11f73
commit 8f20824234
5 changed files with 58 additions and 26 deletions

View file

@ -80,8 +80,10 @@ not all chips support these effects.
- `0Cxx`: **Retrigger.** repeats current note every `xx` ticks. - `0Cxx`: **Retrigger.** repeats current note every `xx` ticks.
- this effect is not continuous; it must be entered on every row. - this effect is not continuous; it must be entered on every row.
- `ECxx`: **Note cut.** ends current note after `xx` ticks. for FM instruments, it's equivalent to a "key off". - `ECxx`: **Note cut.** triggers note off after `xx` ticks. this triggers key off in FM/hardware envelope chips, or cuts note otherwise.
- `EDxx`: **Note delay.** delays note by `x` ticks. - `EDxx`: **Note delay.** delays note by `x` ticks.
- `FCxx`: **Note release.** releases current note after `xx` ticks. this releases macros and triggers key off in FM/hardware envelope chips.
- `E7xx`: **Macro release.** releases macros after `xx` ticks. this does not trigger key off.
## other ## other

View file

@ -105,6 +105,8 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "E4xx: Set vibrato range"; return "E4xx: Set vibrato range";
case 0xe5: case 0xe5:
return "E5xx: Set pitch (80: center)"; return "E5xx: Set pitch (80: center)";
case 0xe7:
return "E7xx: Macro release";
case 0xea: case 0xea:
return "EAxx: Legato"; return "EAxx: Legato";
case 0xeb: case 0xeb:
@ -137,6 +139,8 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "F9xx: Single tick volume slide down"; return "F9xx: Single tick volume slide down";
case 0xfa: case 0xfa:
return "FAxx: Fast volume slide (0y: down; x0: up)"; return "FAxx: Fast volume slide (0y: down; x0: up)";
case 0xfc:
return "FCxx: Note release";
case 0xff: case 0xff:
return "FFxx: Stop song"; return "FFxx: Stop song";
default: default:
@ -1687,6 +1691,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
} }
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
chan[i].cut=-1; chan[i].cut=-1;
chan[i].cutType=0;
} }
repeatPattern=oldRepeatPattern; repeatPattern=oldRepeatPattern;
if (preserveDrift) { if (preserveDrift) {

View file

@ -104,7 +104,7 @@ struct DivChannelState {
int delayOrder, delayRow, retrigSpeed, retrigTick; int delayOrder, delayRow, retrigSpeed, retrigTick;
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
int tremoloDepth, tremoloRate, tremoloPos; int tremoloDepth, tremoloRate, tremoloPos;
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta; unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
bool wentThroughNote, goneThroughNote; bool wentThroughNote, goneThroughNote;
@ -147,6 +147,7 @@ struct DivChannelState {
panRR(0), panRR(0),
lastVibrato(0), lastVibrato(0),
lastPorta(0), lastPorta(0),
cutType(0),
doNote(false), doNote(false),
legato(false), legato(false),
portaStop(false), portaStop(false),

View file

@ -950,6 +950,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
dispatchCmd(DivCommand(DIV_CMD_HINT_PITCH,i,chan[i].pitch)); dispatchCmd(DivCommand(DIV_CMD_HINT_PITCH,i,chan[i].pitch));
break; break;
case 0xe7: // delayed macro release
// "Bruh"
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
chan[i].cut=effectVal+1;
chan[i].cutType=2;
}
break;
case 0xea: // legato mode case 0xea: // legato mode
chan[i].legato=effectVal; chan[i].legato=effectVal;
break; break;
@ -959,6 +966,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xec: // delayed note cut case 0xec: // delayed note cut
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) { if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
chan[i].cut=effectVal+1; chan[i].cut=effectVal+1;
chan[i].cutType=0;
} }
break; break;
case 0xee: // external command case 0xee: // external command
@ -1045,6 +1053,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
} }
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break; break;
case 0xfc: // delayed note release
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
chan[i].cut=effectVal+1;
chan[i].cutType=1;
}
break;
case 0xff: // stop song case 0xff: // stop song
shallStopSched=true; shallStopSched=true;
@ -1333,7 +1348,10 @@ void DivEngine::nextRow() {
} }
} }
} }
if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks+addition; if (doPrepareCut && !wantPreNote && chan[i].cut<=0) {
chan[i].cut=ticks+addition;
chan[i].cutType=0;
}
} }
} }
} }
@ -1530,30 +1548,36 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
} }
if (chan[i].cut>0) { if (chan[i].cut>0) {
if (--chan[i].cut<1) { if (--chan[i].cut<1) {
chan[i].oldNote=chan[i].note; if (chan[i].cutType==2) {
//chan[i].note=-1; dispatchCmd(DivCommand(DIV_CMD_ENV_RELEASE,i));
if (chan[i].inPorta && song.noteOffResetsSlides) { chan[i].releasing=true;
chan[i].keyOff=true; } else {
chan[i].keyOn=false; chan[i].oldNote=chan[i].note;
if (chan[i].stopOnOff) { //chan[i].note=-1;
chan[i].portaNote=-1; if (chan[i].inPorta && song.noteOffResetsSlides) {
chan[i].portaSpeed=-1; chan[i].keyOff=true;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].keyOn=false;
chan[i].stopOnOff=false; if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
}
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].scheduledSlideReset=true;
} }
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { if (chan[i].cutType==1) {
chan[i].portaNote=-1; dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,i));
chan[i].portaSpeed=-1; } else {
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
}*/
} }
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); chan[i].releasing=true;
chan[i].scheduledSlideReset=true;
} }
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
} }
} }
if (chan[i].resetArp) { if (chan[i].resetArp) {

View file

@ -490,7 +490,7 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // E4 GUI_COLOR_PATTERN_EFFECT_MISC, // E4
GUI_COLOR_PATTERN_EFFECT_PITCH, // E5 GUI_COLOR_PATTERN_EFFECT_PITCH, // E5
GUI_COLOR_PATTERN_EFFECT_INVALID, // E6 GUI_COLOR_PATTERN_EFFECT_INVALID, // E6
GUI_COLOR_PATTERN_EFFECT_INVALID, // E7 GUI_COLOR_PATTERN_EFFECT_TIME, // E7
GUI_COLOR_PATTERN_EFFECT_INVALID, // E8 GUI_COLOR_PATTERN_EFFECT_INVALID, // E8
GUI_COLOR_PATTERN_EFFECT_INVALID, // E9 GUI_COLOR_PATTERN_EFFECT_INVALID, // E9
GUI_COLOR_PATTERN_EFFECT_MISC, // EA GUI_COLOR_PATTERN_EFFECT_MISC, // EA
@ -511,7 +511,7 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9 GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9
GUI_COLOR_PATTERN_EFFECT_VOLUME, // FA GUI_COLOR_PATTERN_EFFECT_VOLUME, // FA
GUI_COLOR_PATTERN_EFFECT_INVALID, // FB GUI_COLOR_PATTERN_EFFECT_INVALID, // FB
GUI_COLOR_PATTERN_EFFECT_INVALID, // FC GUI_COLOR_PATTERN_EFFECT_TIME, // FC
GUI_COLOR_PATTERN_EFFECT_INVALID, // FD GUI_COLOR_PATTERN_EFFECT_INVALID, // FD
GUI_COLOR_PATTERN_EFFECT_INVALID, // FE GUI_COLOR_PATTERN_EFFECT_INVALID, // FE
GUI_COLOR_PATTERN_EFFECT_SONG // FF GUI_COLOR_PATTERN_EFFECT_SONG // FF