Add support for multiple effects
This commit is contained in:
parent
7e57e2db71
commit
787bf7f328
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue