mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-16 01:35:07 +00:00
FC loader: preset waveforms, vibrato and stuff
This commit is contained in:
parent
15b4294532
commit
4663534fa3
1 changed files with 175 additions and 14 deletions
|
@ -2262,6 +2262,95 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char fcXORTriangle[32]={
|
||||||
|
0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8,
|
||||||
|
0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char fcCustom1[32]={
|
||||||
|
0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x61, 0x58, 0x53, 0x4d, 0x2c, 0x20, 0x18, 0x12,
|
||||||
|
0x04, 0xdb, 0xd3, 0xcd, 0xc6, 0xbc, 0xb5, 0xae, 0xa8, 0xa3, 0x9d, 0x99, 0x93, 0x8e, 0x8b, 0x8a
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char fcCustom2[32]={
|
||||||
|
0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x5b, 0x4b, 0x43, 0x37, 0x2c, 0x20, 0x18, 0x12,
|
||||||
|
0x04, 0xf8, 0xe8, 0xdb, 0xcf, 0xc6, 0xbe, 0xb0, 0xa8, 0xa4, 0x9e, 0x9a, 0x95, 0x94, 0x8d, 0x83
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char fcTinyTriangle[16]={
|
||||||
|
0x00, 0x00, 0x40, 0x60, 0x7f, 0x60, 0x40, 0x20, 0x00, 0xe0, 0xc0, 0xa0, 0x80, 0xa0, 0xc0, 0xe0
|
||||||
|
};
|
||||||
|
|
||||||
|
void generateFCPresetWave(int index, DivWavetable* wave) {
|
||||||
|
wave->max=255;
|
||||||
|
wave->len=32;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 0x00: case 0x01: case 0x02: case 0x03:
|
||||||
|
case 0x04: case 0x05: case 0x06: case 0x07:
|
||||||
|
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
||||||
|
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
||||||
|
// XOR triangle
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=(unsigned char)((fcXORTriangle[i]^0x80)^(((index+15)<i)?0x87:0x00));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||||
|
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||||
|
case 0x18: case 0x19: case 0x1a: case 0x1b:
|
||||||
|
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||||
|
// pulse
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=(index>i)?0x01:0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x20: case 0x21: case 0x22: case 0x23:
|
||||||
|
case 0x24: case 0x25: case 0x26: case 0x27:
|
||||||
|
// tiny pulse
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=((index-0x18)>(i&15))?0x01:0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x28:
|
||||||
|
case 0x2e:
|
||||||
|
// saw
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=i<<3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x29:
|
||||||
|
case 0x2f:
|
||||||
|
// tiny saw
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=(i<<4)&0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x2a:
|
||||||
|
// custom 1
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=fcCustom1[i]^0x80;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x2b:
|
||||||
|
// custom 2
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=fcCustom2[i]^0x80;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x2c: case 0x2d:
|
||||||
|
// tiny triangle
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=fcTinyTriangle[i&15]^0x80;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
wave->data[i]=i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
struct InvalidHeaderException {};
|
struct InvalidHeaderException {};
|
||||||
bool success=false;
|
bool success=false;
|
||||||
|
@ -2301,10 +2390,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
DivSong ds;
|
DivSong ds;
|
||||||
ds.tuning=436.0;
|
ds.tuning=436.0;
|
||||||
ds.version=DIV_VERSION_FC;
|
ds.version=DIV_VERSION_FC;
|
||||||
//ds.linearPitch=0;
|
ds.linearPitch=0;
|
||||||
|
ds.pitchMacroIsLinear=false;
|
||||||
//ds.noSlidesOnFirstTick=true;
|
//ds.noSlidesOnFirstTick=true;
|
||||||
//ds.rowResetsArpPos=true;
|
//ds.rowResetsArpPos=true;
|
||||||
//ds.ignoreJumpAtEnd=false;
|
ds.ignoreJumpAtEnd=false;
|
||||||
|
|
||||||
// load here
|
// load here
|
||||||
if (!reader.seek(0,SEEK_SET)) {
|
if (!reader.seek(0,SEEK_SET)) {
|
||||||
|
@ -2521,12 +2611,16 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
delete[] waveArray;
|
delete[] waveArray;
|
||||||
} else {
|
} else {
|
||||||
logV("empty wave %d",i);
|
logV("empty wave %d",i);
|
||||||
w->len=32;
|
generateFCPresetWave(i,w);
|
||||||
for (int i=0; i<32; i++) {
|
|
||||||
w->data[i]=(i*255)/31;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ds.wave.push_back(w);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// generate preset waves
|
||||||
|
for (int i=0; i<48; i++) {
|
||||||
|
DivWavetable* w=new DivWavetable;
|
||||||
|
generateFCPresetWave(i,w);
|
||||||
ds.wave.push_back(w);
|
ds.wave.push_back(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2542,6 +2636,14 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
ds.subsong[0]->speed1=3;
|
ds.subsong[0]->speed1=3;
|
||||||
ds.subsong[0]->speed2=3;
|
ds.subsong[0]->speed2=3;
|
||||||
|
|
||||||
|
int lastIns[4];
|
||||||
|
int lastNote[4];
|
||||||
|
signed char lastTranspose[4];
|
||||||
|
|
||||||
|
memset(lastIns,-1,4*sizeof(int));
|
||||||
|
memset(lastNote,-1,4*sizeof(int));
|
||||||
|
memset(lastTranspose,0,4);
|
||||||
|
|
||||||
for (unsigned int i=0; i<seqLen; i++) {
|
for (unsigned int i=0; i<seqLen; i++) {
|
||||||
for (int j=0; j<4; j++) {
|
for (int j=0; j<4; j++) {
|
||||||
ds.subsong[0]->orders.ord[j][i]=i;
|
ds.subsong[0]->orders.ord[j][i]=i;
|
||||||
|
@ -2559,6 +2661,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
for (int k=0; k<32; k++) {
|
for (int k=0; k<32; k++) {
|
||||||
FCPattern& fp=pat[seq[i].pat[j]];
|
FCPattern& fp=pat[seq[i].pat[j]];
|
||||||
if (fp.note[k]>0 && fp.note[k]<0x49) {
|
if (fp.note[k]>0 && fp.note[k]<0x49) {
|
||||||
|
lastNote[j]=fp.note[k];
|
||||||
short note=(fp.note[k]+seq[i].transpose[j])%12;
|
short note=(fp.note[k]+seq[i].transpose[j])%12;
|
||||||
short octave=2+((fp.note[k]+seq[i].transpose[j])/12);
|
short octave=2+((fp.note[k]+seq[i].transpose[j])/12);
|
||||||
if (fp.note[k]>=0x3d) octave-=6;
|
if (fp.note[k]>=0x3d) octave-=6;
|
||||||
|
@ -2574,6 +2677,27 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
p->data[k][4]=2;
|
p->data[k][4]=2;
|
||||||
p->data[k][5]=0;
|
p->data[k][5]=0;
|
||||||
}
|
}
|
||||||
|
} else if (fp.note[k]==0x49) {
|
||||||
|
if (k>0) {
|
||||||
|
p->data[k-1][4]=0x0d;
|
||||||
|
p->data[k-1][5]=0;
|
||||||
|
}
|
||||||
|
} else if (k==0 && lastTranspose[j]!=seq[i].transpose[j]) {
|
||||||
|
p->data[0][2]=lastIns[j];
|
||||||
|
p->data[0][4]=0x03;
|
||||||
|
p->data[0][5]=0xff;
|
||||||
|
lastTranspose[j]=seq[i].transpose[j];
|
||||||
|
|
||||||
|
short note=(lastNote[j]+seq[i].transpose[j])%12;
|
||||||
|
short octave=2+((lastNote[j]+seq[i].transpose[j])/12);
|
||||||
|
if (lastNote[j]>=0x3d) octave-=6;
|
||||||
|
if (note==0) {
|
||||||
|
note=12;
|
||||||
|
octave--;
|
||||||
|
}
|
||||||
|
octave&=0xff;
|
||||||
|
p->data[k][0]=note;
|
||||||
|
p->data[k][1]=octave;
|
||||||
}
|
}
|
||||||
if (fp.val[k]) {
|
if (fp.val[k]) {
|
||||||
if (ignoreNext) {
|
if (ignoreNext) {
|
||||||
|
@ -2602,10 +2726,12 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p->data[k][2]=(fp.val[k]+seq[i].offsetIns[j])&0x3f;
|
p->data[k][2]=(fp.val[k]+seq[i].offsetIns[j])&0x3f;
|
||||||
|
lastIns[j]=p->data[k][2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (fp.note[k]>0) {
|
} else if (fp.note[k]>0 && fp.note[k]<0x49) {
|
||||||
p->data[k][2]=seq[i].offsetIns[j];
|
p->data[k][2]=seq[i].offsetIns[j];
|
||||||
|
lastIns[j]=p->data[k][2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2619,11 +2745,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
ins->type=DIV_INS_AMIGA;
|
ins->type=DIV_INS_AMIGA;
|
||||||
ins->name=fmt::sprintf("Instrument %d",i);
|
ins->name=fmt::sprintf("Instrument %d",i);
|
||||||
ins->amiga.useWave=true;
|
ins->amiga.useWave=true;
|
||||||
//unsigned char seqSpeed=m.val[0];
|
unsigned char seqSpeed=m.val[0];
|
||||||
unsigned char freqMacro=m.val[1];
|
unsigned char freqMacro=m.val[1];
|
||||||
//unsigned char vibSpeed=m.val[2];
|
unsigned char vibSpeed=m.val[2];
|
||||||
//unsigned char vibDepth=m.val[3];
|
unsigned char vibDepth=m.val[3];
|
||||||
//unsigned char vibDelay=m.val[4];
|
unsigned char vibDelay=m.val[4];
|
||||||
|
|
||||||
unsigned char lastVal=m.val[5];
|
unsigned char lastVal=m.val[5];
|
||||||
|
|
||||||
|
@ -2633,6 +2759,9 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
signed char loopMapFreq[64];
|
signed char loopMapFreq[64];
|
||||||
memset(loopMapFreq,-1,64);
|
memset(loopMapFreq,-1,64);
|
||||||
|
|
||||||
|
signed char loopMapWave[64];
|
||||||
|
memset(loopMapWave,-1,64);
|
||||||
|
|
||||||
// volume sequence
|
// volume sequence
|
||||||
ins->std.volMacro.len=0;
|
ins->std.volMacro.len=0;
|
||||||
for (int j=5; j<64; j++) {
|
for (int j=5; j<64; j++) {
|
||||||
|
@ -2672,9 +2801,13 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
if (++ins->std.volMacro.len>=128) break;
|
if (++ins->std.volMacro.len>=128) break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ins->std.volMacro.val[ins->std.volMacro.len]=m.val[j];
|
// TODO: replace with upcoming macro speed
|
||||||
lastVal=m.val[j];
|
for (int k=0; k<MAX(1,seqSpeed); k++) {
|
||||||
if (++ins->std.volMacro.len>=128) break;
|
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>=128) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2685,6 +2818,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
FCMacro& fm=freqMacros[freqMacro];
|
FCMacro& fm=freqMacros[freqMacro];
|
||||||
for (int j=0; j<64; j++) {
|
for (int j=0; j<64; j++) {
|
||||||
loopMapFreq[j]=ins->std.arpMacro.len;
|
loopMapFreq[j]=ins->std.arpMacro.len;
|
||||||
|
loopMapWave[j]=ins->std.waveMacro.len;
|
||||||
if (fm.val[j]==0xe1) {
|
if (fm.val[j]==0xe1) {
|
||||||
if (ins->std.arpMacro.mode) {
|
if (ins->std.arpMacro.mode) {
|
||||||
ins->std.arpMacro.loop=(signed int)ins->std.arpMacro.len-1;
|
ins->std.arpMacro.loop=(signed int)ins->std.arpMacro.len-1;
|
||||||
|
@ -2707,6 +2841,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
} else if (fm.val[j]==0xe0) {
|
} else if (fm.val[j]==0xe0) {
|
||||||
if (++j>=64) break;
|
if (++j>=64) break;
|
||||||
ins->std.arpMacro.loop=loopMapFreq[fm.val[j]&63];
|
ins->std.arpMacro.loop=loopMapFreq[fm.val[j]&63];
|
||||||
|
ins->std.waveMacro.loop=loopMapWave[fm.val[j]&63];
|
||||||
break;
|
break;
|
||||||
} else if (fm.val[j]==0xe3) {
|
} else if (fm.val[j]==0xe3) {
|
||||||
logV("unhandled vibrato!");
|
logV("unhandled vibrato!");
|
||||||
|
@ -2735,6 +2870,32 @@ 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;
|
||||||
|
}
|
||||||
|
int vibPos=0;
|
||||||
|
ins->std.pitchMacro.loop=ins->std.pitchMacro.len;
|
||||||
|
do {
|
||||||
|
vibPos+=vibSpeed;
|
||||||
|
if (vibPos>vibDepth) vibPos=vibDepth;
|
||||||
|
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*4;
|
||||||
|
if (++ins->std.pitchMacro.len>=128) break;
|
||||||
|
} while (vibPos<vibDepth);
|
||||||
|
do {
|
||||||
|
vibPos-=vibSpeed;
|
||||||
|
if (vibPos<-vibDepth) vibPos=-vibDepth;
|
||||||
|
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*4;
|
||||||
|
if (++ins->std.pitchMacro.len>=128) break;
|
||||||
|
} while (vibPos>-vibDepth);
|
||||||
|
do {
|
||||||
|
vibPos+=vibSpeed;
|
||||||
|
if (vibPos>0) vibPos=0;
|
||||||
|
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*4;
|
||||||
|
if (++ins->std.pitchMacro.len>=128) break;
|
||||||
|
} while (vibPos<0);
|
||||||
|
|
||||||
ds.ins.push_back(ins);
|
ds.ins.push_back(ins);
|
||||||
}
|
}
|
||||||
ds.insLen=(int)ds.ins.size();
|
ds.insLen=(int)ds.ins.size();
|
||||||
|
|
Loading…
Reference in a new issue