mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-25 14:05:12 +00:00
implement panning slide and panbrello effects
This commit is contained in:
parent
f09c30a383
commit
e6966b68a9
4 changed files with 111 additions and 0 deletions
|
@ -74,6 +74,13 @@ not all chips support these effects.
|
|||
- `00` is left.
|
||||
- `80` is center.
|
||||
- `FF` is right.
|
||||
- ---
|
||||
- `83xy`: **Panning slide.**
|
||||
- if `y` is 0 then this pans to the left by `x` each tick.
|
||||
- if `x` is 0 then this pans to the right by `y` each tick.
|
||||
- be noted that panning macros override this effect.
|
||||
- `84xy`: **Panbrello.** makes panning oscillate. `x` is the speed, while `y` is the depth.
|
||||
- be noted that panning macros override this effect.
|
||||
|
||||
## time
|
||||
|
||||
|
|
|
@ -83,6 +83,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
return _("81xx: Set panning (left channel)");
|
||||
case 0x82:
|
||||
return _("82xx: Set panning (right channel)");
|
||||
case 0x83:
|
||||
return _("83xy: Panning slide (x0: left; 0y: right)");
|
||||
case 0x84:
|
||||
return _("84xy: Panbrello (x: speed; y: depth)");
|
||||
case 0x88:
|
||||
return _("88xy: Set panning (rear channels; x: left; y: right)");
|
||||
break;
|
||||
|
|
|
@ -137,6 +137,7 @@ struct DivChannelState {
|
|||
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoShape, vibratoFine;
|
||||
int tremoloDepth, tremoloRate, tremoloPos;
|
||||
int panDepth, panRate, panPos, panSpeed;
|
||||
int sampleOff;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing;
|
||||
|
@ -174,6 +175,10 @@ struct DivChannelState {
|
|||
tremoloDepth(0),
|
||||
tremoloRate(0),
|
||||
tremoloPos(0),
|
||||
panDepth(0),
|
||||
panRate(0),
|
||||
panPos(0),
|
||||
panSpeed(0),
|
||||
sampleOff(0),
|
||||
arp(0),
|
||||
arpStage(-1),
|
||||
|
|
|
@ -686,6 +686,31 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].panR=effectVal;
|
||||
panChanged=true;
|
||||
break;
|
||||
case 0x83: // pan slide
|
||||
if (effectVal!=0) {
|
||||
if ((effectVal&15)!=0) {
|
||||
chan[i].panSpeed=(effectVal&15);
|
||||
} else {
|
||||
chan[i].panSpeed=(effectVal>>4);
|
||||
}
|
||||
// panbrello and slides are incompatible
|
||||
chan[i].panDepth=0;
|
||||
chan[i].panRate=0;
|
||||
chan[i].panPos=0;
|
||||
} else {
|
||||
chan[i].panSpeed=0;
|
||||
}
|
||||
break;
|
||||
case 0x84: // panbrello
|
||||
if (chan[i].panDepth==0) {
|
||||
chan[i].panPos=0;
|
||||
}
|
||||
chan[i].panDepth=effectVal&15;
|
||||
chan[i].panRate=effectVal>>4;
|
||||
if (chan[i].panDepth!=0) {
|
||||
chan[i].panSpeed=0;
|
||||
}
|
||||
break;
|
||||
case 0x88: // panning rear (split 4-bit)
|
||||
chan[i].panRL=(effectVal>>4)|(effectVal&0xf0);
|
||||
chan[i].panRR=(effectVal&15)|((effectVal&15)<<4);
|
||||
|
@ -1536,11 +1561,14 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
// process stuff
|
||||
if (!shallStop) for (int i=0; i<chans; i++) {
|
||||
// delay effects
|
||||
if (chan[i].rowDelay>0) {
|
||||
if (--chan[i].rowDelay==0) {
|
||||
processRow(i,true);
|
||||
}
|
||||
}
|
||||
|
||||
// retrigger
|
||||
if (chan[i].retrigSpeed) {
|
||||
if (--chan[i].retrigTick<0) {
|
||||
chan[i].retrigTick=chan[i].retrigSpeed-1;
|
||||
|
@ -1548,6 +1576,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
keyHit[i]=true;
|
||||
}
|
||||
}
|
||||
|
||||
// volume slides and tremolo
|
||||
if (!song.noSlidesOnFirstTick || !firstTick) {
|
||||
if (chan[i].volSpeed!=0) {
|
||||
chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8);
|
||||
|
@ -1577,6 +1607,63 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,MAX(0,chan[i].volume-(tremTable[chan[i].tremoloPos]*chan[i].tremoloDepth))>>8));
|
||||
}
|
||||
}
|
||||
|
||||
// panning slides
|
||||
// TODO: UNTESTED... can't test right now
|
||||
if (chan[i].panSpeed!=0) {
|
||||
int newPanL=chan[i].panL;
|
||||
int newPanR=chan[i].panR;
|
||||
if (chan[i].panSpeed>0) { // right
|
||||
if (newPanR>=0xff) {
|
||||
newPanL-=chan[i].panSpeed;
|
||||
} else {
|
||||
newPanR+=chan[i].panSpeed;
|
||||
}
|
||||
} else { // left
|
||||
if (newPanL>=0xff) {
|
||||
newPanR+=chan[i].panSpeed;
|
||||
} else {
|
||||
newPanL-=chan[i].panSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (newPanL<0) newPanL=0;
|
||||
if (newPanL>0xff) newPanL=0xff;
|
||||
if (newPanR<0) newPanR=0;
|
||||
if (newPanR>0xff) newPanR=0xff;
|
||||
|
||||
chan[i].panL=newPanL;
|
||||
chan[i].panR=newPanR;
|
||||
|
||||
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR));
|
||||
} else if (chan[i].panDepth>0) {
|
||||
chan[i].panPos+=chan[i].panRate;
|
||||
chan[i].panPos&=255;
|
||||
|
||||
// calculate...
|
||||
switch (chan[i].panPos&0xe0) {
|
||||
case 0: // center -> right
|
||||
chan[i].panL=0xff-(chan[i].panPos<<2);
|
||||
chan[i].panR=0xff;
|
||||
break;
|
||||
case 0x40: // right -> center
|
||||
chan[i].panL=chan[i].panPos<<2;
|
||||
chan[i].panR=0xff;
|
||||
break;
|
||||
case 0x80: // center -> left
|
||||
chan[i].panL=0xff;
|
||||
chan[i].panR=0xff-(chan[i].panPos<<2);
|
||||
break;
|
||||
case 0xc0: // left -> center
|
||||
chan[i].panL=0xff;
|
||||
chan[i].panR=chan[i].panPos<<2;
|
||||
break;
|
||||
}
|
||||
|
||||
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR));
|
||||
}
|
||||
|
||||
// vibrato
|
||||
if (chan[i].vibratoDepth>0) {
|
||||
chan[i].vibratoPos+=chan[i].vibratoRate;
|
||||
while (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
|
||||
|
@ -1632,6 +1719,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibratoOut*chan[i].vibratoFine)>>4)/15)));
|
||||
}
|
||||
|
||||
// delayed legato
|
||||
if (chan[i].legatoDelay>0) {
|
||||
if (--chan[i].legatoDelay<1) {
|
||||
chan[i].note+=chan[i].legatoTarget;
|
||||
|
@ -1641,6 +1730,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
chan[i].legatoTarget=0;
|
||||
}
|
||||
}
|
||||
|
||||
// portamento and pitch slides
|
||||
if (!song.noSlidesOnFirstTick || !firstTick) {
|
||||
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
|
||||
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
|
||||
|
@ -1654,6 +1745,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note cut
|
||||
if (chan[i].cut>0) {
|
||||
if (--chan[i].cut<1) {
|
||||
if (chan[i].cutType==2) {
|
||||
|
@ -1688,6 +1781,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// arpeggio
|
||||
if (chan[i].resetArp) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
|
||||
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
|
||||
|
|
Loading…
Reference in a new issue