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)
// - effect number (256 bytes, values 0x0-0x23 (to represent 0-F and G-Z))
// - 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
// key-offs are stored in the note data as 0x01
unsigned char patDataBuf[256];
@ -216,6 +216,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
speed.interleaveFactor=info.interleaveFactor;
int speedGrooveIndex=1;
int usedEffectsCol=0;
std::unordered_map<TFMSpeed, int> speeds({{speed, 0}});
// initialize the global groove pattern first
@ -242,7 +243,6 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
logD("parsing pattern %d",i);
for (int j=0; j<6; j++) {
DivPattern* pat = info.ds->subsong[0]->pat[j].data[i];
info.ds->subsong[0]->pat[j].effectCols=3;
// notes
info.reader->read(patDataBuf,256);
@ -289,86 +289,90 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
}
// effects
int numEffectsCol=(info.v2) ? 4 : 1;
for (int l=0; l<numEffectsCol; l++) {
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++) {
if (effectNum[k] || effectVal[k]) usedEffectsCol=l+1;
switch (effectNum[k]) {
case 0:
// arpeggio or no effect (if effect val is 0)
if (effectVal[k]==0) break;
pat->data[k][4]=effectNum[k];
pat->data[k][5]=effectVal[k];
pat->data[k][4+(l*2)]=effectNum[k];
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 1:
// pitch slide up
case 2:
// pitch slide down
pat->data[k][4]=effectNum[k];
pat->data[k][4+(l*2)]=effectNum[k];
if (effectVal[k]) {
lastSlide=effectVal[k];
pat->data[k][5]=effectVal[k];
pat->data[k][5+(l*2)]=effectVal[k];
} else {
pat->data[k][5]=lastSlide;
pat->data[k][5+(l*2)]=lastSlide;
}
break;
case 3:
// portamento
case 4:
// vibrato
pat->data[k][5]=0;
pat->data[k][5+(l*2)]=0;
if (effectVal[k]&0xF0) {
pat->data[k][5]|=effectVal[k]&0xF0;
pat->data[k][5+(l*2)]|=effectVal[k]&0xF0;
} else {
pat->data[k][5]|=lastVibrato&0xF0;
pat->data[k][5+(l*2)]|=lastVibrato&0xF0;
}
if (effectVal[k]&0x0F) {
pat->data[k][5]|=effectVal[k]&0x0F;
pat->data[k][5+(l*2)]|=effectVal[k]&0x0F;
} else {
pat->data[k][5]|=lastVibrato&0x0F;
pat->data[k][5+(l*2)]|=lastVibrato&0x0F;
}
pat->data[k][4]=effectNum[k];
lastVibrato=pat->data[k][5];
pat->data[k][4+(l*2)]=effectNum[k];
lastVibrato=pat->data[k][5+(l*2)];
break;
case 5:
// poramento + volume slide
pat->data[k][4]=0x06;
pat->data[k][5]=effectVal[k];
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]=0x05;
pat->data[k][5]=effectVal[k];
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]=0x12;
pat->data[k][5]=effectVal[k];
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]=0x13;
pat->data[k][5]=effectVal[k];
pat->data[k][4+(l*2)]=0x13;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 10:
// volume slide
pat->data[k][4]=0xA;
pat->data[k][5]=effectVal[k];
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]=0x14;
pat->data[k][5]=effectVal[k];
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]=0x15;
pat->data[k][5]=effectVal[k];
pat->data[k][4+(l*2)]=0x15;
pat->data[k][5+(l*2)]=effectVal[k];
break;
case 14:
switch (effectVal[k]>>4) {
@ -377,18 +381,18 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
case 2:
case 3:
// modify multiplier of operators
pat->data[k][4]=0x16;
pat->data[k][5]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF);
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]=0x80;
pat->data[k][4+(l*2)]=0x80;
if ((effectVal[k]&0xF)==1) {
pat->data[k][5]=0;
pat->data[k][5+(l*2)]=0;
} else if ((effectVal[k]&0xF)==2) {
pat->data[k][5]=0xFF;
pat->data[k][5+(l*2)]=0xFF;
} else {
pat->data[k][5]=0x80;
pat->data[k][5+(l*2)]=0x80;
}
break;
}
@ -406,9 +410,9 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
speed.interleaveFactor=effectVal[k]&0xF;
} else if ((effectVal[k]>>4)==(effectVal[k]&0xF)) {
// if both speeds are equal
pat->data[k][4]=0x0F;
pat->data[k][4+(l*2)]=0x0F;
unsigned char speedSet=effectVal[k]>>4;
pat->data[k][5]=speedSet;
pat->data[k][5+(l*2)]=speedSet;
break;
} else {
speed.speedEven=effectVal[k]>>4;
@ -417,8 +421,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
auto speedIndex = speeds.find(speed);
if (speedIndex != speeds.end()) {
pat->data[k][4]=0x09;
pat->data[k][5]=speedIndex->second;
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedIndex->second;
break;
}
if (speed.interleaveFactor>8) {
@ -434,14 +438,14 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
info.ds->grooves.push_back(groove);
speeds[speed]=speedGrooveIndex;
pat->data[k][4]=0x09;
pat->data[k][5]=speedGrooveIndex;
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedGrooveIndex;
speedGrooveIndex++;
break;
}
}
if (info.v2) info.reader->skip(1536);
info.ds->subsong[0]->pat[j].effectCols=(usedEffectsCol*2)+1;
}
}
}
@ -454,6 +458,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
for (int i=0; i<info.ds->subsong[0]->ordersLen; i++) {
for (int j=0; j<6; j++) {
for (int l=0; l<usedEffectsCol; l++) {
DivPattern* pat = info.ds->subsong[0]->pat[j].data[info.orderList[i]];
// default instrument
@ -462,25 +467,25 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
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++) {
if (chArpeggio[j] && pat->data[k][4]!=0x00 && pat->data[k][0]!=-1) {
pat->data[k][6]=0x00;
pat->data[k][7]=0;
if (chArpeggio[j] && pat->data[k][4+(l*2)]!=0x00 && pat->data[k][0]!=-1) {
pat->data[k][4+usedEffectsCol*2+(l*2)]=0x00;
pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chArpeggio[j]=false;
} else if (chPorta[j] && pat->data[k][4]!=0x03 && pat->data[k][4]!=0x01 && pat->data[k][4]!=0x02) {
pat->data[k][6]=0x03;
pat->data[k][7]=0;
} 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][4+usedEffectsCol*2+(l*2)]=0x03;
pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chPorta[j]=false;
} else if (chVibrato[j] && pat->data[k][4]!=0x04 && pat->data[k][0]!=-1) {
pat->data[k][6]=0x04;
pat->data[k][7]=0;
} else if (chVibrato[j] && pat->data[k][4+(l*2)]!=0x04 && pat->data[k][0]!=-1) {
pat->data[k][4+usedEffectsCol*2+(l*2)]=0x04;
pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chVibrato[j]=false;
} else if (chVolumeSlide[j] && pat->data[k][4]!=0x0A) {
pat->data[k][6]=0x0A;
pat->data[k][7]=0;
} else if (chVolumeSlide[j] && pat->data[k][4+(l*2)]!=0x0A) {
pat->data[k][4+usedEffectsCol*2+(l*2)]=0x0A;
pat->data[k][5+usedEffectsCol*2+(l*2)]=0;
chVolumeSlide[j]=false;
}
switch (pat->data[k][4]) {
switch (pat->data[k][4+l]) {
case 0:
chArpeggio[j]=true;
break;
@ -502,6 +507,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
}
}
}
}
bool DivEngine::loadTFMv1(unsigned char* file, size_t len) {
// the documentation for this version is in russian only