mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-24 05:25:12 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace
This commit is contained in:
commit
bcc85dee20
16 changed files with 457 additions and 75 deletions
BIN
demos/rule2.fur
Normal file
BIN
demos/rule2.fur
Normal file
Binary file not shown.
102
papers/format.md
102
papers/format.md
|
@ -32,6 +32,9 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
|||
|
||||
the format versions are:
|
||||
|
||||
- 111: Furnace dev111
|
||||
- 110: Furnace dev110
|
||||
- 109: Furnace dev109
|
||||
- 108: Furnace dev108
|
||||
- 107: Furnace dev107
|
||||
- 106: Furnace dev106
|
||||
|
@ -333,7 +336,8 @@ size | description
|
|||
1 | E1xy and E2xy stop on same note (>=100) or reserved
|
||||
1 | broken initial position of porta after arp (>=101) or reserved
|
||||
1 | SN periods under 8 are treated as 1 (>=108) or reserved
|
||||
6 | reserved
|
||||
1 | cut/delay effect policy (>=110) or reserved
|
||||
5 | reserved
|
||||
--- | **virtual tempo data**
|
||||
2 | virtual tempo numerator of first song (>=96) or reserved
|
||||
2 | virtual tempo denominator of first song (>=96) or reserved
|
||||
|
@ -868,6 +872,96 @@ size | description
|
|||
1 | K2 ramp
|
||||
1 | K1 slow
|
||||
1 | K2 slow
|
||||
--- | **SNES data** (>=109)
|
||||
1 | use envelope
|
||||
1 | gain mode
|
||||
1 | gain
|
||||
1 | attack
|
||||
1 | decay
|
||||
1 | sustain
|
||||
1 | release
|
||||
--- | **macro speeds/delays** (>=111)
|
||||
1 | volume macro speed
|
||||
1 | arp macro speed
|
||||
1 | duty macro speed
|
||||
1 | wave macro speed
|
||||
1 | pitch macro speed
|
||||
1 | extra 1 macro speed
|
||||
1 | extra 2 macro speed
|
||||
1 | extra 3 macro speed
|
||||
1 | alg macro speed
|
||||
1 | fb macro speed
|
||||
1 | fms macro speed
|
||||
1 | ams macro speed
|
||||
1 | left panning macro speed
|
||||
1 | right panning macro speed
|
||||
1 | phase reset macro speed
|
||||
1 | extra 4 macro speed
|
||||
1 | extra 5 macro speed
|
||||
1 | extra 6 macro speed
|
||||
1 | extra 7 macro speed
|
||||
1 | extra 8 macro speed
|
||||
1 | volume macro delay
|
||||
1 | arp macro delay
|
||||
1 | duty macro delay
|
||||
1 | wave macro delay
|
||||
1 | pitch macro delay
|
||||
1 | extra 1 macro delay
|
||||
1 | extra 2 macro delay
|
||||
1 | extra 3 macro delay
|
||||
1 | alg macro delay
|
||||
1 | fb macro delay
|
||||
1 | fms macro delay
|
||||
1 | ams macro delay
|
||||
1 | left panning macro delay
|
||||
1 | right panning macro delay
|
||||
1 | phase reset macro delay
|
||||
1 | extra 4 macro delay
|
||||
1 | extra 5 macro delay
|
||||
1 | extra 6 macro delay
|
||||
1 | extra 7 macro delay
|
||||
1 | extra 8 macro delay
|
||||
--- | **operator macro speeds/delay** × 4 (>=111)
|
||||
1 | AM macro speed
|
||||
1 | AR macro speed
|
||||
1 | DR macro speed
|
||||
1 | MULT macro speed
|
||||
1 | RR macro speed
|
||||
1 | SL macro speed
|
||||
1 | TL macro speed
|
||||
1 | DT2 macro speed
|
||||
1 | RS macro speed
|
||||
1 | DT macro speed
|
||||
1 | D2R macro speed
|
||||
1 | SSG-EG macro speed
|
||||
1 | DAM macro speed
|
||||
1 | DVB macro speed
|
||||
1 | EGT macro speed
|
||||
1 | KSL macro speed
|
||||
1 | SUS macro speed
|
||||
1 | VIB macro speed
|
||||
1 | WS macro speed
|
||||
1 | KSR macro speed
|
||||
1 | AM macro delay
|
||||
1 | AR macro delay
|
||||
1 | DR macro delay
|
||||
1 | MULT macro delay
|
||||
1 | RR macro delay
|
||||
1 | SL macro delay
|
||||
1 | TL macro delay
|
||||
1 | DT2 macro delay
|
||||
1 | RS macro delay
|
||||
1 | DT macro delay
|
||||
1 | D2R macro delay
|
||||
1 | SSG-EG macro delay
|
||||
1 | DAM macro delay
|
||||
1 | DVB macro delay
|
||||
1 | EGT macro delay
|
||||
1 | KSL macro delay
|
||||
1 | SUS macro delay
|
||||
1 | VIB macro delay
|
||||
1 | WS macro delay
|
||||
1 | KSR macro delay
|
||||
```
|
||||
|
||||
# wavetable
|
||||
|
@ -878,9 +972,9 @@ size | description
|
|||
4 | "WAVE" block ID
|
||||
4 | size of this block
|
||||
STR | wavetable name
|
||||
4 | wavetable size
|
||||
4 | wavetable min
|
||||
4 | wavetable max
|
||||
4 | wavetable width
|
||||
4 | reserved
|
||||
4 | wavetable height
|
||||
4?? | wavetable data
|
||||
```
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev109"
|
||||
#define DIV_ENGINE_VERSION 109
|
||||
#define DIV_VERSION "dev111"
|
||||
#define DIV_ENGINE_VERSION 111
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
|
|
@ -176,6 +176,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.e1e2StopOnSameNote=true;
|
||||
ds.brokenPortaArp=false;
|
||||
ds.snNoLowPeriods=true;
|
||||
ds.delayBehavior=0;
|
||||
|
||||
// 1.1 compat flags
|
||||
if (ds.version>24) {
|
||||
|
@ -1067,6 +1068,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<108) {
|
||||
ds.snNoLowPeriods=true;
|
||||
}
|
||||
if (ds.version<110) {
|
||||
ds.delayBehavior=1;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
@ -1484,7 +1488,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<6; i++) {
|
||||
if (ds.version>=110) {
|
||||
ds.delayBehavior=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<5; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
@ -1917,6 +1926,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
ds.noSlidesOnFirstTick=true;
|
||||
ds.rowResetsArpPos=true;
|
||||
ds.ignoreJumpAtEnd=false;
|
||||
ds.delayBehavior=0;
|
||||
|
||||
int insCount=31;
|
||||
bool bypassLimits=false;
|
||||
|
@ -2833,9 +2843,9 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
// TODO: <= or <?
|
||||
for (int k=0; k<=susTime; k++) {
|
||||
ins->std.volMacro.val[ins->std.volMacro.len]=lastVal;
|
||||
if (++ins->std.volMacro.len>=128) break;
|
||||
if (++ins->std.volMacro.len>=255) break;
|
||||
}
|
||||
if (ins->std.volMacro.len>=128) break;
|
||||
if (ins->std.volMacro.len>=255) break;
|
||||
} else if (m.val[j]==0xe9 || m.val[j]==0xea) { // volume slide
|
||||
if (++j>=64) break;
|
||||
signed char slideStep=m.val[j];
|
||||
|
@ -2854,16 +2864,16 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
ins->std.volMacro.val[ins->std.volMacro.len]=lastVal;
|
||||
if (++ins->std.volMacro.len>=128) break;
|
||||
if (++ins->std.volMacro.len>=255) break;
|
||||
}
|
||||
} else {
|
||||
// TODO: replace with upcoming macro speed
|
||||
for (int k=0; k<MAX(1,seqSpeed); k++) {
|
||||
ins->std.volMacro.val[ins->std.volMacro.len]=m.val[j];
|
||||
lastVal=m.val[j];
|
||||
if (++ins->std.volMacro.len>=128) break;
|
||||
if (++ins->std.volMacro.len>=255) break;
|
||||
}
|
||||
if (ins->std.volMacro.len>=128) break;
|
||||
if (ins->std.volMacro.len>=255) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2892,7 +2902,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ins->std.waveMacro.val[ins->std.waveMacro.len]=wave-10;
|
||||
ins->std.waveMacro.open=true;
|
||||
lastVal=wave;
|
||||
//if (++ins->std.arpMacro.len>=128) break;
|
||||
//if (++ins->std.arpMacro.len>=255) break;
|
||||
}
|
||||
} else if (fm.val[j]==0xe0) {
|
||||
if (++j>=64) break;
|
||||
|
@ -2922,8 +2932,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ins->std.waveMacro.val[ins->std.waveMacro.len]=lastVal-10;
|
||||
}
|
||||
ins->std.arpMacro.open=true;
|
||||
if (++ins->std.arpMacro.len>=128) break;
|
||||
if (++ins->std.waveMacro.len>=128) break;
|
||||
if (++ins->std.arpMacro.len>=255) break;
|
||||
if (++ins->std.waveMacro.len>=255) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2936,7 +2946,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
// vibrato
|
||||
for (int j=0; j<=vibDelay; j++) {
|
||||
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=0;
|
||||
if (++ins->std.pitchMacro.len>=128) break;
|
||||
if (++ins->std.pitchMacro.len>=255) break;
|
||||
}
|
||||
int vibPos=0;
|
||||
ins->std.pitchMacro.loop=ins->std.pitchMacro.len;
|
||||
|
@ -2944,19 +2954,19 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
vibPos+=vibSpeed;
|
||||
if (vibPos>vibDepth) vibPos=vibDepth;
|
||||
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
|
||||
if (++ins->std.pitchMacro.len>=128) break;
|
||||
if (++ins->std.pitchMacro.len>=255) break;
|
||||
} while (vibPos<vibDepth);
|
||||
do {
|
||||
vibPos-=vibSpeed;
|
||||
if (vibPos<-vibDepth) vibPos=-vibDepth;
|
||||
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
|
||||
if (++ins->std.pitchMacro.len>=128) break;
|
||||
if (++ins->std.pitchMacro.len>=255) break;
|
||||
} while (vibPos>-vibDepth);
|
||||
do {
|
||||
vibPos+=vibSpeed;
|
||||
if (vibPos>0) vibPos=0;
|
||||
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
|
||||
if (++ins->std.pitchMacro.len>=128) break;
|
||||
if (++ins->std.pitchMacro.len>=255) break;
|
||||
} while (vibPos<0);
|
||||
|
||||
ds.ins.push_back(ins);
|
||||
|
@ -3729,7 +3739,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeC(song.e1e2StopOnSameNote);
|
||||
w->writeC(song.brokenPortaArp);
|
||||
w->writeC(song.snNoLowPeriods);
|
||||
for (int i=0; i<6; i++) {
|
||||
w->writeC(song.delayBehavior);
|
||||
for (int i=0; i<5; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -564,6 +564,96 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
w->writeC(snes.s);
|
||||
w->writeC(snes.r);
|
||||
|
||||
// macro speed/delay
|
||||
w->writeC(std.volMacro.speed);
|
||||
w->writeC(std.arpMacro.speed);
|
||||
w->writeC(std.dutyMacro.speed);
|
||||
w->writeC(std.waveMacro.speed);
|
||||
w->writeC(std.pitchMacro.speed);
|
||||
w->writeC(std.ex1Macro.speed);
|
||||
w->writeC(std.ex2Macro.speed);
|
||||
w->writeC(std.ex3Macro.speed);
|
||||
w->writeC(std.algMacro.speed);
|
||||
w->writeC(std.fbMacro.speed);
|
||||
w->writeC(std.fmsMacro.speed);
|
||||
w->writeC(std.amsMacro.speed);
|
||||
w->writeC(std.panLMacro.speed);
|
||||
w->writeC(std.panRMacro.speed);
|
||||
w->writeC(std.phaseResetMacro.speed);
|
||||
w->writeC(std.ex4Macro.speed);
|
||||
w->writeC(std.ex5Macro.speed);
|
||||
w->writeC(std.ex6Macro.speed);
|
||||
w->writeC(std.ex7Macro.speed);
|
||||
w->writeC(std.ex8Macro.speed);
|
||||
|
||||
w->writeC(std.volMacro.delay);
|
||||
w->writeC(std.arpMacro.delay);
|
||||
w->writeC(std.dutyMacro.delay);
|
||||
w->writeC(std.waveMacro.delay);
|
||||
w->writeC(std.pitchMacro.delay);
|
||||
w->writeC(std.ex1Macro.delay);
|
||||
w->writeC(std.ex2Macro.delay);
|
||||
w->writeC(std.ex3Macro.delay);
|
||||
w->writeC(std.algMacro.delay);
|
||||
w->writeC(std.fbMacro.delay);
|
||||
w->writeC(std.fmsMacro.delay);
|
||||
w->writeC(std.amsMacro.delay);
|
||||
w->writeC(std.panLMacro.delay);
|
||||
w->writeC(std.panRMacro.delay);
|
||||
w->writeC(std.phaseResetMacro.delay);
|
||||
w->writeC(std.ex4Macro.delay);
|
||||
w->writeC(std.ex5Macro.delay);
|
||||
w->writeC(std.ex6Macro.delay);
|
||||
w->writeC(std.ex7Macro.delay);
|
||||
w->writeC(std.ex8Macro.delay);
|
||||
|
||||
// op macro speed/delay
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
|
||||
|
||||
w->writeC(op.amMacro.speed);
|
||||
w->writeC(op.arMacro.speed);
|
||||
w->writeC(op.drMacro.speed);
|
||||
w->writeC(op.multMacro.speed);
|
||||
w->writeC(op.rrMacro.speed);
|
||||
w->writeC(op.slMacro.speed);
|
||||
w->writeC(op.tlMacro.speed);
|
||||
w->writeC(op.dt2Macro.speed);
|
||||
w->writeC(op.rsMacro.speed);
|
||||
w->writeC(op.dtMacro.speed);
|
||||
w->writeC(op.d2rMacro.speed);
|
||||
w->writeC(op.ssgMacro.speed);
|
||||
w->writeC(op.damMacro.speed);
|
||||
w->writeC(op.dvbMacro.speed);
|
||||
w->writeC(op.egtMacro.speed);
|
||||
w->writeC(op.kslMacro.speed);
|
||||
w->writeC(op.susMacro.speed);
|
||||
w->writeC(op.vibMacro.speed);
|
||||
w->writeC(op.wsMacro.speed);
|
||||
w->writeC(op.ksrMacro.speed);
|
||||
|
||||
w->writeC(op.amMacro.delay);
|
||||
w->writeC(op.arMacro.delay);
|
||||
w->writeC(op.drMacro.delay);
|
||||
w->writeC(op.multMacro.delay);
|
||||
w->writeC(op.rrMacro.delay);
|
||||
w->writeC(op.slMacro.delay);
|
||||
w->writeC(op.tlMacro.delay);
|
||||
w->writeC(op.dt2Macro.delay);
|
||||
w->writeC(op.rsMacro.delay);
|
||||
w->writeC(op.dtMacro.delay);
|
||||
w->writeC(op.d2rMacro.delay);
|
||||
w->writeC(op.ssgMacro.delay);
|
||||
w->writeC(op.damMacro.delay);
|
||||
w->writeC(op.dvbMacro.delay);
|
||||
w->writeC(op.egtMacro.delay);
|
||||
w->writeC(op.kslMacro.delay);
|
||||
w->writeC(op.susMacro.delay);
|
||||
w->writeC(op.vibMacro.delay);
|
||||
w->writeC(op.wsMacro.delay);
|
||||
w->writeC(op.ksrMacro.delay);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
@ -960,15 +1050,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
// clear noise macro if PCE instrument and version<63
|
||||
if (version<63 && type==DIV_INS_PCE) {
|
||||
std.dutyMacro.len=0;
|
||||
std.dutyMacro.loop=-1;
|
||||
std.dutyMacro.rel=-1;
|
||||
std.dutyMacro.loop=255;
|
||||
std.dutyMacro.rel=255;
|
||||
}
|
||||
|
||||
// clear wave macro if OPLL instrument and version<70
|
||||
if (version<70 && type==DIV_INS_OPLL) {
|
||||
std.waveMacro.len=0;
|
||||
std.waveMacro.loop=-1;
|
||||
std.waveMacro.rel=-1;
|
||||
std.waveMacro.loop=255;
|
||||
std.waveMacro.rel=255;
|
||||
}
|
||||
|
||||
// sample map
|
||||
|
@ -1160,6 +1250,98 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
snes.r=reader.readC();
|
||||
}
|
||||
|
||||
// macro speed/delay
|
||||
if (version>=111) {
|
||||
std.volMacro.speed=reader.readC();
|
||||
std.arpMacro.speed=reader.readC();
|
||||
std.dutyMacro.speed=reader.readC();
|
||||
std.waveMacro.speed=reader.readC();
|
||||
std.pitchMacro.speed=reader.readC();
|
||||
std.ex1Macro.speed=reader.readC();
|
||||
std.ex2Macro.speed=reader.readC();
|
||||
std.ex3Macro.speed=reader.readC();
|
||||
std.algMacro.speed=reader.readC();
|
||||
std.fbMacro.speed=reader.readC();
|
||||
std.fmsMacro.speed=reader.readC();
|
||||
std.amsMacro.speed=reader.readC();
|
||||
std.panLMacro.speed=reader.readC();
|
||||
std.panRMacro.speed=reader.readC();
|
||||
std.phaseResetMacro.speed=reader.readC();
|
||||
std.ex4Macro.speed=reader.readC();
|
||||
std.ex5Macro.speed=reader.readC();
|
||||
std.ex6Macro.speed=reader.readC();
|
||||
std.ex7Macro.speed=reader.readC();
|
||||
std.ex8Macro.speed=reader.readC();
|
||||
|
||||
std.volMacro.delay=reader.readC();
|
||||
std.arpMacro.delay=reader.readC();
|
||||
std.dutyMacro.delay=reader.readC();
|
||||
std.waveMacro.delay=reader.readC();
|
||||
std.pitchMacro.delay=reader.readC();
|
||||
std.ex1Macro.delay=reader.readC();
|
||||
std.ex2Macro.delay=reader.readC();
|
||||
std.ex3Macro.delay=reader.readC();
|
||||
std.algMacro.delay=reader.readC();
|
||||
std.fbMacro.delay=reader.readC();
|
||||
std.fmsMacro.delay=reader.readC();
|
||||
std.amsMacro.delay=reader.readC();
|
||||
std.panLMacro.delay=reader.readC();
|
||||
std.panRMacro.delay=reader.readC();
|
||||
std.phaseResetMacro.delay=reader.readC();
|
||||
std.ex4Macro.delay=reader.readC();
|
||||
std.ex5Macro.delay=reader.readC();
|
||||
std.ex6Macro.delay=reader.readC();
|
||||
std.ex7Macro.delay=reader.readC();
|
||||
std.ex8Macro.delay=reader.readC();
|
||||
|
||||
// op macro speed/delay
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
|
||||
|
||||
op.amMacro.speed=reader.readC();
|
||||
op.arMacro.speed=reader.readC();
|
||||
op.drMacro.speed=reader.readC();
|
||||
op.multMacro.speed=reader.readC();
|
||||
op.rrMacro.speed=reader.readC();
|
||||
op.slMacro.speed=reader.readC();
|
||||
op.tlMacro.speed=reader.readC();
|
||||
op.dt2Macro.speed=reader.readC();
|
||||
op.rsMacro.speed=reader.readC();
|
||||
op.dtMacro.speed=reader.readC();
|
||||
op.d2rMacro.speed=reader.readC();
|
||||
op.ssgMacro.speed=reader.readC();
|
||||
op.damMacro.speed=reader.readC();
|
||||
op.dvbMacro.speed=reader.readC();
|
||||
op.egtMacro.speed=reader.readC();
|
||||
op.kslMacro.speed=reader.readC();
|
||||
op.susMacro.speed=reader.readC();
|
||||
op.vibMacro.speed=reader.readC();
|
||||
op.wsMacro.speed=reader.readC();
|
||||
op.ksrMacro.speed=reader.readC();
|
||||
|
||||
op.amMacro.delay=reader.readC();
|
||||
op.arMacro.delay=reader.readC();
|
||||
op.drMacro.delay=reader.readC();
|
||||
op.multMacro.delay=reader.readC();
|
||||
op.rrMacro.delay=reader.readC();
|
||||
op.slMacro.delay=reader.readC();
|
||||
op.tlMacro.delay=reader.readC();
|
||||
op.dt2Macro.delay=reader.readC();
|
||||
op.rsMacro.delay=reader.readC();
|
||||
op.dtMacro.delay=reader.readC();
|
||||
op.d2rMacro.delay=reader.readC();
|
||||
op.ssgMacro.delay=reader.readC();
|
||||
op.damMacro.delay=reader.readC();
|
||||
op.dvbMacro.delay=reader.readC();
|
||||
op.egtMacro.delay=reader.readC();
|
||||
op.kslMacro.delay=reader.readC();
|
||||
op.susMacro.delay=reader.readC();
|
||||
op.vibMacro.delay=reader.readC();
|
||||
op.wsMacro.delay=reader.readC();
|
||||
op.ksrMacro.delay=reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,21 +165,20 @@ struct DivInstrumentMacro {
|
|||
int val[256];
|
||||
unsigned int mode;
|
||||
bool open;
|
||||
unsigned char len;
|
||||
signed char loop;
|
||||
signed char rel;
|
||||
unsigned char len, delay, speed, loop, rel;
|
||||
|
||||
// the following variables are used by the GUI and not saved in the file
|
||||
int vScroll, vZoom;
|
||||
|
||||
|
||||
explicit DivInstrumentMacro(const String& n, bool initOpen=false):
|
||||
name(n),
|
||||
mode(0),
|
||||
open(initOpen),
|
||||
len(0),
|
||||
loop(-1),
|
||||
rel(-1),
|
||||
delay(0),
|
||||
speed(1),
|
||||
loop(255),
|
||||
rel(255),
|
||||
vScroll(0),
|
||||
vZoom(-1) {
|
||||
memset(val,0,256*sizeof(int));
|
||||
|
|
|
@ -32,6 +32,18 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
|
|||
had=false;
|
||||
return;
|
||||
}
|
||||
if (delay>0) {
|
||||
delay--;
|
||||
return;
|
||||
}
|
||||
if (began && source.delay>0) {
|
||||
delay=source.delay;
|
||||
} else {
|
||||
delay=source.speed-1;
|
||||
}
|
||||
if (began) {
|
||||
began=false;
|
||||
}
|
||||
if (finished) {
|
||||
finished=false;
|
||||
}
|
||||
|
@ -43,15 +55,15 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
|
|||
if (has) {
|
||||
lastPos=pos;
|
||||
val=source.val[pos++];
|
||||
if (source.rel>=0 && pos>source.rel && !released) {
|
||||
if (source.loop<source.len && source.loop>=0 && source.loop<source.rel) {
|
||||
if (pos>source.rel && !released) {
|
||||
if (source.loop<source.len && source.loop<source.rel) {
|
||||
pos=source.loop;
|
||||
} else {
|
||||
pos--;
|
||||
}
|
||||
}
|
||||
if (pos>=source.len) {
|
||||
if (source.loop<source.len && source.loop>=0 && (source.loop>=source.rel || source.rel>=source.len)) {
|
||||
if (source.loop<source.len && (source.loop>=source.rel || source.rel>=source.len)) {
|
||||
pos=source.loop;
|
||||
} else if (linger) {
|
||||
pos--;
|
||||
|
@ -240,7 +252,7 @@ void DivMacroInt::init(DivInstrument* which) {
|
|||
for (size_t i=0; i<macroListLen; i++) {
|
||||
if (macroSource[i]!=NULL) {
|
||||
macroList[i]->prepare(*macroSource[i],e);
|
||||
hasRelease=(macroSource[i]->rel>=0 && macroSource[i]->rel<macroSource[i]->len);
|
||||
hasRelease=(macroSource[i]->rel<macroSource[i]->len);
|
||||
} else {
|
||||
hasRelease=false;
|
||||
}
|
||||
|
|
|
@ -25,21 +25,24 @@
|
|||
class DivEngine;
|
||||
|
||||
struct DivMacroStruct {
|
||||
int pos, lastPos;
|
||||
int pos, lastPos, delay;
|
||||
int val;
|
||||
bool has, had, actualHad, finished, will, linger;
|
||||
bool has, had, actualHad, finished, will, linger, began;
|
||||
unsigned int mode;
|
||||
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
|
||||
void init() {
|
||||
pos=lastPos=mode=0;
|
||||
pos=lastPos=mode=delay=0;
|
||||
has=had=actualHad=will=false;
|
||||
linger=false;
|
||||
began=true;
|
||||
// TODO: test whether this breaks anything?
|
||||
val=0;
|
||||
}
|
||||
void prepare(DivInstrumentMacro& source, DivEngine* e);
|
||||
DivMacroStruct():
|
||||
pos(0),
|
||||
lastPos(0),
|
||||
delay(0),
|
||||
val(0),
|
||||
has(false),
|
||||
had(false),
|
||||
|
@ -47,6 +50,7 @@ struct DivMacroStruct {
|
|||
finished(false),
|
||||
will(false),
|
||||
linger(false),
|
||||
began(false),
|
||||
mode(0) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -263,6 +263,23 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (i>=5 && chan[i].furnaceDac) {
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=parent->calcBaseFreq(1,1,chan[i].std.arp.val,false);
|
||||
} else {
|
||||
chan[i].baseFreq=parent->calcBaseFreq(1,1,chan[i].note+(signed char)chan[i].std.arp.val,false);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=parent->calcBaseFreq(1,1,chan[i].note,false);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
|
@ -278,6 +295,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i>=5 && chan[i].furnaceDac) {
|
||||
if (chan[i].std.panL.had) {
|
||||
|
|
|
@ -561,6 +561,11 @@ void DivPlatformGenesisExt::forceIns() {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (extMode && softPCM && chan[7].active) { // CSM
|
||||
chan[7].insChanged=true;
|
||||
chan[7].freqChanged=true;
|
||||
chan[7].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformGenesisExt::getChanState(int ch) {
|
||||
|
|
|
@ -361,7 +361,9 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
break;
|
||||
case 0xed: // delay
|
||||
if (effectVal!=0) {
|
||||
if (effectVal<=nextSpeed) {
|
||||
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<nextSpeed);
|
||||
if (song.delayBehavior==2) comparison=true;
|
||||
if (comparison) {
|
||||
chan[i].rowDelay=effectVal+1;
|
||||
chan[i].delayOrder=whatOrder;
|
||||
chan[i].delayRow=whatRow;
|
||||
|
@ -372,6 +374,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
}
|
||||
returnAfterPre=true;
|
||||
} else {
|
||||
logV("higher than nextSpeed! %d>%d",effectVal,nextSpeed);
|
||||
chan[i].delayLocked=false;
|
||||
}
|
||||
}
|
||||
|
@ -379,6 +382,8 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
}
|
||||
}
|
||||
if (returnAfterPre) return;
|
||||
} else {
|
||||
logV("honoring delay at position %d",whatRow);
|
||||
}
|
||||
|
||||
if (chan[i].delayLocked) return;
|
||||
|
@ -704,7 +709,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_BANK,i,effectVal));
|
||||
break;
|
||||
case 0xec: // delayed note cut
|
||||
if (effectVal>0 && effectVal<nextSpeed) {
|
||||
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
|
||||
chan[i].cut=effectVal+1;
|
||||
}
|
||||
break;
|
||||
|
@ -900,7 +905,9 @@ void DivEngine::nextRow() {
|
|||
prevRow=curRow;
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (song.delayBehavior!=2) {
|
||||
chan[i].rowDelay=0;
|
||||
}
|
||||
processRow(i,false);
|
||||
}
|
||||
|
||||
|
|
|
@ -463,6 +463,11 @@ struct DivSong {
|
|||
// 1: fake reset on loop
|
||||
// 2: don't do anything on loop
|
||||
unsigned char loopModality;
|
||||
// cut/delay effect behavior
|
||||
// 0: strict (don't allow value higher than or equal to speed)
|
||||
// 1: broken (don't allow value higher than speed)
|
||||
// 2: lax (allow value higher than speed)
|
||||
unsigned char delayBehavior;
|
||||
bool properNoiseLayout;
|
||||
bool waveDutyIsVol;
|
||||
bool resetMacroOnPorta;
|
||||
|
@ -565,6 +570,7 @@ struct DivSong {
|
|||
linearPitch(2),
|
||||
pitchSlideSpeed(4),
|
||||
loopModality(0),
|
||||
delayBehavior(2),
|
||||
properNoiseLayout(true),
|
||||
waveDutyIsVol(false),
|
||||
resetMacroOnPorta(false),
|
||||
|
|
|
@ -193,6 +193,26 @@ void FurnaceGUI::drawCompatFlags() {
|
|||
ImGui::SetTooltip("select to not reset channels on loop.");
|
||||
}
|
||||
|
||||
ImGui::Text("Cut/delay effect policy:");
|
||||
if (ImGui::RadioButton("Strict",e->song.delayBehavior==0)) {
|
||||
e->song.delayBehavior=0;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("only when time is less than speed (like DefleMask/ProTracker)");
|
||||
}
|
||||
if (ImGui::RadioButton("Strict (old)",e->song.delayBehavior==1)) {
|
||||
e->song.delayBehavior=1;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("only when time is less than or equal to speed (original buggy behavior)");
|
||||
}
|
||||
if (ImGui::RadioButton("Lax",e->song.delayBehavior==2)) {
|
||||
e->song.delayBehavior=2;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("no checks (like FamiTracker)");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions.");
|
||||
|
|
|
@ -276,13 +276,13 @@ void FurnaceGUI::decodeMMLStrW(String& source, int* macro, int& macroLen, int ma
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) {
|
||||
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel) {
|
||||
int buf=0;
|
||||
bool negaBuf=false;
|
||||
bool hasVal=false;
|
||||
macroLen=0;
|
||||
macroLoop=-1;
|
||||
macroRel=-1;
|
||||
macroLoop=255;
|
||||
macroRel=255;
|
||||
for (char& i: source) {
|
||||
switch (i) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
|
@ -318,7 +318,7 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
|
|||
macroLen++;
|
||||
buf=0;
|
||||
}
|
||||
if (macroLoop==-1) {
|
||||
if (macroLoop==255) {
|
||||
macroLoop=macroLen;
|
||||
}
|
||||
break;
|
||||
|
@ -332,14 +332,14 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
|
|||
macroLen++;
|
||||
buf=0;
|
||||
}
|
||||
if (macroRel==-1) {
|
||||
if (macroRel==255) {
|
||||
macroRel=macroLen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (macroLen>=128) break;
|
||||
if (macroLen>=255) break;
|
||||
}
|
||||
if (hasVal && macroLen<128) {
|
||||
if (hasVal && macroLen<255) {
|
||||
hasVal=false;
|
||||
macro[macroLen]=negaBuf?-buf:buf;
|
||||
negaBuf=false;
|
||||
|
|
|
@ -1409,7 +1409,7 @@ class FurnaceGUI {
|
|||
|
||||
ImVec2 macroLoopDragStart;
|
||||
ImVec2 macroLoopDragAreaSize;
|
||||
signed char* macroLoopDragTarget;
|
||||
unsigned char* macroLoopDragTarget;
|
||||
int macroLoopDragLen;
|
||||
bool macroLoopDragActive;
|
||||
|
||||
|
@ -1690,7 +1690,7 @@ class FurnaceGUI {
|
|||
void initSystemPresets();
|
||||
|
||||
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false);
|
||||
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel);
|
||||
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel);
|
||||
void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false);
|
||||
|
||||
String encodeKeyMap(std::map<int,int>& map);
|
||||
|
|
|
@ -1215,14 +1215,14 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
}
|
||||
ImGui::TableNextColumn();
|
||||
float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace;
|
||||
int totalFit=MIN(128,availableWidth/MAX(1,macroPointSize*dpiScale));
|
||||
if (macroDragScroll>128-totalFit) {
|
||||
macroDragScroll=128-totalFit;
|
||||
int totalFit=MIN(255,availableWidth/MAX(1,macroPointSize*dpiScale));
|
||||
if (macroDragScroll>255-totalFit) {
|
||||
macroDragScroll=255-totalFit;
|
||||
}
|
||||
ImGui::SetNextItemWidth(availableWidth);
|
||||
if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) {
|
||||
if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) {
|
||||
if (macroDragScroll<0) macroDragScroll=0;
|
||||
if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit;
|
||||
if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit;
|
||||
}
|
||||
|
||||
// draw macros
|
||||
|
@ -1239,8 +1239,32 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
}
|
||||
if (i.macro->open) {
|
||||
ImGui::SetNextItemWidth(lenAvail);
|
||||
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,&i.macro->len,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (i.macro->len>128) i.macro->len=128;
|
||||
int macroLen=i.macro->len;
|
||||
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (macroLen<0) macroLen=0;
|
||||
if (macroLen>255) macroLen=255;
|
||||
i.macro->len=macroLen;
|
||||
}
|
||||
if (ImGui::Button(ICON_FA_BAR_CHART "##IMacroType")) {
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Coming soon!");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_ELLIPSIS_H "##IMacroSet");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delay/Step Length");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("IMacroSetP",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
if (ImGui::InputScalar("Step Length (ticks)##IMacroSpeed",ImGuiDataType_U8,&i.macro->speed,&_ONE,&_THREE)) {
|
||||
if (i.macro->speed<1) i.macro->speed=1;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
if (ImGui::InputScalar("Delay##IMacroDelay",ImGuiDataType_U8,&i.macro->delay,&_ONE,&_THREE)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
// do not change this!
|
||||
// anything other than a checkbox will look ugly!
|
||||
|
@ -1267,7 +1291,7 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
if (j+macroDragScroll>=i.macro->len || (j+macroDragScroll>i.macro->rel && i.macro->loop<i.macro->rel)) {
|
||||
loopIndicator[j]=0;
|
||||
} else {
|
||||
loopIndicator[j]=((i.macro->loop!=-1 && (j+macroDragScroll)>=i.macro->loop))|((i.macro->rel!=-1 && (j+macroDragScroll)==i.macro->rel)<<1);
|
||||
loopIndicator[j]=((i.macro->loop!=255 && (j+macroDragScroll)>=i.macro->loop))|((i.macro->rel!=255 && (j+macroDragScroll)==i.macro->rel)<<1);
|
||||
}
|
||||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
|
||||
|
@ -1414,9 +1438,9 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
}
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) {
|
||||
i.macro->rel=-1;
|
||||
i.macro->rel=255;
|
||||
} else {
|
||||
i.macro->loop=-1;
|
||||
i.macro->loop=255;
|
||||
}
|
||||
}
|
||||
ImGui::SetNextItemWidth(availableWidth);
|
||||
|
@ -1437,9 +1461,9 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(availableWidth);
|
||||
if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) {
|
||||
if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) {
|
||||
if (macroDragScroll<0) macroDragScroll=0;
|
||||
if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit;
|
||||
if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit;
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
@ -4311,8 +4335,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("clear")) {
|
||||
lastMacroDesc.macro->len=0;
|
||||
lastMacroDesc.macro->loop=-1;
|
||||
lastMacroDesc.macro->rel=-1;
|
||||
lastMacroDesc.macro->loop=255;
|
||||
lastMacroDesc.macro->rel=255;
|
||||
for (int i=0; i<256; i++) {
|
||||
lastMacroDesc.macro->val[i]=0;
|
||||
}
|
||||
|
@ -4341,15 +4365,15 @@ void FurnaceGUI::drawInsEdit() {
|
|||
lastMacroDesc.macro->val[i]=val;
|
||||
}
|
||||
|
||||
if (lastMacroDesc.macro->loop>=0 && lastMacroDesc.macro->loop<lastMacroDesc.macro->len) {
|
||||
if (lastMacroDesc.macro->loop<lastMacroDesc.macro->len) {
|
||||
lastMacroDesc.macro->loop+=macroOffX;
|
||||
} else {
|
||||
lastMacroDesc.macro->loop=-1;
|
||||
lastMacroDesc.macro->loop=255;
|
||||
}
|
||||
if ((lastMacroDesc.macro->rel+macroOffX)>=0 && (lastMacroDesc.macro->rel+macroOffX)<lastMacroDesc.macro->len) {
|
||||
lastMacroDesc.macro->rel+=macroOffX;
|
||||
} else {
|
||||
lastMacroDesc.macro->rel=-1;
|
||||
lastMacroDesc.macro->rel=255;
|
||||
}
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
|
Loading…
Reference in a new issue