diff --git a/demos/amiga/trance_dream.fur b/demos/amiga/trance_dream.fur deleted file mode 100644 index 553b3bbc8..000000000 Binary files a/demos/amiga/trance_dream.fur and /dev/null differ diff --git a/doc/2-interface/basic-mode.md b/doc/2-interface/basic-mode.md deleted file mode 100644 index cb69659d3..000000000 --- a/doc/2-interface/basic-mode.md +++ /dev/null @@ -1,36 +0,0 @@ -# basic mode - -Furnace comes with a "basic mode" that can be toggled through the "settings" menu. it disables certain features in Furnace that may look intimidating or confusing for newcomers. if you find that a certain feature of Furnace is missing, see if this setting is enabled or not. - -among the features that cannot be accessed in this mode are: - -### edit menu -- paste special -- operation masking -- input latch -- find and replace - -### speed window -- virtual tempo -- divider -- song length - -### song info window -- manual system naming -- tuning options - -### pattern right-click menu -- gradient/fade -- scale -- randomize -- invert values -- flip selection -- collapse -- expand - -### other windows -- mixer -- grooves -- channels -- pattern manager -- compatibility flags diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index c7f3cd7fd..021638026 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -15,6 +15,12 @@ however, effects are continuous (unless specified), which means you only need to - `F8xx`: **Single tick volume slide up.** adds `x` to volume on first tick only. - `F9xx`: **Single tick volume slide down.** subtracts `x` from volume on first tick only. - --- +- `D8xx`: **Set volume slide bottom boundary.** + - the default is 0. +- `D9xx`: **Set volume slide top boundary.** + - the default is the channel's maximum volume. +- `DCxx`: **Delayed mute.** sets volume to 0 after `xx` ticks. + - --- - `07xy`: **Tremolo.** changes volume to be "wavy" with a sine LFO. `x` is the speed. `y` is the depth. - tremolo is downward only. - maximum tremolo depth is -60 volume steps. diff --git a/papers/format.md b/papers/format.md index 406a07c02..92f8b0edf 100644 --- a/papers/format.md +++ b/papers/format.md @@ -255,6 +255,7 @@ size | description | - 0xdf: YM2612 XGM extended - 13 channels (UNAVAILABLE) | - 0xe0: QSound - 19 channels | - 0xe1: PS1 - 24 channels (UNAVAILABLE) + | - 0xe2: C64 (6581) with PCM - 4 channels (UNAVAILABLE) | - 0xf0: SID2 - 3 channels | - 0xf1: 5E01 - 5 channels | - 0xfc: Pong - 1 channel diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7ca58611b..b8114abbf 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -98,6 +98,12 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul break; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return _("Cxxx: Set tick rate (hz)"); + case 0xd8: + return _("D8xx: Set volume slide bottom boundary"); + case 0xd9: + return _("D9xx: Set volume slide top boundary"); + case 0xdc: + return _("DCxx: Delayed mute"); case 0xe0: return _("E0xx: Set arp speed"); case 0xe1: @@ -2108,6 +2114,7 @@ void DivEngine::reset() { chan[i]=DivChannelState(); if (idispatch(DivCommand(DIV_CMD_GET_VOLMAX,dispatchChanOfChan[i]))<<8)|0xff; chan[i].volume=chan[i].volMax; + chan[i].volTop=chan[i].volMax; if (song.linearPitch==0) chan[i].vibratoFine=4; } extValue=0; diff --git a/src/engine/engine.h b/src/engine/engine.h index 288f1af2b..08a4845c2 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -133,7 +133,7 @@ struct DivAudioExportOptions { struct DivChannelState { std::vector delayed; int note, oldNote, lastIns, pitch, portaSpeed, portaNote; - int volume, volSpeed, cut, legatoDelay, legatoTarget, rowDelay, volMax; + int volume, volSpeed, cut, volCut, legatoDelay, legatoTarget, rowDelay, volMax, volBottom, volTop; int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoShape, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; @@ -158,10 +158,13 @@ struct DivChannelState { volume(0x7f00), volSpeed(0), cut(-1), + volCut(-1), legatoDelay(-1), legatoTarget(0), rowDelay(0), volMax(0), + volBottom(0), + volTop(0), delayOrder(0), delayRow(0), retrigSpeed(0), diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp index d4b7c6ca0..0d616968b 100644 --- a/src/engine/fileOps/xm.cpp +++ b/src/engine/fileOps/xm.cpp @@ -1077,8 +1077,24 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { p->data[j][effectCol[k]++]=0x0c; p->data[j][effectCol[k]++]=(effectVal&15); break; + case 0xa: // vol slide up (fine) + volSlideStatus[k]=((effectVal&15)<<4)|0xf; + volSlideStatusChanged[k]=true; + if (hasNote || hasIns) { + volSlideStatusChanged[k]=true; + } + volSliding[k]=true; + break; + case 0xb: // vol slide down (fine) + volSlideStatus[k]=0xf0|(effectVal&15); + volSlideStatusChanged[k]=true; + if (hasNote || hasIns) { + volSlideStatusChanged[k]=true; + } + volSliding[k]=true; + break; case 0xc: - p->data[j][effectCol[k]++]=0xec; + p->data[j][effectCol[k]++]=0xdc; p->data[j][effectCol[k]++]=MAX(1,effectVal&15); break; case 0xd: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 5443fa25b..ac54f10cd 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -948,6 +948,20 @@ void DivEngine::processRow(int i, bool afterDelay) { clockDrift=0; subticks=0; break; + case 0xd8: // vol slide bottom + chan[i].volBottom=(effectVal<<8)|0xff; + if (chan[i].volBottom>chan[i].volMax) chan[i].volBottom=chan[i].volMax; + break; + case 0xd9: // vol slide top + chan[i].volTop=(effectVal<<8)|0xff; + if (chan[i].volTop>chan[i].volMax) chan[i].volTop=chan[i].volMax; + break; + case 0xdc: // delayed mute + if (effectVal>0 && (song.delayBehavior==2 || effectVal0) { curSubSong->arpLen=effectVal; @@ -1105,13 +1119,13 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xf8: // single volume ramp up chan[i].volSpeed=0; // add compat flag? - chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); + chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volTop); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xf9: // single volume ramp down chan[i].volSpeed=0; // add compat flag? - chan[i].volume=MAX(chan[i].volume-effectVal*256,0); + chan[i].volume=MAX(chan[i].volume-effectVal*256,chan[i].volBottom); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; @@ -1598,19 +1612,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].volSpeed!=0) { chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); chan[i].volume+=chan[i].volSpeed; - if (chan[i].volume>chan[i].volMax) { - chan[i].volume=chan[i].volMax; + if (chan[i].volume>chan[i].volTop) { + chan[i].volume=chan[i].volTop; chan[i].volSpeed=0; dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); - } else if (chan[i].volume<0) { + } else if (chan[i].volume>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -1628,7 +1642,6 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].panSpeed!=0) { int newPanL=chan[i].panL; int newPanR=chan[i].panR; - logV("panSpeed: %d",chan[i].panSpeed); if (chan[i].panSpeed>0) { // right if (newPanR>=0xff) { newPanL-=chan[i].panSpeed; @@ -1798,6 +1811,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } } + // volume cut/mute + if (chan[i].volCut>0) { + if (--chan[i].volCut<1) { + chan[i].volume=0; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); + } + } + // arpeggio if (chan[i].resetArp) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index b9d9ae85d..fc484a6e2 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -478,11 +478,11 @@ const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_VOLUME, // D8 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // D9 GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_VOLUME, // DC GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_MISC, // DF