Add support for multiple effects

This commit is contained in:
techmetx11 2024-04-16 22:25:29 +01:00
parent 7e57e2db71
commit 787bf7f328
No known key found for this signature in database
GPG key ID: 20E0C88A0E7E5AF2

View file

@ -202,7 +202,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
// - instrument number data (256 bytes) // - instrument number data (256 bytes)
// - effect number (256 bytes, values 0x0-0x23 (to represent 0-F and G-Z)) // - effect number (256 bytes, values 0x0-0x23 (to represent 0-F and G-Z))
// - effect value (256 bytes) // - effect value (256 bytes)
// - padding(?) (1536 bytes, always set to 0) (ONLY ON V2) // - extra 3 effects (1536 bytes 256x3x2) (ONLY ON V2)
// notes are stored as an inverted value of note+octave*12 // notes are stored as an inverted value of note+octave*12
// key-offs are stored in the note data as 0x01 // key-offs are stored in the note data as 0x01
unsigned char patDataBuf[256]; unsigned char patDataBuf[256];
@ -216,6 +216,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
speed.interleaveFactor=info.interleaveFactor; speed.interleaveFactor=info.interleaveFactor;
int speedGrooveIndex=1; int speedGrooveIndex=1;
int usedEffectsCol=0;
std::unordered_map<TFMSpeed, int> speeds({{speed, 0}}); std::unordered_map<TFMSpeed, int> speeds({{speed, 0}});
// initialize the global groove pattern first // initialize the global groove pattern first
@ -242,7 +243,6 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
logD("parsing pattern %d",i); logD("parsing pattern %d",i);
for (int j=0; j<6; j++) { for (int j=0; j<6; j++) {
DivPattern* pat = info.ds->subsong[0]->pat[j].data[i]; DivPattern* pat = info.ds->subsong[0]->pat[j].data[i];
info.ds->subsong[0]->pat[j].effectCols=3;
// notes // notes
info.reader->read(patDataBuf,256); info.reader->read(patDataBuf,256);
@ -289,159 +289,163 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
} }
// effects // effects
unsigned char effectNum[256];
unsigned char effectVal[256];
info.reader->read(effectNum,256);
info.reader->read(effectVal,256);
for (int k=0; k<256; k++) { int numEffectsCol=(info.v2) ? 4 : 1;
switch (effectNum[k]) { for (int l=0; l<numEffectsCol; l++) {
case 0: unsigned char effectNum[256];
// arpeggio or no effect (if effect val is 0) unsigned char effectVal[256];
if (effectVal[k]==0) break; info.reader->read(effectNum,256);
pat->data[k][4]=effectNum[k]; info.reader->read(effectVal,256);
pat->data[k][5]=effectVal[k];
break; for (int k=0; k<256; k++) {
case 1: if (effectNum[k] || effectVal[k]) usedEffectsCol=l+1;
// pitch slide up switch (effectNum[k]) {
case 2:
// pitch slide down
pat->data[k][4]=effectNum[k];
if (effectVal[k]) {
lastSlide=effectVal[k];
pat->data[k][5]=effectVal[k];
} else {
pat->data[k][5]=lastSlide;
}
break;
case 3:
// portamento
case 4:
// vibrato
pat->data[k][5]=0;
if (effectVal[k]&0xF0) {
pat->data[k][5]|=effectVal[k]&0xF0;
} else {
pat->data[k][5]|=lastVibrato&0xF0;
}
if (effectVal[k]&0x0F) {
pat->data[k][5]|=effectVal[k]&0x0F;
} else {
pat->data[k][5]|=lastVibrato&0x0F;
}
pat->data[k][4]=effectNum[k];
lastVibrato=pat->data[k][5];
break;
case 5:
// poramento + volume slide
pat->data[k][4]=0x06;
pat->data[k][5]=effectVal[k];
break;
case 6:
// vibrato + volume slide
pat->data[k][4]=0x05;
pat->data[k][5]=effectVal[k];
break;
case 8:
// modify TL of operator 1
pat->data[k][4]=0x12;
pat->data[k][5]=effectVal[k];
break;
case 9:
// modify TL of operator 2
pat->data[k][4]=0x13;
pat->data[k][5]=effectVal[k];
break;
case 10:
// volume slide
pat->data[k][4]=0xA;
pat->data[k][5]=effectVal[k];
break;
case 11:
// multi-frequency mode of CH3 control
// TODO
case 12:
// modify TL of operator 3
pat->data[k][4]=0x14;
pat->data[k][5]=effectVal[k];
break;
case 13:
// modify TL of operator 2
pat->data[k][4]=0x15;
pat->data[k][5]=effectVal[k];
break;
case 14:
switch (effectVal[k]>>4) {
case 0: case 0:
case 1: // arpeggio or no effect (if effect val is 0)
case 2: if (effectVal[k]==0) break;
case 3: pat->data[k][4+(l*2)]=effectNum[k];
// modify multiplier of operators pat->data[k][5+(l*2)]=effectVal[k];
pat->data[k][4]=0x16;
pat->data[k][5]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF);
break; break;
case 8: case 1:
// pan // pitch slide up
pat->data[k][4]=0x80; case 2:
if ((effectVal[k]&0xF)==1) { // pitch slide down
pat->data[k][5]=0; pat->data[k][4+(l*2)]=effectNum[k];
} else if ((effectVal[k]&0xF)==2) { if (effectVal[k]) {
pat->data[k][5]=0xFF; lastSlide=effectVal[k];
pat->data[k][5+(l*2)]=effectVal[k];
} else { } else {
pat->data[k][5]=0x80; pat->data[k][5+(l*2)]=lastSlide;
} }
break; break;
} case 3:
break; // portamento
case 15: case 4:
// speed // vibrato
pat->data[k][5+(l*2)]=0;
if (effectVal[k]==0) { if (effectVal[k]&0xF0) {
// if speed is set to zero (reset to global values) pat->data[k][5+(l*2)]|=effectVal[k]&0xF0;
speed.speedEven=info.speedEven; } else {
speed.speedOdd=info.speedOdd; pat->data[k][5+(l*2)]|=lastVibrato&0xF0;
speed.interleaveFactor=info.interleaveFactor; }
} else if (effectVal[k]>>4==0) { if (effectVal[k]&0x0F) {
// if the top nibble is set to zero (set interleave factor) pat->data[k][5+(l*2)]|=effectVal[k]&0x0F;
speed.interleaveFactor=effectVal[k]&0xF; } else {
} else if ((effectVal[k]>>4)==(effectVal[k]&0xF)) { pat->data[k][5+(l*2)]|=lastVibrato&0x0F;
// if both speeds are equal }
pat->data[k][4]=0x0F; pat->data[k][4+(l*2)]=effectNum[k];
unsigned char speedSet=effectVal[k]>>4; lastVibrato=pat->data[k][5+(l*2)];
pat->data[k][5]=speedSet;
break; break;
} else { case 5:
speed.speedEven=effectVal[k]>>4; // poramento + volume slide
speed.speedOdd=effectVal[k]&0xF; pat->data[k][4+(l*2)]=0x06;
} pat->data[k][5+(l*2)]=effectVal[k];
break;
case 6:
// vibrato + volume slide
pat->data[k][4+(l*2)]=0x05;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 8:
// modify TL of operator 1
pat->data[k][4+(l*2)]=0x12;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 9:
// modify TL of operator 2
pat->data[k][4+(l*2)]=0x13;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 10:
// volume slide
pat->data[k][4+(l*2)]=0xA;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 11:
// multi-frequency mode of CH3 control
// TODO
case 12:
// modify TL of operator 3
pat->data[k][4+(l*2)]=0x14;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 13:
// modify TL of operator 2
pat->data[k][4+(l*2)]=0x15;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 14:
switch (effectVal[k]>>4) {
case 0:
case 1:
case 2:
case 3:
// modify multiplier of operators
pat->data[k][4+(l*2)]=0x16;
pat->data[k][5+(l*2)]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF);
break;
case 8:
// pan
pat->data[k][4+(l*2)]=0x80;
if ((effectVal[k]&0xF)==1) {
pat->data[k][5+(l*2)]=0;
} else if ((effectVal[k]&0xF)==2) {
pat->data[k][5+(l*2)]=0xFF;
} else {
pat->data[k][5+(l*2)]=0x80;
}
break;
}
break;
case 15:
// speed
auto speedIndex = speeds.find(speed); if (effectVal[k]==0) {
if (speedIndex != speeds.end()) { // if speed is set to zero (reset to global values)
pat->data[k][4]=0x09; speed.speedEven=info.speedEven;
pat->data[k][5]=speedIndex->second; speed.speedOdd=info.speedOdd;
speed.interleaveFactor=info.interleaveFactor;
} else if (effectVal[k]>>4==0) {
// if the top nibble is set to zero (set interleave factor)
speed.interleaveFactor=effectVal[k]&0xF;
} else if ((effectVal[k]>>4)==(effectVal[k]&0xF)) {
// if both speeds are equal
pat->data[k][4+(l*2)]=0x0F;
unsigned char speedSet=effectVal[k]>>4;
pat->data[k][5+(l*2)]=speedSet;
break;
} else {
speed.speedEven=effectVal[k]>>4;
speed.speedOdd=effectVal[k]&0xF;
}
auto speedIndex = speeds.find(speed);
if (speedIndex != speeds.end()) {
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedIndex->second;
break;
}
if (speed.interleaveFactor>8) {
logW("speed interleave factor is bigger than 8, speed information may be inaccurate");
speed.interleaveFactor=8;
}
for (int i=0; i<speed.interleaveFactor; i++) {
groove.val[i]=speed.speedEven;
groove.val[i+speed.interleaveFactor]=speed.speedOdd;
}
groove.len=speed.interleaveFactor*2;
info.ds->grooves.push_back(groove);
speeds[speed]=speedGrooveIndex;
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedGrooveIndex;
speedGrooveIndex++;
break; break;
} }
if (speed.interleaveFactor>8) {
logW("speed interleave factor is bigger than 8, speed information may be inaccurate");
speed.interleaveFactor=8;
}
for (int i=0; i<speed.interleaveFactor; i++) {
groove.val[i]=speed.speedEven;
groove.val[i+speed.interleaveFactor]=speed.speedOdd;
}
groove.len=speed.interleaveFactor*2;
info.ds->grooves.push_back(groove);
speeds[speed]=speedGrooveIndex;
pat->data[k][4]=0x09;
pat->data[k][5]=speedGrooveIndex;
speedGrooveIndex++;
break;
} }
info.ds->subsong[0]->pat[j].effectCols=(usedEffectsCol*2)+1;
} }
if (info.v2) info.reader->skip(1536);
} }
} }
@ -454,49 +458,51 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
for (int i=0; i<info.ds->subsong[0]->ordersLen; i++) { for (int i=0; i<info.ds->subsong[0]->ordersLen; i++) {
for (int j=0; j<6; j++) { for (int j=0; j<6; j++) {
DivPattern* pat = info.ds->subsong[0]->pat[j].data[info.orderList[i]]; for (int l=0; l<usedEffectsCol; l++) {
DivPattern* pat = info.ds->subsong[0]->pat[j].data[info.orderList[i]];
// default instrument // default instrument
if (i==0 && pat->data[0][2]==-1) pat->data[0][2]=0; if (i==0 && pat->data[0][2]==-1) pat->data[0][2]=0;
unsigned char truePatLen=(info.patLens[info.orderList[i]]<info.ds->subsong[0]->patLen) ? info.patLens[info.orderList[i]] : info.ds->subsong[0]->patLen; unsigned char truePatLen=(info.patLens[info.orderList[i]]<info.ds->subsong[0]->patLen) ? info.patLens[info.orderList[i]] : info.ds->subsong[0]->patLen;
for (int k=0; k<truePatLen; k++) { for (int k=0; k<truePatLen; k++) {
if (chArpeggio[j] && pat->data[k][4]!=0x00 && pat->data[k][0]!=-1) { if (chArpeggio[j] && pat->data[k][4+(l*2)]!=0x00 && pat->data[k][0]!=-1) {
pat->data[k][6]=0x00; pat->data[k][4+usedEffectsCol*2+(l*2)]=0x00;
pat->data[k][7]=0; pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chArpeggio[j]=false; chArpeggio[j]=false;
} else if (chPorta[j] && pat->data[k][4]!=0x03 && pat->data[k][4]!=0x01 && pat->data[k][4]!=0x02) { } else if (chPorta[j] && pat->data[k][4+(l*2)]!=0x03 && pat->data[k][4+(l*2)]!=0x01 && pat->data[k][4+(l*2)]!=0x02) {
pat->data[k][6]=0x03; pat->data[k][4+usedEffectsCol*2+(l*2)]=0x03;
pat->data[k][7]=0; pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chPorta[j]=false; chPorta[j]=false;
} else if (chVibrato[j] && pat->data[k][4]!=0x04 && pat->data[k][0]!=-1) { } else if (chVibrato[j] && pat->data[k][4+(l*2)]!=0x04 && pat->data[k][0]!=-1) {
pat->data[k][6]=0x04; pat->data[k][4+usedEffectsCol*2+(l*2)]=0x04;
pat->data[k][7]=0; pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chVibrato[j]=false; chVibrato[j]=false;
} else if (chVolumeSlide[j] && pat->data[k][4]!=0x0A) { } else if (chVolumeSlide[j] && pat->data[k][4+(l*2)]!=0x0A) {
pat->data[k][6]=0x0A; pat->data[k][4+usedEffectsCol*2+(l*2)]=0x0A;
pat->data[k][7]=0; pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chVolumeSlide[j]=false; chVolumeSlide[j]=false;
} }
switch (pat->data[k][4]) { switch (pat->data[k][4+l]) {
case 0: case 0:
chArpeggio[j]=true; chArpeggio[j]=true;
break; break;
case 1: case 1:
case 2: case 2:
case 3: case 3:
chPorta[j]=true; chPorta[j]=true;
break; break;
case 4: case 4:
chVibrato[j]=true; chVibrato[j]=true;
break; break;
case 0xA: case 0xA:
chVolumeSlide[j]=true; chVolumeSlide[j]=true;
break; break;
default: default:
break; break;
}
} }
} }
} }