Merge branch 'master' into newMixer

This commit is contained in:
Eknous-P 2025-10-20 16:43:27 +04:00
commit 678a095822
96 changed files with 2015 additions and 1491 deletions

View file

@ -58,6 +58,9 @@ bool DivConfig::save(const char* path, bool redundancy) {
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
return false;
}
if (redundancy) {
fputs("!DIV_CONFIG_START!\n",f);
}
for (auto& i: conf) {
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
@ -69,6 +72,9 @@ bool DivConfig::save(const char* path, bool redundancy) {
return false;
}
}
if (redundancy) {
fputs("~DIV_CONFIG_END~\n",f);
}
fclose(f);
logD("config file written successfully.");
return true;
@ -124,8 +130,12 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
if (redundancy) {
unsigned char* readBuf=new unsigned char[CHECK_BUF_SIZE];
size_t readBufLen=0;
bool weRescued=false;
for (int i=0; i<REDUNDANCY_NUM_ATTEMPTS; i++) {
bool viable=false;
bool startCheck=true;
bool hasStartMarker=false;
unsigned char endMarker[18];
if (i>0) {
snprintf(line,4095,"%s.%d",path,i);
} else {
@ -143,15 +153,27 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
// check whether there's something
while (!feof(f)) {
bool willBreak=false;
readBufLen=fread(readBuf,1,CHECK_BUF_SIZE,f);
if (ferror(f)) {
logV("fread(): %s",strerror(errno));
break;
}
if (startCheck) {
if (readBufLen>=19) {
if (memcmp(readBuf,"!DIV_CONFIG_START!\n",19)==0) {
hasStartMarker=true;
logV("start marker found");
}
}
startCheck=false;
}
for (size_t j=0; j<readBufLen; j++) {
if (readBuf[j]==0) {
viable=false;
willBreak=true;
logW("a zero?");
break;
}
@ -160,7 +182,30 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
}
}
if (viable) break;
if (readBufLen>=18) {
memcpy(endMarker,&readBuf[readBufLen-18],18);
} else if (readBufLen>0) {
// shift buffer left
for (size_t j=0, k=readBufLen; j<readBufLen && k<18; j++, k++) {
endMarker[j]=endMarker[k];
}
// copy to end
memcpy(&endMarker[18-readBufLen],readBuf,readBufLen);
}
if (willBreak) break;
}
// check for end marker if start marker is present
if (hasStartMarker) {
if (memcmp(endMarker,"\n~DIV_CONFIG_END~\n",18)!=0) {
// file is incomplete
viable=false;
logV("end marker NOT found!");
reportError("saved from an incomplete config.\nyeah! for a second I thought you were going to lose it.");
weRescued=true;
}
}
// there's something
@ -184,6 +229,9 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
logD("config does not exist");
if (createOnFail) {
logI("creating default config.");
if (weRescued) {
reportError("what the FUCK is that supposed to mean?!");
}
//reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
return save(path,redundancy);
} else {

View file

@ -31,6 +31,22 @@
#define DIV_MAX_COLS 32
#define DIV_MAX_EFFECTS 8
// pattern fields
#define DIV_PAT_NOTE 0
#define DIV_PAT_INS 1
#define DIV_PAT_VOL 2
#define DIV_PAT_FX(_x) (3+((_x)<<1))
#define DIV_PAT_FXVAL(_x) (4+((_x)<<1))
// column type checks
#define DIV_PAT_IS_EFFECT(_x) ((_x)>DIV_PAT_VOL && (!((_x)&1)))
#define DIV_PAT_IS_EFFECT_VAL(_x) ((_x)>DIV_PAT_VOL && ((_x)&1))
#define DIV_NOTE_NULL_PAT 252
#define DIV_NOTE_OFF 253
#define DIV_NOTE_REL 254
#define DIV_MACRO_REL 255
// sample related
#define DIV_MAX_SAMPLE_TYPE 4

View file

@ -947,8 +947,8 @@ void DivEngine::delUnusedIns() {
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; l++) {
if (song.subsong[j]->pat[i].data[k]->data[l][2]>=0 && song.subsong[j]->pat[i].data[k]->data[l][2]<256) {
isUsed[song.subsong[j]->pat[i].data[k]->data[l][2]]=true;
if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]>=0 && song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]<256) {
isUsed[song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]]=true;
}
}
}
@ -1033,38 +1033,6 @@ void DivEngine::delUnusedSamples() {
}
}
// scan in pattern (legacy sample mode)
// disabled because it is unreliable
/*
for (DivSubSong* i: song.subsong) {
for (int j=0; j<getTotalChannelCount(); j++) {
bool is17On=false;
int bank=0;
for (int k=0; k<i->ordersLen; k++) {
DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],false);
for (int l=0; l<i->patLen; l++) {
for (int m=0; m<i->pat[j].effectCols; m++) {
if (p->data[l][4+(m<<1)]==0x17) {
is17On=(p->data[l][5+(m<<1)]>0);
}
if (p->data[l][4+(m<<1)]==0xeb) {
bank=p->data[l][5+(m<<1)];
if (bank==-1) bank=0;
}
}
if (is17On) {
if (p->data[l][1]!=0 || p->data[l][0]!=0) {
if (p->data[l][0]<=12) {
int note=(12*bank)+(p->data[l][0]%12);
if (note<256) isUsed[note]=true;
}
}
}
}
}
}
}*/
// delete
for (int i=0; i<song.sampleLen; i++) {
if (!isUsed[i]) {
@ -2293,6 +2261,64 @@ int DivEngine::getEffectiveSampleRate(int rate) {
return rate;
}
short DivEngine::splitNoteToNote(short note, short octave) {
if (note==100) {
return DIV_NOTE_OFF;
} else if (note==101) {
return DIV_NOTE_REL;
} else if (note==102) {
return DIV_MACRO_REL;
} else if (note==0 && octave!=0) {
// "BUG" note!
return DIV_NOTE_NULL_PAT;
} else if (note==0 && octave==0) {
return -1;
} else {
int seek=(note+(signed char)octave*12)+60;
if (seek<0 || seek>=180) {
return DIV_NOTE_NULL_PAT;
} else {
return seek;
}
}
return -1;
}
void DivEngine::noteToSplitNote(short note, short& outNote, short& outOctave) {
switch (note) {
case DIV_NOTE_OFF:
outNote=100;
outOctave=0;
break;
case DIV_NOTE_REL:
outNote=101;
outOctave=0;
break;
case DIV_MACRO_REL:
outNote=102;
outOctave=0;
break;
case DIV_NOTE_NULL_PAT:
// "BUG" note!
outNote=0;
outOctave=1;
break;
case -1:
outNote=0;
outOctave=0;
break;
default:
outNote=note%12;
outOctave=(unsigned char)(note-60)/12;
if (outNote==0) {
outNote=12;
outOctave--;
}
break;
}
}
void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
BUSY_BEGIN;
previewSampleNoLock(sample,note,pStart,pEnd);
@ -2749,8 +2775,8 @@ void DivEngine::delInstrumentUnsafe(int index) {
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; l++) {
if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) {
song.subsong[j]->pat[i].data[k]->data[l][2]--;
if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]>index) {
song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]--;
}
}
}
@ -3111,7 +3137,7 @@ void DivEngine::deepCloneOrder(int pos, bool where) {
order[i]=j;
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
DivPattern* pat=curPat[i].getPattern(j,true);
memcpy(pat->data,oldPat->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
memcpy(pat->newData,oldPat->newData,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
logD("found at %d",j);
didNotFind=false;
break;
@ -3217,10 +3243,10 @@ void DivEngine::exchangeIns(int one, int two) {
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; l++) {
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
song.subsong[j]->pat[i].data[k]->data[l][2]=two;
} else if (song.subsong[j]->pat[i].data[k]->data[l][2]==two) {
song.subsong[j]->pat[i].data[k]->data[l][2]=one;
if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]==one) {
song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]=two;
} else if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]==two) {
song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]=one;
}
}
}

View file

@ -54,8 +54,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "dev233"
#define DIV_ENGINE_VERSION 233
#define DIV_VERSION "dev235"
#define DIV_ENGINE_VERSION 235
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -140,7 +140,7 @@ struct DivChannelState {
int panDepth, panRate, panPos, panSpeed;
int sampleOff;
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing;
bool doNote, legato, portaStop, keyOn, keyOff, stopOnOff, releasing;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp, sampleOffSet;
bool wentThroughNote, goneThroughNote;
@ -197,7 +197,6 @@ struct DivChannelState {
portaStop(false),
keyOn(false),
keyOff(false),
nowYouCanStop(true),
stopOnOff(false),
releasing(false),
arpYield(false),
@ -723,7 +722,7 @@ class DivEngine {
SafeWriter* saveDMF(unsigned char version);
// save as .fur.
// if notPrimary is true then the song will not be altered
SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true);
SafeWriter* saveFur(bool notPrimary=false);
// return a ROM exporter.
DivROMExport* buildROM(DivROMExportOptions sys);
// dump to VGM.
@ -929,6 +928,10 @@ class DivEngine {
// get effective sample rate
int getEffectiveSampleRate(int rate);
// convert between old and new note/octave format
short splitNoteToNote(short note, short octave);
void noteToSplitNote(short note, short& outNote, short& outOctave);
// is FM system
bool isFMSystem(DivSystem sys);

View file

@ -783,109 +783,113 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
DivPattern* pat=chan.getPattern(ds.subsong[0]->orders.ord[i][j],true);
if (ds.version>0x08) { // current pattern format
for (int k=0; k<ds.subsong[0]->patLen; k++) {
// note
pat->data[k][0]=reader.readS();
// octave
pat->data[k][1]=reader.readS();
if (ds.system[0]==DIV_SYSTEM_SMS && ds.version<0x0e && pat->data[k][1]>0) {
short note=reader.readS();
short octave=reader.readS();
if (ds.system[0]==DIV_SYSTEM_SMS && ds.version<0x0e && octave>0) {
// apparently it was up one octave before
pat->data[k][1]--;
} else if (ds.system[0]==DIV_SYSTEM_GENESIS && ds.version<0x0e && pat->data[k][1]>0 && i>5) {
octave--;
} else if (ds.system[0]==DIV_SYSTEM_GENESIS && ds.version<0x0e && octave>0 && i>5) {
// ditto
pat->data[k][1]--;
} else if (ds.system[0]==DIV_SYSTEM_MSX2 && pat->data[k][1]>0 && i<3) {
octave--;
} else if (ds.system[0]==DIV_SYSTEM_MSX2 && octave>0 && i<3) {
// why the hell?
pat->data[k][1]++;
octave++;
}
if (ds.version<0x12) {
if (ds.system[0]==DIV_SYSTEM_GB && i==3 && pat->data[k][1]>0) {
if (ds.system[0]==DIV_SYSTEM_GB && i==3 && octave>0) {
// back then noise was 2 octaves lower
pat->data[k][1]-=2;
octave-=2;
}
}
if (ds.system[0]==DIV_SYSTEM_YMU759 && pat->data[k][0]!=0) {
if (ds.system[0]==DIV_SYSTEM_YMU759 && note!=0) {
// apparently YMU759 is stored 2 octaves lower
pat->data[k][1]+=2;
octave+=2;
}
if (pat->data[k][0]==0 && pat->data[k][1]!=0) {
logD("what? %d:%d:%d note %d octave %d",i,j,k,pat->data[k][0],pat->data[k][1]);
pat->data[k][0]=12;
pat->data[k][1]--;
if (note==0 && octave!=0) {
logD("what? %d:%d:%d note %d octave %d",i,j,k,note,octave);
note=12;
octave--;
}
pat->newData[k][DIV_PAT_NOTE]=splitNoteToNote(note,octave);
// volume
pat->data[k][3]=reader.readS();
pat->newData[k][DIV_PAT_VOL]=reader.readS();
if (ds.version<0x0a) {
// back then volume was stored as 00-ff instead of 00-7f/0-f
if (i>5) {
pat->data[k][3]>>=4;
pat->newData[k][DIV_PAT_VOL]>>=4;
} else {
pat->data[k][3]>>=1;
pat->newData[k][DIV_PAT_VOL]>>=1;
}
}
if (ds.version<0x12) {
if (ds.system[0]==DIV_SYSTEM_GB && i==2 && pat->data[k][3]>0) {
if (ds.system[0]==DIV_SYSTEM_GB && i==2 && pat->newData[k][DIV_PAT_VOL]>0) {
// volume range of GB wave channel was 0-3 rather than 0-F
pat->data[k][3]=(pat->data[k][3]&3)*5;
pat->newData[k][DIV_PAT_VOL]=(pat->newData[k][DIV_PAT_VOL]&3)*5;
}
}
for (int l=0; l<chan.effectCols; l++) {
// effect
pat->data[k][4+(l<<1)]=reader.readS();
pat->data[k][5+(l<<1)]=reader.readS();
pat->newData[k][DIV_PAT_FX(l)]=reader.readS();
pat->newData[k][DIV_PAT_FXVAL(l)]=reader.readS();
if (ds.version<0x14) {
if (pat->data[k][4+(l<<1)]==0xe5 && pat->data[k][5+(l<<1)]!=-1) {
pat->data[k][5+(l<<1)]=128+((pat->data[k][5+(l<<1)]-128)/4);
// the range of E5xx was different back then
if (pat->newData[k][DIV_PAT_FX(l)]==0xe5 && pat->newData[k][DIV_PAT_FXVAL(l)]!=-1) {
pat->newData[k][DIV_PAT_FXVAL(l)]=128+((pat->newData[k][DIV_PAT_FXVAL(l)]-128)/4);
}
}
}
// instrument
pat->data[k][2]=reader.readS();
pat->newData[k][DIV_PAT_INS]=reader.readS();
// this is sad
if (ds.system[0]==DIV_SYSTEM_NES_FDS) {
if (i==5 && pat->data[k][2]!=-1) {
if (pat->data[k][2]>=0 && pat->data[k][2]<ds.insLen) {
ds.ins[pat->data[k][2]]->type=DIV_INS_FDS;
if (i==5 && pat->newData[k][DIV_PAT_INS]!=-1) {
if (pat->newData[k][DIV_PAT_INS]>=0 && pat->newData[k][DIV_PAT_INS]<ds.insLen) {
ds.ins[pat->newData[k][DIV_PAT_INS]]->type=DIV_INS_FDS;
}
}
}
if (ds.system[0]==DIV_SYSTEM_MSX2) {
if (i>=3 && pat->data[k][2]!=-1) {
if (pat->data[k][2]>=0 && pat->data[k][2]<ds.insLen) {
ds.ins[pat->data[k][2]]->type=DIV_INS_SCC;
if (i>=3 && pat->newData[k][DIV_PAT_INS]!=-1) {
if (pat->newData[k][DIV_PAT_INS]>=0 && pat->newData[k][DIV_PAT_INS]<ds.insLen) {
ds.ins[pat->newData[k][DIV_PAT_INS]]->type=DIV_INS_SCC;
}
}
}
}
} else { // historic pattern format
if (i<16) pat->data[0][2]=historicColIns[i];
if (i<16) pat->newData[0][DIV_PAT_INS]=historicColIns[i];
for (int k=0; k<ds.subsong[0]->patLen; k++) {
// note
pat->data[k][0]=reader.readC();
// octave
pat->data[k][1]=reader.readC();
if (pat->data[k][0]!=0) {
short note=reader.readC();
short octave=reader.readC();
if (note!=0) {
// YMU759 is stored 2 octaves lower
pat->data[k][1]+=2;
octave+=2;
}
if (pat->data[k][0]==0 && pat->data[k][1]!=0) {
logD("what? %d:%d:%d note %d octave %d",i,j,k,pat->data[k][0],pat->data[k][1]);
pat->data[k][0]=12;
pat->data[k][1]--;
if (note==0 && octave!=0) {
logD("what? %d:%d:%d note %d octave %d",i,j,k,note,octave);
note=12;
octave--;
}
pat->newData[k][DIV_PAT_NOTE]=splitNoteToNote(note,octave);
// volume and effect
unsigned char vol=reader.readC();
unsigned char fx=reader.readC();
unsigned char fxVal=reader.readC();
pat->data[k][3]=(vol==0x80 || vol==0xff)?-1:vol;
pat->newData[k][DIV_PAT_VOL]=(vol==0x80 || vol==0xff)?-1:vol;
// effect
pat->data[k][4]=(fx==0x80 || fx==0xff)?-1:fx;
pat->data[k][5]=(fxVal==0x80 || fx==0xff)?-1:fxVal;
pat->newData[k][DIV_PAT_FX(0)]=(fx==0x80 || fx==0xff)?-1:fx;
pat->newData[k][DIV_PAT_FXVAL(0)]=(fxVal==0x80 || fx==0xff)?-1:fxVal;
// instrument
if (ds.version>0x05) {
pat->data[k][2]=reader.readC();
if (pat->data[k][2]==0x80 || pat->data[k][2]==0xff) pat->data[k][2]=-1;
pat->newData[k][DIV_PAT_INS]=reader.readC();
if (pat->newData[k][DIV_PAT_INS]==0x80 || pat->newData[k][DIV_PAT_INS]==0xff) pat->newData[k][DIV_PAT_INS]=-1;
}
}
}
@ -1630,12 +1634,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
bool relWarning=false;
for (int i=0; i<getChannelCount(sys); i++) {
short note, octave;
w->writeC(curPat[i].effectCols);
for (int j=0; j<curSubSong->ordersLen; j++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
for (int k=0; k<curSubSong->patLen; k++) {
if ((pat->data[k][0]==101 || pat->data[k][0]==102) && pat->data[k][1]==0) {
if (pat->newData[k][DIV_PAT_NOTE]==DIV_NOTE_REL || pat->newData[k][DIV_PAT_NOTE]==DIV_MACRO_REL) {
w->writeS(100);
w->writeS(0);
if (!relWarning) {
@ -1643,18 +1648,19 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
addWarning("note/macro release will be converted to note off!");
}
} else {
w->writeS(pat->data[k][0]); // note
w->writeS(pat->data[k][1]); // octave
noteToSplitNote(pat->newData[k][DIV_PAT_NOTE],note,octave);
w->writeS(note); // note
w->writeS(octave); // octave
}
w->writeS(pat->data[k][3]); // volume
w->writeS(pat->newData[k][DIV_PAT_VOL]); // volume
#ifdef TA_BIG_ENDIAN
for (int l=0; l<curPat[i].effectCols*2; l++) {
w->writeS(pat->data[k][4+l]);
w->writeS(pat->newData[k][DIV_PAT_FX(0)+l]);
}
#else
w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects
w->write(&pat->newData[k][DIV_PAT_FX(0)],2*curPat[i].effectCols*2); // effects
#endif
w->writeS(pat->data[k][2]); // instrument
w->writeS(pat->newData[k][DIV_PAT_INS]); // instrument
}
}
}

View file

@ -418,8 +418,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->orders.ord[j][i]=i;
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
if (j==3 && seq[i].speed) {
p->data[0][6]=0x0f;
p->data[0][7]=seq[i].speed;
p->newData[0][DIV_PAT_FX(1)]=0x0f;
p->newData[0][DIV_PAT_FXVAL(1)]=seq[i].speed;
}
bool ignoreNext=false;
@ -428,80 +428,65 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
FCPattern& fp=pat[seq[i].pat[j]];
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 octave=2+((fp.note[k]+seq[i].transpose[j])/12);
if (fp.note[k]>=0x3d) octave-=6;
if (note==0) {
note=12;
octave--;
}
octave&=0xff;
p->data[k][0]=note;
p->data[k][1]=octave;
p->newData[k][DIV_PAT_NOTE]=fp.note[k]+seq[i].transpose[j]+84;
// wrap-around if the note is too high
if (fp.note[k]>=0x3d) p->newData[k][DIV_PAT_NOTE]-=6*12;
if (isSliding[j]) {
isSliding[j]=false;
p->data[k][4]=2;
p->data[k][5]=0;
p->newData[k][DIV_PAT_FX(0)]=2;
p->newData[k][DIV_PAT_FXVAL(0)]=0;
}
} else if (fp.note[k]==0x49) {
if (k>0) {
p->data[k-1][4]=0x0d;
p->data[k-1][5]=0;
p->newData[k-1][DIV_PAT_FX(0)]=0x0d;
p->newData[k-1][DIV_PAT_FXVAL(0)]=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;
p->newData[0][DIV_PAT_INS]=lastIns[j];
p->newData[0][DIV_PAT_FX(0)]=0x03;
p->newData[0][DIV_PAT_FXVAL(0)]=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;
p->newData[k][DIV_PAT_NOTE]=lastNote[j]+seq[i].transpose[j]+84;
// wrap-around if the note is too high
if (lastNote[j]>=0x3d) p->newData[k][DIV_PAT_NOTE]-=6*12;
}
if (fp.val[k]) {
if (ignoreNext) {
ignoreNext=false;
} else {
if (fp.val[k]==0xf0) {
p->data[k][0]=100;
p->data[k][1]=0;
p->data[k][2]=-1;
p->newData[k][DIV_PAT_NOTE]=DIV_NOTE_OFF;
p->newData[k][DIV_PAT_INS]=-1;
} else if (fp.val[k]&0xe0) {
if (fp.val[k]&0x40) {
p->data[k][4]=2;
p->data[k][5]=0;
p->newData[k][DIV_PAT_FX(0)]=2;
p->newData[k][DIV_PAT_FXVAL(0)]=0;
isSliding[j]=false;
} else if (fp.val[k]&0x80) {
isSliding[j]=true;
if (k<31) {
if (fp.val[k+1]&0x20) {
p->data[k][4]=2;
p->data[k][5]=fp.val[k+1]&0x1f;
p->newData[k][DIV_PAT_FX(0)]=2;
p->newData[k][DIV_PAT_FXVAL(0)]=fp.val[k+1]&0x1f;
} else {
p->data[k][4]=1;
p->data[k][5]=fp.val[k+1]&0x1f;
p->newData[k][DIV_PAT_FX(0)]=1;
p->newData[k][DIV_PAT_FXVAL(0)]=fp.val[k+1]&0x1f;
}
ignoreNext=true;
} else {
p->data[k][4]=2;
p->data[k][5]=0;
p->newData[k][DIV_PAT_FX(0)]=2;
p->newData[k][DIV_PAT_FXVAL(0)]=0;
}
}
} else {
p->data[k][2]=(fp.val[k]+seq[i].offsetIns[j])&0x3f;
lastIns[j]=p->data[k][2];
p->newData[k][DIV_PAT_INS]=(fp.val[k]+seq[i].offsetIns[j])&0x3f;
lastIns[j]=p->newData[k][DIV_PAT_INS];
}
}
} else if (fp.note[k]>0 && fp.note[k]<0x49) {
p->data[k][2]=seq[i].offsetIns[j];
lastIns[j]=p->data[k][2];
p->newData[k][DIV_PAT_INS]=seq[i].offsetIns[j];
lastIns[j]=p->newData[k][DIV_PAT_INS];
}
}
}

View file

@ -270,7 +270,7 @@ int convert_vrc6_duties[4] = {1, 3, 7, 3};
int findEmptyFx(short* data) {
for (int i=0; i<7; i++) {
if (data[4+i*2]==-1) return i;
if (data[DIV_PAT_FX(i)]==-1) return i;
}
return -1;
}
@ -1752,17 +1752,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (map_channels[ch] != 0xff) {
if (nextNote == 0x0d) {
pat->data[row][0] = 101;
pat->newData[row][DIV_PAT_NOTE] = DIV_NOTE_REL;
} else if (nextNote == 0x0e) {
pat->data[row][0] = 100;
} else if (nextNote == 0x01) {
pat->data[row][0] = 12;
pat->data[row][1] = nextOctave - 1;
pat->newData[row][DIV_PAT_NOTE] = DIV_NOTE_OFF;
} else if (nextNote == 0) {
pat->data[row][0] = 0;
pat->newData[row][DIV_PAT_NOTE] = -1;
} else if (nextNote < 0x0d) {
pat->data[row][0] = nextNote - 1;
pat->data[row][1] = nextOctave;
pat->newData[row][DIV_PAT_NOTE] = nextOctave*12 + (nextNote - 1) + 60;
}
}
@ -1770,27 +1766,27 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
// TODO: you sure about 0xff?
if (map_channels[ch] != 0xff) {
if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) {
pat->data[row][2] = nextIns;
pat->newData[row][DIV_PAT_INS] = nextIns;
} else {
pat->data[row][2] = -1;
pat->newData[row][DIV_PAT_INS] = -1;
}
}
unsigned char nextVol = reader.readC();
if (map_channels[ch] != 0xff) {
if (nextVol < 0x10) {
pat->data[row][3] = nextVol;
pat->newData[row][DIV_PAT_VOL] = nextVol;
if (map_channels[ch] == vrc6_saw_chan) // scale volume
{
// TODO: shouldn't it be 32?
pat->data[row][3] = (pat->data[row][3] * 42) / 15;
pat->newData[row][DIV_PAT_VOL] = (pat->newData[row][DIV_PAT_VOL] * 42) / 15;
}
if (map_channels[ch] == fds_chan) {
pat->data[row][3] = (pat->data[row][3] * 31) / 15;
pat->newData[row][DIV_PAT_VOL] = (pat->newData[row][DIV_PAT_VOL] * 31) / 15;
}
} else {
pat->data[row][3] = -1;
pat->newData[row][DIV_PAT_VOL] = -1;
}
}
@ -1829,15 +1825,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (nextEffect == FT_EF_SPEED && nextEffectVal < 20)
nextEffectVal++;
if (pat->data[row][3] == 0)
pat->data[row][3] = 0xf;
if (pat->newData[row][DIV_PAT_VOL] == 0)
pat->newData[row][DIV_PAT_VOL] = 0xf;
else {
pat->data[row][3]--;
pat->data[row][3] &= 0x0F;
pat->newData[row][DIV_PAT_VOL]--;
pat->newData[row][DIV_PAT_VOL] &= 0x0F;
}
if (pat->data[row][0] == 0)
pat->data[row][2] = -1;
if (pat->newData[row][DIV_PAT_NOTE] == -1)
pat->newData[row][DIV_PAT_INS] = -1;
}
if (blockVersion == 3) {
@ -1902,43 +1898,43 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (map_channels[ch] != 0xff) {
if (nextEffect == 0 && nextEffectVal == 0) {
pat->data[row][4 + (j * 2)] = -1;
pat->data[row][5 + (j * 2)] = -1;
pat->newData[row][DIV_PAT_FX(j)] = -1;
pat->newData[row][DIV_PAT_FXVAL(j)] = -1;
} else {
if ((eft && nextEffect<eftEffectMapSize) || (!eft && nextEffect<ftEffectMapSize)) {
if (eft) {
pat->data[row][4 + (j * 2)] = eftEffectMap[nextEffect];
pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
pat->newData[row][DIV_PAT_FX(j)] = eftEffectMap[nextEffect];
pat->newData[row][DIV_PAT_FXVAL(j)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
if (pat->data[row][4 + (j * 2)] == 0x100) {
pat->data[row][3] += pat->data[row][5 + (j * 2)] ? 0x10 : 0; // extra volume bit for AY8930
pat->data[row][4 + (j * 2)] = -1;
pat->data[row][5 + (j * 2)] = -1;
if (pat->newData[row][DIV_PAT_FX(j)] == 0x100) {
pat->newData[row][DIV_PAT_VOL] += pat->newData[row][DIV_PAT_FXVAL(j)] ? 0x10 : 0; // extra volume bit for AY8930
pat->newData[row][DIV_PAT_FX(j)] = -1;
pat->newData[row][DIV_PAT_FXVAL(j)] = -1;
}
if (eftEffectMap[nextEffect] == 0x0f && nextEffectVal > 0x1f) {
pat->data[row][4 + (j * 2)] = 0xfd; // BPM speed change!
pat->newData[row][DIV_PAT_FX(j)] = 0xfd; // BPM speed change!
}
if ((eftEffectMap[nextEffect] == 0xe1 || eftEffectMap[nextEffect] == 0xe2) && (nextEffectVal & 0xf0) == 0) {
pat->data[row][5 + (j * 2)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed!
pat->newData[row][DIV_PAT_FXVAL(j)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed!
}
} else {
pat->data[row][4 + (j * 2)] = ftEffectMap[nextEffect];
pat->data[row][5 + (j * 2)] = ftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
pat->newData[row][DIV_PAT_FX(j)] = ftEffectMap[nextEffect];
pat->newData[row][DIV_PAT_FXVAL(j)] = ftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
if (ftEffectMap[nextEffect] == 0x0f && nextEffectVal > 0x1f) {
pat->data[row][4 + (j * 2)] = 0xfd; // BPM speed change!
pat->newData[row][DIV_PAT_FX(j)] = 0xfd; // BPM speed change!
}
if ((ftEffectMap[nextEffect] == 0xe1 || ftEffectMap[nextEffect] == 0xe2) && (nextEffectVal & 0xf0) == 0) {
pat->data[row][5 + (j * 2)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed!
pat->newData[row][DIV_PAT_FXVAL(j)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed!
}
}
for (int v = 0; v < 8; v++) {
if (map_channels[ch] == n163_chans[v]) {
if (pat->data[row][4 + (j * 2)] == 0x12) {
pat->data[row][4 + (j * 2)] = 0x110; // N163 wave change (we'll map this later)
if (pat->newData[row][DIV_PAT_FX(j)] == 0x12) {
pat->newData[row][DIV_PAT_FX(j)] = 0x110; // N163 wave change (we'll map this later)
}
}
}
@ -1947,23 +1943,24 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
{
if (map_channels[ch] == vrc7_chans[vrr])
{
if (pat->data[row][4 + (j * 2)] == 0x12)
if (pat->newData[row][DIV_PAT_FX(j)] == 0x12)
{
pat->data[row][4 + (j * 2)] = 0x10; // set VRC7 patch
pat->newData[row][DIV_PAT_FX(j)] = 0x10; // set VRC7 patch
}
}
}
for (int v = 0; v < 3; v++) {
if (map_channels[ch] == s5b_chans[v] || map_channels[ch] == ay8930_chans[v]) {
if (pat->data[row][4 + (j * 2)] == 0x22 && (pat->data[row][5 + (j * 2)] & 0xf0) != 0) {
pat->data[row][4 + (7 * 2)] = -666; //marker
if (pat->newData[row][DIV_PAT_FX(j)] == 0x22 && (pat->newData[row][DIV_PAT_FXVAL(j)] & 0xf0) != 0) {
// TODO: in the second stage of pattern refactor this will have to change.
pat->newData[row][DIV_PAT_FX(7)] = -666; //marker
}
}
}
} else {
pat->data[row][4 + (j * 2)] = -1;
pat->data[row][5 + (j * 2)] = -1;
pat->newData[row][DIV_PAT_FX(j)] = -1;
pat->newData[row][DIV_PAT_FXVAL(j)] = -1;
}
}
}
@ -2438,8 +2435,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.subsong[j]->pat[ii].data[k] == NULL)
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] > index) {
ds.subsong[j]->pat[ii].data[k]->data[l][2]--;
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] > index) {
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS]--;
}
}
}
@ -2456,12 +2453,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.subsong[j]->pat[ii].data[k] == NULL)
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
if (ds.subsong[j]->pat[ii].data[k]->data[l][4 + 7*2] == -666) {
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(7)] == -666) {
bool converted = false;
// for()? if()? THESE ARE NOT FUNCTIONS!
for (int hh = 0; hh < 7; hh++) { // oh and now you 1TBS up. oh man...
if (ds.subsong[j]->pat[ii].data[k]->data[l][4 + hh*2] == 0x22 && !converted) {
int slot = findEmptyFx(ds.subsong[j]->pat[ii].data[k]->data[l]);
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(hh)] == 0x22 && !converted) {
int slot = findEmptyFx(ds.subsong[j]->pat[ii].data[k]->newData[l]);
if (slot != -1) {
// space your comments damn it!
// Hxy - Envelope automatic pitch
@ -2469,7 +2466,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
// Sets envelope period to the note period shifted by x and envelope type y.
// Approximate envelope frequency is note frequency * (2^|x - 8|) / 32.
int ftAutoEnv = (ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] >> 4) & 15;
int ftAutoEnv = (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] >> 4) & 15;
int autoEnvDen = 16; // ???? with 32 it's an octave lower...
int autoEnvNum = (1 << (abs(ftAutoEnv - 8)));
@ -2479,18 +2476,18 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
if (autoEnvDen < 16 && autoEnvNum < 16) {
ds.subsong[j]->pat[ii].data[k]->data[l][4 + slot*2] = 0x29;
ds.subsong[j]->pat[ii].data[k]->data[l][5 + slot*2] = (autoEnvNum << 4) | autoEnvDen;
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(slot)] = 0x29;
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(slot)] = (autoEnvNum << 4) | autoEnvDen;
}
ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] = (ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] & 0xf) << 4;
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] = (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] & 0xf) << 4;
converted = true;
}
}
}
ds.subsong[j]->pat[ii].data[k]->data[l][4 + (7 * 2)] = -1; //delete marker
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(7)] = -1; //delete marker
}
}
}
@ -2506,10 +2503,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
for (int p = 0; p < s->ordersLen; p++) {
for (int r = 0; r < s->patLen; r++) {
DivPattern* pat = s->pat[c].getPattern(s->orders.ord[c][p], true);
short* s_row_data = pat->data[r];
short* s_row_data = pat->newData[r];
for (int eff = 0; eff < DIV_MAX_EFFECTS - 1; eff++) {
if (s_row_data[4 + 2 * eff] != -1 && eff + 1 > num_fx) {
if (s_row_data[DIV_PAT_FX(eff)] != -1 && eff + 1 > num_fx) {
num_fx = eff + 1;
}
}
@ -2556,7 +2553,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
// 1TBS > GNU
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) { // instrument
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) { // instrument
DivInstrument* ins = ds.ins[i];
bool go_to_end = false;
@ -2606,7 +2603,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.subsong[j]->pat[ii].data[k] == NULL)
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) // instrument
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) // instrument
{
DivInstrument* ins = ds.ins[i];
bool go_to_end = false;
@ -2658,7 +2655,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.subsong[j]->pat[ii].data[k] == NULL)
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) // instrument
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) // instrument
{
DivInstrument* ins = ds.ins[i];
bool go_to_end = false;
@ -2729,17 +2726,17 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.subsong[j]->pat[ii].data[k] == NULL)
continue;
for (int l = 0; l < ds.subsong[j]->patLen; l++) {
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_vrc6_conv[i][0] && (ii == vrc6_chans[0] || ii == vrc6_chans[1])) // change ins index
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_vrc6_conv[i][0] && (ii == vrc6_chans[0] || ii == vrc6_chans[1])) // change ins index
{
ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_vrc6_conv[i][1];
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_vrc6_conv[i][1];
}
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_vrc6_saw_conv[i][0] && ii == vrc6_saw_chan) {
ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_vrc6_saw_conv[i][1];
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_vrc6_saw_conv[i][0] && ii == vrc6_saw_chan) {
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_vrc6_saw_conv[i][1];
}
if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_nes_conv[i][0] && (ii == mmc5_chans[0] || ii == mmc5_chans[1] || ii < 5)) {
ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_nes_conv[i][1];
if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_nes_conv[i][0] && (ii == mmc5_chans[0] || ii == mmc5_chans[1] || ii < 5)) {
ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_nes_conv[i][1];
}
}
}
@ -2785,19 +2782,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],true);
for (int l=0; l<i->patLen; l++) {
// check for instrument change
if (p->data[l][2]!=-1) {
curWaveOff=n163WaveOff[p->data[l][2]&127];
if (p->newData[l][DIV_PAT_INS]!=-1) {
curWaveOff=n163WaveOff[p->newData[l][DIV_PAT_INS]&127];
}
// check effect columns for 0x110 (dummy wave change)
for (int m=0; m<i->pat[j].effectCols; m++) {
if (p->data[l][4+(m<<1)]==0x110) {
if (p->newData[l][DIV_PAT_FX(m)]==0x110) {
// map wave
p->data[l][4+(m<<1)]=0x10;
if (p->data[l][5+(m<<1)]==-1) {
p->data[l][5+(m<<1)]=curWaveOff&0xff;
p->newData[l][DIV_PAT_FX(m)]=0x10;
if (p->newData[l][DIV_PAT_FXVAL(m)]==-1) {
p->newData[l][DIV_PAT_FXVAL(m)]=curWaveOff&0xff;
} else {
p->data[l][5+(m<<1)]=(p->data[l][5+(m<<1)]+curWaveOff)&0xff;
p->newData[l][DIV_PAT_FXVAL(m)]=(p->newData[l][DIV_PAT_FXVAL(m)]+curWaveOff)&0xff;
}
}
}

View file

@ -1778,32 +1778,28 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
if (mask&1) { // note
unsigned char note=reader.readC();
// TODO: PAT2 format with new off/===/rel values!
if (note==180) {
pat->data[j][0]=100;
pat->data[j][1]=0;
pat->newData[j][0]=DIV_NOTE_OFF;
} else if (note==181) {
pat->data[j][0]=101;
pat->data[j][1]=0;
pat->newData[j][0]=DIV_NOTE_REL;
} else if (note==182) {
pat->data[j][0]=102;
pat->data[j][1]=0;
pat->newData[j][0]=DIV_MACRO_REL;
} else if (note<180) {
pat->data[j][0]=newFormatNotes[note];
pat->data[j][1]=newFormatOctaves[note];
pat->newData[j][DIV_PAT_NOTE]=note;
} else {
pat->data[j][0]=0;
pat->data[j][1]=0;
pat->newData[j][0]=-1;
}
}
if (mask&2) { // instrument
pat->data[j][2]=(unsigned char)reader.readC();
pat->newData[j][DIV_PAT_INS]=(unsigned char)reader.readC();
}
if (mask&4) { // volume
pat->data[j][3]=(unsigned char)reader.readC();
pat->newData[j][DIV_PAT_VOL]=(unsigned char)reader.readC();
}
for (unsigned char k=0; k<16; k++) {
if (effectMask&(1<<k)) {
pat->data[j][4+k]=(unsigned char)reader.readC();
pat->newData[j][DIV_PAT_FX(0)+k]=(unsigned char)reader.readC();
}
}
}
@ -1844,18 +1840,20 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
pat->data[j][0]=reader.readS();
pat->data[j][1]=reader.readS();
pat->data[j][2]=reader.readS();
pat->data[j][3]=reader.readS();
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
pat->data[j][4+(k<<1)]=reader.readS();
pat->data[j][5+(k<<1)]=reader.readS();
short note=reader.readS();
short octave=reader.readS();
if (note==0 && octave!=0) {
logD("what? %d:%d:%d note %d octave %d",chan,i,j,note,octave);
note=12;
octave--;
}
if (pat->data[j][0]==0 && pat->data[j][1]!=0) {
logD("what? %d:%d:%d note %d octave %d",chan,i,j,pat->data[j][0],pat->data[j][1]);
pat->data[j][0]=12;
pat->data[j][1]--;
pat->newData[j][DIV_PAT_NOTE]=splitNoteToNote(note,octave);
pat->newData[j][DIV_PAT_INS]=reader.readS();
pat->newData[j][DIV_PAT_VOL]=reader.readS();
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
pat->newData[j][DIV_PAT_FX(k)]=reader.readS();
pat->newData[j][DIV_PAT_FXVAL(k)]=reader.readS();
}
}
@ -2171,7 +2169,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
return true;
}
SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
SafeWriter* DivEngine::saveFur(bool notPrimary) {
saveLock.lock();
std::vector<int> subSongPtr;
std::vector<int> sysFlagsPtr;
@ -2622,133 +2620,100 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
patPtr.push_back(w->tell());
if (newPatternFormat) {
w->write("PATN",4);
blockStartSeek=w->tell();
w->writeI(0);
w->write("PATN",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeC(i.subsong);
w->writeC(i.chan);
w->writeS(i.pat);
w->writeString(pat->name,false);
w->writeC(i.subsong);
w->writeC(i.chan);
w->writeS(i.pat);
w->writeString(pat->name,false);
unsigned char emptyRows=0;
unsigned char emptyRows=0;
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
unsigned char mask=0;
unsigned char finalNote=255;
unsigned short effectMask=0;
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
unsigned char mask=0;
unsigned char finalNote=255;
unsigned short effectMask=0;
if (pat->data[j][0]==100) {
finalNote=180;
} else if (pat->data[j][0]==101) { // note release
finalNote=181;
} else if (pat->data[j][0]==102) { // macro release
finalNote=182;
} else if (pat->data[j][1]==0 && pat->data[j][0]==0) {
finalNote=255;
} else {
int seek=(pat->data[j][0]+(signed char)pat->data[j][1]*12)+60;
if (seek<0 || seek>=180) {
finalNote=255;
} else {
finalNote=seek;
}
}
if (finalNote!=255) mask|=1; // note
if (pat->data[j][2]!=-1) mask|=2; // instrument
if (pat->data[j][3]!=-1) mask|=4; // volume
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k+=2) {
if (k==0) {
if (pat->data[j][4+k]!=-1) mask|=8;
if (pat->data[j][5+k]!=-1) mask|=16;
} else if (k<8) {
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=32;
} else {
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=64;
}
if (pat->data[j][4+k]!=-1) effectMask|=(1<<k);
if (pat->data[j][5+k]!=-1) effectMask|=(2<<k);
}
if (mask==0) {
emptyRows++;
if (emptyRows>127) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
}
} else {
if (emptyRows>1) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
} else if (emptyRows) {
w->writeC(0);
emptyRows=0;
}
w->writeC(mask);
if (mask&32) w->writeC(effectMask&0xff);
if (mask&64) w->writeC((effectMask>>8)&0xff);
if (mask&1) w->writeC(finalNote);
if (mask&2) w->writeC(pat->data[j][2]);
if (mask&4) w->writeC(pat->data[j][3]);
if (mask&8) w->writeC(pat->data[j][4]);
if (mask&16) w->writeC(pat->data[j][5]);
if (mask&32) {
if (effectMask&4) w->writeC(pat->data[j][6]);
if (effectMask&8) w->writeC(pat->data[j][7]);
if (effectMask&16) w->writeC(pat->data[j][8]);
if (effectMask&32) w->writeC(pat->data[j][9]);
if (effectMask&64) w->writeC(pat->data[j][10]);
if (effectMask&128) w->writeC(pat->data[j][11]);
}
if (mask&64) {
if (effectMask&256) w->writeC(pat->data[j][12]);
if (effectMask&512) w->writeC(pat->data[j][13]);
if (effectMask&1024) w->writeC(pat->data[j][14]);
if (effectMask&2048) w->writeC(pat->data[j][15]);
if (effectMask&4096) w->writeC(pat->data[j][16]);
if (effectMask&8192) w->writeC(pat->data[j][17]);
if (effectMask&16384) w->writeC(pat->data[j][18]);
if (effectMask&32768) w->writeC(pat->data[j][19]);
}
}
if (pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_OFF) { // note off
finalNote=180;
} else if (pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_REL) { // note release
finalNote=181;
} else if (pat->newData[j][DIV_PAT_NOTE]==DIV_MACRO_REL) { // macro release
finalNote=182;
} else if (pat->newData[j][DIV_PAT_NOTE]==-1) { // empty
finalNote=255;
} else {
finalNote=pat->newData[j][DIV_PAT_NOTE];
}
// stop
w->writeC(0xff);
} else {
w->write("PATR",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeS(i.chan);
w->writeS(i.pat);
w->writeS(i.subsong);
w->writeS(0); // reserved
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
w->writeS(pat->data[j][0]); // note
w->writeS(pat->data[j][1]); // octave
w->writeS(pat->data[j][2]); // instrument
w->writeS(pat->data[j][3]); // volume
#ifdef TA_BIG_ENDIAN
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k++) {
w->writeS(pat->data[j][4+k]);
if (finalNote!=255) mask|=1; // note
if (pat->newData[j][DIV_PAT_INS]!=-1) mask|=2; // instrument
if (pat->newData[j][DIV_PAT_VOL]!=-1) mask|=4; // volume
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k+=2) {
if (k==0) {
if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1) mask|=8;
if (pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=16;
} else if (k<8) {
if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1 || pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=32;
} else {
if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1 || pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=64;
}
#else
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
#endif
if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1) effectMask|=(1<<k);
if (pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) effectMask|=(2<<k);
}
w->writeString(pat->name,false);
if (mask==0) {
emptyRows++;
if (emptyRows>127) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
}
} else {
if (emptyRows>1) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
} else if (emptyRows) {
w->writeC(0);
emptyRows=0;
}
w->writeC(mask);
if (mask&32) w->writeC(effectMask&0xff);
if (mask&64) w->writeC((effectMask>>8)&0xff);
if (mask&1) w->writeC(finalNote);
if (mask&2) w->writeC(pat->newData[j][DIV_PAT_INS]);
if (mask&4) w->writeC(pat->newData[j][DIV_PAT_VOL]);
if (mask&8) w->writeC(pat->newData[j][DIV_PAT_FX(0)]);
if (mask&16) w->writeC(pat->newData[j][DIV_PAT_FXVAL(0)]);
if (mask&32) {
if (effectMask&4) w->writeC(pat->newData[j][DIV_PAT_FX(1)]);
if (effectMask&8) w->writeC(pat->newData[j][DIV_PAT_FXVAL(1)]);
if (effectMask&16) w->writeC(pat->newData[j][DIV_PAT_FX(2)]);
if (effectMask&32) w->writeC(pat->newData[j][DIV_PAT_FXVAL(2)]);
if (effectMask&64) w->writeC(pat->newData[j][DIV_PAT_FX(3)]);
if (effectMask&128) w->writeC(pat->newData[j][DIV_PAT_FXVAL(3)]);
}
if (mask&64) {
if (effectMask&256) w->writeC(pat->newData[j][DIV_PAT_FX(4)]);
if (effectMask&512) w->writeC(pat->newData[j][DIV_PAT_FXVAL(4)]);
if (effectMask&1024) w->writeC(pat->newData[j][DIV_PAT_FX(5)]);
if (effectMask&2048) w->writeC(pat->newData[j][DIV_PAT_FXVAL(5)]);
if (effectMask&4096) w->writeC(pat->newData[j][DIV_PAT_FX(6)]);
if (effectMask&8192) w->writeC(pat->newData[j][DIV_PAT_FXVAL(6)]);
if (effectMask&16384) w->writeC(pat->newData[j][DIV_PAT_FX(7)]);
if (effectMask&32768) w->writeC(pat->newData[j][DIV_PAT_FXVAL(7)]);
}
}
}
// stop
w->writeC(0xff);
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);

View file

@ -1062,7 +1062,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
int readRow=0;
bool mustCommitInitial=true;
memset(effectCol,4,64);
memset(effectCol,0,64);
memset(vibStatus,0,64);
memset(vibStatusChanged,0,64*sizeof(bool));
memset(vibing,0,64*sizeof(bool));
@ -1139,86 +1139,86 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
for (int j=0; j<64; j++) {
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
if (vibing[j]!=vibingOld[j] || vibStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x04;
p->data[readRow][effectCol[j]++]=vibing[j]?vibStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=vibing[j]?vibStatus[j]:0;
doesVibrato[j]=true;
} else if (doesVibrato[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x04;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (volSliding[j]!=volSlidingOld[j] || volSlideStatusChanged[j]) {
if (volSlideStatus[j]>=0xf1 && volSliding[j]) {
p->data[readRow][effectCol[j]++]=0xf9;
p->data[readRow][effectCol[j]++]=volSlideStatus[j]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf9;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]&15;
volSliding[j]=false;
} else if ((volSlideStatus[j]&15)==15 && volSlideStatus[j]>=0x10 && volSliding[j]) {
p->data[readRow][effectCol[j]++]=0xf8;
p->data[readRow][effectCol[j]++]=volSlideStatus[j]>>4;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf8;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]>>4;
volSliding[j]=false;
} else {
p->data[readRow][effectCol[j]++]=0xfa;
p->data[readRow][effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0;
}
doesVolSlide[j]=true;
} else if (doesVolSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0xfa;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (porting[j]!=portingOld[j] || portaStatusChanged[j]) {
if (portaStatus[j]>=0xe0 && portaType[j]!=3 && porting[j]) {
p->data[readRow][effectCol[j]++]=portaType[j]|0xf0;
p->data[readRow][effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]|0xf0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1);
porting[j]=false;
} else {
p->data[readRow][effectCol[j]++]=portaType[j];
p->data[readRow][effectCol[j]++]=porting[j]?portaStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=porting[j]?portaStatus[j]:0;
}
doesPitchSlide[j]=true;
} else if (doesPitchSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x01;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x01;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (arping[j]!=arpingOld[j] || arpStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x00;
p->data[readRow][effectCol[j]++]=arping[j]?arpStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=arping[j]?arpStatus[j]:0;
doesArp[j]=true;
} else if (doesArp[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x00;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (treming[j]!=tremingOld[j] || tremStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x07;
p->data[readRow][effectCol[j]++]=treming[j]?tremStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=treming[j]?tremStatus[j]:0;
doesTremolo[j]=true;
} else if (doesTremolo[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x07;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (panning[j]!=panningOld[j] || panStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x84;
p->data[readRow][effectCol[j]++]=panning[j]?panStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panning[j]?panStatus[j]:0;
doesPanbrello[j]=true;
} else if (doesPanbrello[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x84;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (panSliding[j]!=panSlidingOld[j] || panSlideStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x83;
p->data[readRow][effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0;
doesPanSlide[j]=true;
} else if (doesPanSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x83;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if ((effectCol[j]>>1)-2>ds.subsong[0]->pat[j].effectCols) {
ds.subsong[0]->pat[j].effectCols=(effectCol[j]>>1)-1;
if ((effectCol[j]>>1)>=ds.subsong[0]->pat[j].effectCols) {
ds.subsong[0]->pat[j].effectCols=(effectCol[j]>>1)+1;
}
}
@ -1250,16 +1250,16 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
if (readRow>0) {
// place end of pattern marker
DivPattern* p=ds.subsong[0]->pat[0].getPattern(i,true);
p->data[readRow-1][effectCol[0]++]=0x0d;
p->data[readRow-1][effectCol[0]++]=0;
p->newData[readRow-1][DIV_PAT_FX(0)+effectCol[0]++]=0x0d;
p->newData[readRow-1][DIV_PAT_FX(0)+effectCol[0]++]=0;
if ((effectCol[0]>>1)-2>ds.subsong[0]->pat[0].effectCols) {
ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)-1;
if ((effectCol[0]>>1)>=ds.subsong[0]->pat[0].effectCols) {
ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)+1;
}
}
break;
}
memset(effectCol,4,64);
memset(effectCol,0,64);
continue;
}
@ -1302,25 +1302,17 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
if (hasNote) {
if (note[chan]==255) { // note release
p->data[readRow][0]=101;
p->data[readRow][1]=0;
p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_REL;
} else if (note[chan]==254) { // note off
p->data[readRow][0]=100;
p->data[readRow][1]=0;
p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_OFF;
} else if (note[chan]<120) {
p->data[readRow][0]=note[chan]%12;
p->data[readRow][1]=note[chan]/12;
if (p->data[readRow][0]==0) {
p->data[readRow][0]=12;
p->data[readRow][1]--;
}
p->newData[readRow][DIV_PAT_NOTE]=note[chan]+60;
} else { // note fade, but Furnace does not support that
p->data[readRow][0]=102;
p->data[readRow][1]=0;
p->newData[readRow][DIV_PAT_NOTE]=DIV_MACRO_REL;
}
}
if (hasIns) {
p->data[readRow][2]=ins[chan]-1;
p->newData[readRow][DIV_PAT_INS]=ins[chan]-1;
if ((note[chan]<120 || ds.insLen==0) && ins[chan]>0) {
unsigned char targetPan=0;
if (ds.insLen==0) {
@ -1332,26 +1324,26 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
}
}
if (targetPan&128) {
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=CLAMP((targetPan&127)<<2,0,255);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=CLAMP((targetPan&127)<<2,0,255);
}
}
if (hasNote && (note[chan]<120 || ds.insLen==0) && ins[chan]>0) {
if (ds.insLen==0) {
p->data[readRow][3]=defVol[(ins[chan]-1)&255];
p->newData[readRow][DIV_PAT_VOL]=defVol[(ins[chan]-1)&255];
} else {
p->data[readRow][3]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]];
p->newData[readRow][DIV_PAT_VOL]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]];
}
}
}
if (hasVol) {
if (vol[chan]<=64) {
p->data[readRow][3]=vol[chan];
p->newData[readRow][DIV_PAT_VOL]=vol[chan];
} else { // effects in volume column
if (vol[chan]>=128 && vol[chan]<=192) { // panning
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=CLAMP((vol[chan]-128)<<2,0,255);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=CLAMP((vol[chan]-128)<<2,0,255);
} else if (vol[chan]>=65 && vol[chan]<=74) { // fine vol up
} else if (vol[chan]>=75 && vol[chan]<=84) { // fine vol down
} else if (vol[chan]>=85 && vol[chan]<=94) { // vol slide up
@ -1398,16 +1390,16 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
if (hasEffect) {
switch (effect[chan]+'A'-1) {
case 'A': // speed
p->data[readRow][effectCol[chan]++]=0x0f;
p->data[readRow][effectCol[chan]++]=effectVal[chan];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0f;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan];
break;
case 'B': // go to order
p->data[readRow][effectCol[chan]++]=0x0b;
p->data[readRow][effectCol[chan]++]=orders[effectVal[chan]];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0b;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=orders[effectVal[chan]];
break;
case 'C': // next order
p->data[readRow][effectCol[chan]++]=0x0d;
p->data[readRow][effectCol[chan]++]=effectVal[chan];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan];
break;
case 'D': // vol slide
if (effectVal[chan]!=0) {
@ -1490,8 +1482,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
case 'N': // channel vol slide
break;
case 'O': // offset
p->data[readRow][effectCol[chan]++]=0x91;
p->data[readRow][effectCol[chan]++]=effectVal[chan];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x91;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan];
break;
case 'P': // pan slide
if (effectVal[chan]!=0) {
@ -1504,8 +1496,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
if (effectVal[chan]!=0) {
lastRetrig[chan]=effectVal[chan];
}
p->data[readRow][effectCol[chan]++]=0x0c;
p->data[readRow][effectCol[chan]++]=lastRetrig[chan]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=lastRetrig[chan]&15;
break;
case 'R': // tremolo
if (effectVal[chan]!=0) {
@ -1519,77 +1511,77 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
case 0x3: // vibrato waveform
switch (effectVal[chan]&3) {
case 0x0: // sine
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00;
break;
case 0x1: // ramp down
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x05;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x05;
break;
case 0x2: // square
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x06;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x06;
break;
case 0x3: // random
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x07;
break;
}
break;
case 0x7:
switch (effectVal[chan]&15) {
case 0x7: // volume envelope off
p->data[readRow][effectCol[chan]++]=0xf5;
p->data[readRow][effectCol[chan]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00;
break;
case 0x8: // volume envelope on
p->data[readRow][effectCol[chan]++]=0xf6;
p->data[readRow][effectCol[chan]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00;
break;
case 0x9: // panning envelope off
p->data[readRow][effectCol[chan]++]=0xf5;
p->data[readRow][effectCol[chan]++]=0x0c;
p->data[readRow][effectCol[chan]++]=0xf5;
p->data[readRow][effectCol[chan]++]=0x0d;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d;
break;
case 0xa: // panning envelope on
p->data[readRow][effectCol[chan]++]=0xf6;
p->data[readRow][effectCol[chan]++]=0x0c;
p->data[readRow][effectCol[chan]++]=0xf6;
p->data[readRow][effectCol[chan]++]=0x0d;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d;
break;
case 0xb: // pitch envelope off
p->data[readRow][effectCol[chan]++]=0xf5;
p->data[readRow][effectCol[chan]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x04;
break;
case 0xc: //pitch envelope on
p->data[readRow][effectCol[chan]++]=0xf6;
p->data[readRow][effectCol[chan]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x04;
break;
}
break;
case 0x8: // panning
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=(effectVal[chan]&15)<<4;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal[chan]&15)<<4;
break;
case 0xa: // offset (high nibble)
p->data[readRow][effectCol[chan]++]=0x92;
p->data[readRow][effectCol[chan]++]=effectVal[chan]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x92;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15;
break;
case 0xc: // note cut
p->data[readRow][effectCol[chan]++]=0xec;
p->data[readRow][effectCol[chan]++]=effectVal[chan]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xec;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15;
break;
case 0xd: // note delay
p->data[readRow][effectCol[chan]++]=0xed;
p->data[readRow][effectCol[chan]++]=effectVal[chan]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xed;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15;
break;
}
break;
case 'T': // tempo
if (effectVal[chan]>=0x20) {
p->data[readRow][effectCol[chan]++]=0xf0;
p->data[readRow][effectCol[chan]++]=effectVal[chan];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan];
}
break;
case 'U': // fine vibrato
@ -1604,8 +1596,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
case 'W': // global volume slide (!)
break;
case 'X': // panning
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=effectVal[chan];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan];
break;
case 'Y': // panbrello
if (effectVal[chan]!=0) {
@ -1690,18 +1682,18 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
for (int j=0; j<maxChan; j++) {
DivPattern* p=ds.subsong[i]->pat[j].getPattern(ds.subsong[i]->orders.ord[j][0],true);
for (int k=0; k<DIV_MAX_EFFECTS; k++) {
if (p->data[0][4+(k<<1)]==0x80) {
if (p->newData[0][DIV_PAT_FX(k)]==0x80) {
// give up if there's a panning effect already
break;
}
if (p->data[0][4+(k<<1)]==-1) {
if (p->newData[0][DIV_PAT_FX(k)]==-1) {
if ((chanPan[j]&127)==100) {
// should be surround...
p->data[0][4+(k<<1)]=0x80;
p->data[0][5+(k<<1)]=0x80;
p->newData[0][DIV_PAT_FX(k)]=0x80;
p->newData[0][DIV_PAT_FXVAL(k)]=0x80;
} else {
p->data[0][4+(k<<1)]=0x80;
p->data[0][5+(k<<1)]=CLAMP((chanPan[j]&127)<<2,0,255);
p->newData[0][DIV_PAT_FX(k)]=0x80;
p->newData[0][DIV_PAT_FXVAL(k)]=CLAMP((chanPan[j]&127)<<2,0,255);
}
if (ds.subsong[i]->pat[j].effectCols<=k) ds.subsong[i]->pat[j].effectCols=k+1;
break;

View file

@ -185,21 +185,20 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
}
for (int row=0; row<64; row++) {
for (int ch=0; ch<chCount; ch++) {
short* dstrow=chpats[ch]->data[row];
short* dstrowN=chpats[ch]->newData[row];
unsigned char data[4];
reader.read(&data,4);
// instrument
short ins=(data[0]&0xf0)|(data[2]>>4);
if (ins>0) {
dstrow[2]=ins-1;
dstrow[3]=defaultVols[ins-1];
dstrowN[DIV_PAT_INS]=ins-1;
dstrowN[DIV_PAT_VOL]=defaultVols[ins-1];
}
// note
int period=data[1]+((data[0]&0x0f)<<8);
if (period>0 && period<0x0fff) {
short note=(short)round(log2(3424.0/period)*12);
dstrow[0]=((note+11)%12)+1;
dstrow[1]=(note-1)/12+1;
dstrowN[DIV_PAT_NOTE]=note+72;
if (period<114) {
bypassLimits=true;
}
@ -207,8 +206,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
// effects are done later
short fxtyp=data[2]&0x0f;
short fxval=data[3];
dstrow[4]=fxtyp;
dstrow[5]=fxval;
dstrowN[DIV_PAT_FX(0)]=fxtyp;
dstrowN[DIV_PAT_FXVAL(0)]=fxval;
switch (fxtyp) {
case 0:
if (fxval!=0) fxUsage[ch][0]=true;
@ -256,7 +255,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
for (int ch=0; ch<=chCount; ch++) {
unsigned char fxCols=1;
for (int pat=0; pat<=patMax; pat++) {
auto* data=ds.subsong[0]->pat[ch].getPattern(pat,true)->data;
auto* newData=ds.subsong[0]->pat[ch].getPattern(pat,true)->newData;
short lastPitchEffect=-1;
short lastEffectState[5]={-1,-1,-1,-1,-1};
short setEffectState[5]={-1,-1,-1,-1,-1};
@ -264,11 +263,11 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
const short fxUsageTyp[5]={0x00,0x01,0x04,0x07,0xFA};
short effectState[5]={0,0,0,0,0};
unsigned char curFxCol=0;
short fxTyp=data[row][4];
short fxVal=data[row][5];
auto writeFxCol=[data,row,&curFxCol](short typ, short val) {
data[row][4+curFxCol*2]=typ;
data[row][5+curFxCol*2]=val;
short fxTyp=newData[row][DIV_PAT_FX(0)];
short fxVal=newData[row][DIV_PAT_FXVAL(0)];
auto writeFxCol=[newData,row,&curFxCol](short typ, short val) {
newData[row][DIV_PAT_FX(curFxCol)]=typ;
newData[row][DIV_PAT_FXVAL(curFxCol)]=val;
curFxCol++;
};
writeFxCol(-1,-1);
@ -293,7 +292,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
effectState[1]=fxVal;
if ((effectState[1]!=lastEffectState[1]) ||
(fxTyp!=lastPitchEffect) ||
(effectState[1]!=0 && data[row][0]>0)) {
(effectState[1]!=0 && newData[row][DIV_PAT_NOTE]>-1)) {
writeFxCol(fxTyp,fxVal);
}
lastPitchEffect=fxTyp;
@ -331,7 +330,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
writeFxCol(fxTyp,fxVal);
break;
case 12: // set vol
data[row][3]=MIN(0x40,fxVal);
newData[row][DIV_PAT_VOL]=MIN(0x40,fxVal);
break;
case 13: // break to row (BCD)
writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15));
@ -387,7 +386,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
for (int i=0; i<5; i++) {
// pitch slide and volume slide needs to be kept active on new note
// even after target/max is reached
if (fxUsage[ch][i] && (effectState[i]!=lastEffectState[i] || (effectState[i]!=0 && i==4 && data[row][3]>=0))) {
if (fxUsage[ch][i] && (effectState[i]!=lastEffectState[i] || (effectState[i]!=0 && i==4 && newData[row][DIV_PAT_VOL]>=0))) {
writeFxCol(fxUsageTyp[i],effectState[i]);
}
}

View file

@ -771,7 +771,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
bool mustCommitInitial=true;
memset(effectCol,4,32);
memset(effectCol,0,32);
memset(vibStatus,0,32);
memset(vibStatusChanged,0,32*sizeof(bool));
memset(vibing,0,32*sizeof(bool));
@ -811,95 +811,95 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
for (int j=0; j<32; j++) {
DivPattern* p=ds.subsong[0]->pat[chanMap[j]].getPattern(i,true);
if (vibing[j]!=vibingOld[j] || vibStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x04;
p->data[readRow][effectCol[j]++]=vibing[j]?vibStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=vibing[j]?vibStatus[j]:0;
doesVibrato[j]=true;
} else if (doesVibrato[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x04;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (volSliding[j]!=volSlidingOld[j] || volSlideStatusChanged[j]) {
if (volSlideStatus[j]>=0xf1 && volSliding[j]) {
p->data[readRow][effectCol[j]++]=0xf9;
p->data[readRow][effectCol[j]++]=volSlideStatus[j]&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf9;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]&15;
volSliding[j]=false;
} else if ((volSlideStatus[j]&15)==15 && volSlideStatus[j]>=0x10 && volSliding[j]) {
p->data[readRow][effectCol[j]++]=0xf8;
p->data[readRow][effectCol[j]++]=volSlideStatus[j]>>4;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf8;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]>>4;
volSliding[j]=false;
} else {
p->data[readRow][effectCol[j]++]=0xfa;
p->data[readRow][effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0;
}
doesVolSlide[j]=true;
} else if (doesVolSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0xfa;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (porting[j]!=portingOld[j] || portaStatusChanged[j]) {
if (portaStatus[j]>=0xe0 && portaType[j]!=3 && porting[j]) {
p->data[readRow][effectCol[j]++]=portaType[j]|0xf0;
p->data[readRow][effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]|0xf0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1);
porting[j]=false;
} else {
p->data[readRow][effectCol[j]++]=portaType[j];
p->data[readRow][effectCol[j]++]=porting[j]?portaStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=porting[j]?portaStatus[j]:0;
}
doesPitchSlide[j]=true;
} else if (doesPitchSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x01;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x01;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (arping[j]!=arpingOld[j] || arpStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x00;
p->data[readRow][effectCol[j]++]=arping[j]?arpStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=arping[j]?arpStatus[j]:0;
doesArp[j]=true;
} else if (doesArp[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x00;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (treming[j]!=tremingOld[j] || tremStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x07;
p->data[readRow][effectCol[j]++]=treming[j]?tremStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=treming[j]?tremStatus[j]:0;
doesTremolo[j]=true;
} else if (doesTremolo[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x07;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (panning[j]!=panningOld[j] || panStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x84;
p->data[readRow][effectCol[j]++]=panning[j]?panStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panning[j]?panStatus[j]:0;
doesPanbrello[j]=true;
} else if (doesPanbrello[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x84;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (panSliding[j]!=panSlidingOld[j] || panSlideStatusChanged[j]) {
p->data[readRow][effectCol[j]++]=0x83;
p->data[readRow][effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0;
doesPanSlide[j]=true;
} else if (doesPanSlide[j] && mustCommitInitial) {
p->data[readRow][effectCol[j]++]=0x83;
p->data[readRow][effectCol[j]++]=0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0;
}
if (effectCol[j]>=4+8*2) {
if (effectCol[j]>=8*2) {
logE("oh crap!");
}
if ((effectCol[j]>>1)-2>ds.subsong[0]->pat[j].effectCols) {
ds.subsong[0]->pat[chanMap[j]].effectCols=(effectCol[j]>>1)-1;
if ((effectCol[j]>>1)>=ds.subsong[0]->pat[j].effectCols) {
ds.subsong[0]->pat[chanMap[j]].effectCols=(effectCol[j]>>1)+1;
}
}
readRow++;
memset(effectCol,4,32);
memset(effectCol,0,32);
memcpy(vibingOld,vibing,32*sizeof(bool));
memcpy(volSlidingOld,volSliding,32*sizeof(bool));
memcpy(portingOld,porting,32*sizeof(bool));
@ -935,22 +935,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
unsigned char ins=reader.readC();
if (note==254) { // note off
p->data[readRow][0]=100;
p->data[readRow][1]=0;
p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_OFF;
} else if (note!=255) {
p->data[readRow][0]=note&15;
p->data[readRow][1]=note>>4;
if ((note&15)==0) {
p->data[readRow][0]=12;
p->data[readRow][1]--;
}
p->newData[readRow][DIV_PAT_NOTE]=(note&15)+(note>>4)*12+60;
}
p->data[readRow][2]=(short)ins-1;
p->newData[readRow][DIV_PAT_INS]=(short)ins-1;
}
if (hasVol) {
unsigned char vol=reader.readC();
if (vol==255) {
p->data[readRow][3]=-1;
p->newData[readRow][DIV_PAT_VOL]=-1;
} else {
// check for OPL channel
if ((chanSettings[chan]&31)>=16) {
@ -958,17 +952,17 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
} else {
if (vol>64) vol=64;
}
p->data[readRow][3]=vol;
p->newData[readRow][DIV_PAT_VOL]=vol;
}
} else if (p->data[readRow][2]!=-1) {
} else if (p->newData[readRow][DIV_PAT_INS]!=-1) {
// populate with instrument volume
unsigned char vol=defVol[p->data[readRow][2]&255];
unsigned char vol=defVol[p->newData[readRow][DIV_PAT_INS]&255];
if ((chanSettings[chan]&31)>=16) {
if (vol>63) vol=63;
} else {
if (vol>64) vol=64;
}
p->data[readRow][3]=vol;
p->newData[readRow][DIV_PAT_VOL]=vol;
}
if (hasEffect) {
unsigned char effect=reader.readC();
@ -976,17 +970,17 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
switch (effect+'A'-1) {
case 'A': // speed
p->data[readRow][effectCol[chan]++]=0x0f;
p->data[readRow][effectCol[chan]++]=effectVal;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0f;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal;
break;
case 'B': // go to order
p->data[readRow][effectCol[chan]++]=0x0b;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0b;
logD("0B: %x %x",effectVal,orders[effectVal]);
p->data[readRow][effectCol[chan]++]=orders[effectVal];
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=orders[effectVal];
break;
case 'C': // next order
p->data[readRow][effectCol[chan]++]=0x0d;
p->data[readRow][effectCol[chan]++]=(effectVal>>4)*10+(effectVal&15);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal>>4)*10+(effectVal&15);
break;
case 'D': // vol slide
if (effectVal!=0) {
@ -1078,8 +1072,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
case 'N': // channel vol slide (extension)
break;
case 'O': // offset
p->data[readRow][effectCol[chan]++]=0x91;
p->data[readRow][effectCol[chan]++]=effectVal;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x91;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal;
break;
case 'P': // pan slide (extension)
if (effectVal!=0) {
@ -1089,8 +1083,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
panSliding[chan]=true;
break;
case 'Q': // retrigger
p->data[readRow][effectCol[chan]++]=0x0c;
p->data[readRow][effectCol[chan]++]=effectVal&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15;
break;
case 'R': // tremolo
if (effectVal!=0) {
@ -1104,40 +1098,40 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
case 0x3: // vibrato waveform
switch (effectVal&3) {
case 0x0: // sine
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x00;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00;
break;
case 0x1: // ramp down
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x05;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x05;
break;
case 0x2: // square
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x06;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x06;
break;
case 0x3: // random
p->data[readRow][effectCol[chan]++]=0xe3;
p->data[readRow][effectCol[chan]++]=0x07;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x07;
break;
}
break;
case 0x8: // panning
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=(effectVal&15)<<4;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal&15)<<4;
break;
case 0xc: // note cut
p->data[readRow][effectCol[chan]++]=0xec;
p->data[readRow][effectCol[chan]++]=effectVal&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xec;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15;
break;
case 0xd: // note delay
p->data[readRow][effectCol[chan]++]=0xed;
p->data[readRow][effectCol[chan]++]=effectVal&15;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xed;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15;
break;
}
break;
case 'T': // tempo
p->data[readRow][effectCol[chan]++]=0xf0;
p->data[readRow][effectCol[chan]++]=effectVal;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf0;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal;
break;
case 'U': // fine vibrato
if (effectVal!=0) {
@ -1152,8 +1146,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
break;
case 'X': // panning (extension)
if (effectVal<=0x80) {
p->data[readRow][effectCol[chan]++]=0x80;
p->data[readRow][effectCol[chan]++]=(effectVal&0x80)?0xff:(effectVal<<1);
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80;
p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal&0x80)?0xff:(effectVal<<1);
}
break;
case 'Y': // panbrello (extension)
@ -1193,16 +1187,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
for (int j=0; j<16; j++) {
DivPattern* p=ds.subsong[i]->pat[chanMap[j]].getPattern(ds.subsong[i]->orders.ord[j][0],true);
for (int k=0; k<DIV_MAX_EFFECTS; k++) {
if (p->data[0][4+(k<<1)]==0x80) {
if (p->newData[0][DIV_PAT_FX(k)]==0x80) {
// give up if there's a panning effect already
break;
}
if (p->data[0][4+(k<<1)]==-1) {
p->data[0][4+(k<<1)]=0x80;
if (p->newData[0][DIV_PAT_FX(k)]==-1) {
p->newData[0][DIV_PAT_FX(k)]=0x80;
if (chanPan[j]&16) {
p->data[0][5+(k<<1)]=(j&1)?0xcc:0x33;
p->newData[0][DIV_PAT_FXVAL(k)]=(j&1)?0xcc:0x33;
} else {
p->data[0][5+(k<<1)]=(chanPan[j]&15)|((chanPan[j]&15)<<4);
p->newData[0][DIV_PAT_FXVAL(k)]=(chanPan[j]&15)|((chanPan[j]&15)<<4);
}
if (ds.subsong[i]->pat[chanMap[j]].effectCols<=k) ds.subsong[i]->pat[chanMap[j]].effectCols=k+1;
break;

View file

@ -321,9 +321,8 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
for (int l=0; l<chans; l++) {
DivPattern* p=s->pat[l].getPattern(s->orders.ord[l][j],false);
int note=p->data[k][0];
int octave=p->data[k][1];
short note, octave;
noteToSplitNote(p->newData[k][DIV_PAT_NOTE],note,octave);
if (note==0 && octave==0) {
w->writeText("|... ");
@ -344,28 +343,28 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
w->writeText(fmt::sprintf("|%s%d ",(octave<0)?notesNegative[note]:notes[note],(octave<0)?(-octave):octave));
}
if (p->data[k][2]==-1) {
if (p->newData[k][DIV_PAT_INS]==-1) {
w->writeText(".. ");
} else {
w->writeText(fmt::sprintf("%.2X ",p->data[k][2]&0xff));
w->writeText(fmt::sprintf("%.2X ",p->newData[k][DIV_PAT_INS]&0xff));
}
if (p->data[k][3]==-1) {
if (p->newData[k][DIV_PAT_VOL]==-1) {
w->writeText("..");
} else {
w->writeText(fmt::sprintf("%.2X",p->data[k][3]&0xff));
w->writeText(fmt::sprintf("%.2X",p->newData[k][DIV_PAT_VOL]&0xff));
}
for (int m=0; m<s->pat[l].effectCols; m++) {
if (p->data[k][4+(m<<1)]==-1) {
if (p->newData[k][DIV_PAT_FX(m)]==-1) {
w->writeText(" ..");
} else {
w->writeText(fmt::sprintf(" %.2X",p->data[k][4+(m<<1)]&0xff));
w->writeText(fmt::sprintf(" %.2X",p->newData[k][DIV_PAT_FX(m)]&0xff));
}
if (p->data[k][5+(m<<1)]==-1) {
if (p->newData[k][DIV_PAT_FXVAL(m)]==-1) {
w->writeText("..");
} else {
w->writeText(fmt::sprintf("%.2X",p->data[k][5+(m<<1)]&0xff));
w->writeText(fmt::sprintf("%.2X",p->newData[k][DIV_PAT_FXVAL(m)]&0xff));
}
}
}

View file

@ -254,16 +254,10 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
if (patDataBuf[k]==0) continue;
else if (patDataBuf[k]==1) {
// note off
pat->data[k][0]=100;
pat->newData[k][DIV_PAT_NOTE]=DIV_NOTE_OFF;
} else {
unsigned char invertedNote=~patDataBuf[k];
pat->data[k][0]=invertedNote%12;
pat->data[k][1]=(invertedNote/12)-1;
if (pat->data[k][0]==0) {
pat->data[k][0]=12;
pat->data[k][1]--;
}
pat->newData[k][DIV_PAT_NOTE]=invertedNote+60;
}
}
@ -273,7 +267,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
logD("parsing volumes of pattern %d channel %d",i,j);
for (int k=0; k<256; k++) {
if (patDataBuf[k]==0) continue;
else pat->data[k][3]=0x60+patDataBuf[k];
else pat->newData[k][DIV_PAT_VOL]=0x60+patDataBuf[k];
}
// instrument
@ -282,7 +276,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
logD("parsing instruments of pattern %d channel %d",i,j);
for (int k=0; k<256; k++) {
if (patDataBuf[k]==0) continue;
pat->data[k][2]=info.insNumMaps[patDataBuf[k]-1];
pat->newData[k][DIV_PAT_INS]=info.insNumMaps[patDataBuf[k]-1];
}
// effects
@ -300,76 +294,76 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
case 0:
// arpeggio or no effect (if effect val is 0)
if (effectVal[k]==0) break;
pat->data[k][4+(l*2)]=effectNum[k];
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FX(l)]=effectNum[k];
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
break;
case 1:
// pitch slide up
case 2:
// pitch slide down
pat->data[k][4+(l*2)]=effectNum[k];
pat->newData[k][DIV_PAT_FX(l)]=effectNum[k];
if (effectVal[k]) {
lastSlide=effectVal[k];
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
} else {
pat->data[k][5+(l*2)]=lastSlide;
pat->newData[k][DIV_PAT_FXVAL(l)]=lastSlide;
}
break;
case 3:
// portamento
case 4:
// vibrato
pat->data[k][5+(l*2)]=0;
pat->newData[k][DIV_PAT_FXVAL(l)]=0;
if (effectVal[k]&0xF0) {
pat->data[k][5+(l*2)]|=effectVal[k]&0xF0;
pat->newData[k][DIV_PAT_FXVAL(l)]|=effectVal[k]&0xF0;
} else {
pat->data[k][5+(l*2)]|=lastVibrato&0xF0;
pat->newData[k][DIV_PAT_FXVAL(l)]|=lastVibrato&0xF0;
}
if (effectVal[k]&0x0F) {
pat->data[k][5+(l*2)]|=effectVal[k]&0x0F;
pat->newData[k][DIV_PAT_FXVAL(l)]|=effectVal[k]&0x0F;
} else {
pat->data[k][5+(l*2)]|=lastVibrato&0x0F;
pat->newData[k][DIV_PAT_FXVAL(l)]|=lastVibrato&0x0F;
}
pat->data[k][4+(l*2)]=effectNum[k];
lastVibrato=pat->data[k][5+(l*2)];
pat->newData[k][DIV_PAT_FX(l)]=effectNum[k];
lastVibrato=pat->newData[k][DIV_PAT_FXVAL(l)];
break;
case 5:
// poramento + volume slide
pat->data[k][4+(l*2)]=0x06;
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FX(l)]=0x06;
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
break;
case 6:
// vibrato + volume slide
pat->data[k][4+(l*2)]=0x05;
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FX(l)]=0x05;
pat->newData[k][DIV_PAT_FXVAL(l)]=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];
pat->newData[k][DIV_PAT_FX(l)]=0x12;
pat->newData[k][DIV_PAT_FXVAL(l)]=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];
pat->newData[k][DIV_PAT_FX(l)]=0x13;
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
break;
case 10:
// volume slide
pat->data[k][4+(l*2)]=0xA;
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FX(l)]=0xA;
pat->newData[k][DIV_PAT_FXVAL(l)]=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];
pat->newData[k][DIV_PAT_FX(l)]=0x14;
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
break;
case 13:
// modify TL of operator 4
pat->data[k][4+(l*2)]=0x15;
pat->data[k][5+(l*2)]=effectVal[k];
pat->newData[k][DIV_PAT_FX(l)]=0x15;
pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k];
break;
case 14:
switch (effectVal[k]>>4) {
@ -378,18 +372,18 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
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);
pat->newData[k][DIV_PAT_FX(l)]=0x16;
pat->newData[k][DIV_PAT_FXVAL(l)]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF);
break;
case 8:
// pan
pat->data[k][4+(l*2)]=0x80;
pat->newData[k][DIV_PAT_FX(l)]=0x80;
if ((effectVal[k]&0xF)==1) {
pat->data[k][5+(l*2)]=0;
pat->newData[k][DIV_PAT_FXVAL(l)]=0;
} else if ((effectVal[k]&0xF)==2) {
pat->data[k][5+(l*2)]=0xFF;
pat->newData[k][DIV_PAT_FXVAL(l)]=0xFF;
} else {
pat->data[k][5+(l*2)]=0x80;
pat->newData[k][DIV_PAT_FXVAL(l)]=0x80;
}
break;
}
@ -407,9 +401,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+(l*2)]=0x0F;
pat->newData[k][DIV_PAT_FX(l)]=0x0F;
unsigned char speedSet=effectVal[k]>>4;
pat->data[k][5+(l*2)]=speedSet;
pat->newData[k][DIV_PAT_FXVAL(l)]=speedSet;
break;
} else {
speed.speedEven=effectVal[k]>>4;
@ -418,8 +412,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
auto speedIndex = speeds.find(speed);
if (speedIndex != speeds.end()) {
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedIndex->second;
pat->newData[k][DIV_PAT_FX(l)]=0x09;
pat->newData[k][DIV_PAT_FXVAL(l)]=speedIndex->second;
break;
}
if (speed.interleaveFactor>8) {
@ -437,8 +431,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
info.ds->grooves.push_back(groove);
speeds[speed]=speedGrooveIndex;
pat->data[k][4+(l*2)]=0x09;
pat->data[k][5+(l*2)]=speedGrooveIndex;
pat->newData[k][DIV_PAT_FX(l)]=0x09;
pat->newData[k][DIV_PAT_FXVAL(l)]=speedGrooveIndex;
speedGrooveIndex++;
break;
}
@ -447,8 +441,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
// put a "jump to next pattern" effect if the pattern is smaller than the maximum pattern length
if (info.patLens[i]!=0 && info.patLens[i]<info.ds->subsong[0]->patLen) {
pat->data[info.patLens[i]-1][4+(usedEffectsCol*4)]=0x0D;
pat->data[info.patLens[i]-1][5+(usedEffectsCol*4)]=0x00;
pat->newData[info.patLens[i]-1][DIV_PAT_FX(0)+(usedEffectsCol*4)]=0x0D;
pat->newData[info.patLens[i]-1][DIV_PAT_FXVAL(0)+(usedEffectsCol*4)]=0x00;
}
}
}
@ -473,28 +467,30 @@ 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;
// default instrument
if (i==0 && pat->data[0][2]==-1) pat->data[0][2]=0;
if (i==0 && pat->newData[0][DIV_PAT_INS]==-1) pat->newData[0][DIV_PAT_INS]=0;
for (int k=0; k<truePatLen; k++) {
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;
// TODO: -1 check? does it still work after refactor?
if (chArpeggio[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x00 && pat->newData[k][DIV_PAT_NOTE]!=-1) {
pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x00;
pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0;
chArpeggio[j]=false;
} 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;
} else if (chPorta[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x03 && pat->newData[k][DIV_PAT_FX(l)]!=0x01 && pat->newData[k][DIV_PAT_FX(l)]!=0x02) {
pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x03;
pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0;
chPorta[j]=false;
} 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;
} else if (chVibrato[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x04 && pat->newData[k][DIV_PAT_NOTE]!=-1) {
pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x04;
pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0;
chVibrato[j]=false;
} 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;
} else if (chVolumeSlide[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x0A) {
pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x0A;
pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0;
chVolumeSlide[j]=false;
}
switch (pat->data[k][4+l]) {
// TODO: looks like we have a bug here! it should be DIV_PAT_FX(l), right?
switch (pat->newData[k][DIV_PAT_FX(0)+l]) {
case 0:
chArpeggio[j]=true;
break;
@ -527,16 +523,16 @@ void TFMParsePattern(struct TFMParsePatternInfo info) {
lastPat->copyOn(newPat);
info.ds->subsong[0]->orders.ord[i][info.ds->subsong[0]->ordersLen - 1] = info.maxPat;
newPat->data[info.patLens[lastPatNum]-1][4+(usedEffectsCol*4)] = 0x0B;
newPat->data[info.patLens[lastPatNum]-1][5+(usedEffectsCol*4)] = info.loopPos;
newPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FX(usedEffectsCol*2)] = 0x0B;
newPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FXVAL(usedEffectsCol*2)] = info.loopPos;
info.ds->subsong[0]->pat[i].data[info.maxPat] = newPat;
}
} else {
for (int i=0;i<6;i++) {
int lastPatNum=info.ds->subsong[0]->orders.ord[i][info.ds->subsong[0]->ordersLen - 1];
DivPattern* lastPat=info.ds->subsong[0]->pat[i].getPattern(lastPatNum, false);
lastPat->data[info.patLens[lastPatNum]-1][4+(usedEffectsCol*4)] = 0x0B;
lastPat->data[info.patLens[lastPatNum]-1][5+(usedEffectsCol*4)] = info.loopPos;
lastPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FX(usedEffectsCol*2)] = 0x0B;
lastPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FXVAL(usedEffectsCol*2)] = info.loopPos;
}
}
}

View file

@ -786,7 +786,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
bool mustCommitInitial=true;
memset(effectCol,4,128);
memset(effectCol,0,128);
memset(vibStatus,0,128);
memset(vibStatusChanged,0,128*sizeof(bool));
memset(vibing,0,128*sizeof(bool));
@ -901,32 +901,26 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
if (note!=0) {
lastNote[k]=note;
if (note>96) {
p->data[j][0]=101;
p->data[j][1]=0;
p->newData[j][DIV_PAT_NOTE]=DIV_NOTE_REL;
} else {
note--;
p->data[j][0]=note%12;
p->data[j][1]=note/12;
if (p->data[j][0]==0) {
p->data[j][0]=12;
p->data[j][1]=(unsigned char)(p->data[j][1]-1);
}
p->newData[j][DIV_PAT_NOTE]=note+60;
}
}
}
if (hasIns) {
ins=reader.readC();
p->data[j][2]=((int)ins)-1;
p->newData[j][DIV_PAT_INS]=((int)ins)-1;
// default volume
if (lastNote[k]<96 && ins>0) {
p->data[j][3]=sampleVol[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(lastNote[k]&127)])];
p->newData[j][DIV_PAT_VOL]=sampleVol[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(lastNote[k]&127)])];
}
writePanning=true;
}
if (hasVol) {
vol=reader.readC();
if (vol>=0x10 && vol<=0x50) {
p->data[j][3]=vol-0x10;
p->newData[j][DIV_PAT_VOL]=vol-0x10;
} else { // effects in volume column
switch (vol>>4) {
case 0x6: // vol slide down
@ -986,11 +980,11 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
vibing[k]=true;
break;
case 0xc: // panning
p->data[j][effectCol[k]++]=0x80;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80;
if ((vol&15)==8) {
p->data[j][effectCol[k]++]=0x80;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80;
} else {
p->data[j][effectCol[k]++]=(vol&15)|((vol&15)<<4);
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(vol&15)|((vol&15)<<4);
}
writePanning=false;
break;
@ -1113,14 +1107,14 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
treming[k]=true;
break;
case 8: // panning
p->data[j][effectCol[k]++]=0x80;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
writePanning=false;
break;
case 9: // offset
if (hasNote) {
p->data[j][effectCol[k]++]=0x91;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x91;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
}
break;
case 0xa: // vol slide
@ -1134,15 +1128,15 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
volSliding[k]=true;
break;
case 0xb: // go to order
p->data[j][effectCol[k]++]=0x0b;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0b;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
break;
case 0xc: // set volume
p->data[j][3]=effectVal;
p->newData[j][DIV_PAT_VOL]=effectVal;
break;
case 0xd: // next order
p->data[j][effectCol[k]++]=0x0d;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0d;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
break;
case 0xe: // special...
// TODO: implement the rest
@ -1150,27 +1144,27 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
case 0x4: // vibrato waveform
switch (effectVal&3) {
case 0x0: // sine
p->data[j][effectCol[k]++]=0xe3;
p->data[j][effectCol[k]++]=0x00;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00;
break;
case 0x1: // ramp down
p->data[j][effectCol[k]++]=0xe3;
p->data[j][effectCol[k]++]=0x05;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x05;
break;
case 0x2: // square
case 0x3:
p->data[j][effectCol[k]++]=0xe3;
p->data[j][effectCol[k]++]=0x06;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x06;
break;
}
break;
case 0x5: // fine tune
p->data[j][effectCol[k]++]=0xe5;
p->data[j][effectCol[k]++]=(effectVal&15)<<4;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe5;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(effectVal&15)<<4;
break;
case 0x9: // retrigger
p->data[j][effectCol[k]++]=0x0c;
p->data[j][effectCol[k]++]=(effectVal&15);
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0c;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(effectVal&15);
break;
case 0xa: // vol slide up (fine)
volSlideStatus[k]=((effectVal&15)<<4)|0xf;
@ -1189,32 +1183,32 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
volSliding[k]=true;
break;
case 0xc: // note cut
p->data[j][effectCol[k]++]=0xdc;
p->data[j][effectCol[k]++]=MAX(1,effectVal&15);
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xdc;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=MAX(1,effectVal&15);
break;
case 0xd: // note delay
p->data[j][effectCol[k]++]=0xed;
p->data[j][effectCol[k]++]=MAX(1,effectVal&15);
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xed;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=MAX(1,effectVal&15);
break;
}
break;
case 0xf: // speed/tempo
if (effectVal>=0x20) {
p->data[j][effectCol[k]++]=0xf0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf0;
} else if (effectVal==0) {
p->data[j][effectCol[k]++]=0xff;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xff;
} else {
p->data[j][effectCol[k]++]=0x0f;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0f;
}
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
break;
case 0x10: // G: global volume (!)
break;
case 0x11: // H: global volume slide (!)
break;
case 0x14: // K: key off
p->data[j][effectCol[k]++]=0xe7;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe7;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
break;
case 0x15: // L: set envelope position (!)
break;
@ -1226,8 +1220,8 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
panSliding[k]=true;
break;
case 0x1b: // R: retrigger
p->data[j][effectCol[k]++]=0x0c;
p->data[j][effectCol[k]++]=effectVal;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0c;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal;
break;
case 0x1d: // T: tremor (!)
break;
@ -1244,8 +1238,8 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
}
if (writePanning && hasNote && note<96 && ins>0) {
p->data[j][effectCol[k]++]=0x80;
p->data[j][effectCol[k]++]=samplePan[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(note&127)])];
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=samplePan[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(note&127)])];
}
}
@ -1253,80 +1247,80 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
for (int k=0; k<totalChans; k++) {
DivPattern* p=ds.subsong[0]->pat[k].getPattern(i,true);
if (vibing[k]!=vibingOld[k] || vibStatusChanged[k]) {
p->data[j][effectCol[k]++]=0x04;
p->data[j][effectCol[k]++]=vibing[k]?vibStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x04;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=vibing[k]?vibStatus[k]:0;
doesVibrato[k]=true;
} else if (doesVibrato[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x04;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x04;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (volSliding[k]!=volSlidingOld[k] || volSlideStatusChanged[k]) {
if (volSlideStatus[k]>=0xf1 && volSliding[k]) {
p->data[j][effectCol[k]++]=0xf9;
p->data[j][effectCol[k]++]=volSlideStatus[k]&15;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf9;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSlideStatus[k]&15;
volSliding[k]=false;
} else if ((volSlideStatus[k]&15)==15 && volSlideStatus[k]>=0x10 && volSliding[k]) {
p->data[j][effectCol[k]++]=0xf8;
p->data[j][effectCol[k]++]=volSlideStatus[k]>>4;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf8;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSlideStatus[k]>>4;
volSliding[k]=false;
} else {
p->data[j][effectCol[k]++]=0xfa;
p->data[j][effectCol[k]++]=volSliding[k]?volSlideStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xfa;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSliding[k]?volSlideStatus[k]:0;
}
doesVolSlide[k]=true;
} else if (doesVolSlide[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0xfa;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xfa;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (porting[k]!=portingOld[k] || portaStatusChanged[k]) {
p->data[j][effectCol[k]++]=portaType[k];
p->data[j][effectCol[k]++]=porting[k]?portaStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=portaType[k];
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=porting[k]?portaStatus[k]:0;
doesPitchSlide[k]=true;
} else if (doesPitchSlide[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x01;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x01;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (arping[k]!=arpingOld[k] || arpStatusChanged[k]) {
p->data[j][effectCol[k]++]=0x00;
p->data[j][effectCol[k]++]=arping[k]?arpStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=arping[k]?arpStatus[k]:0;
doesArp[k]=true;
} else if (doesArp[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x00;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (treming[k]!=tremingOld[k] || tremStatusChanged[k]) {
p->data[j][effectCol[k]++]=0x07;
p->data[j][effectCol[k]++]=treming[k]?tremStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x07;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=treming[k]?tremStatus[k]:0;
doesTremolo[k]=true;
} else if (doesTremolo[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x07;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x07;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (panning[k]!=panningOld[k] || panStatusChanged[k]) {
p->data[j][effectCol[k]++]=0x84;
p->data[j][effectCol[k]++]=panning[k]?panStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x84;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=panning[k]?panStatus[k]:0;
doesPanbrello[k]=true;
} else if (doesPanbrello[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x84;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x84;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if (panSliding[k]!=panSlidingOld[k] || panSlideStatusChanged[k]) {
p->data[j][effectCol[k]++]=0x83;
p->data[j][effectCol[k]++]=panSliding[k]?panSlideStatus[k]:0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x83;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=panSliding[k]?panSlideStatus[k]:0;
doesPanSlide[k]=true;
} else if (doesPanSlide[k] && mustCommitInitial) {
p->data[j][effectCol[k]++]=0x83;
p->data[j][effectCol[k]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x83;
p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0;
}
if ((effectCol[k]>>1)-2>ds.subsong[0]->pat[k].effectCols) {
ds.subsong[0]->pat[k].effectCols=(effectCol[k]>>1)-1;
if ((effectCol[k]>>1)>=ds.subsong[0]->pat[k].effectCols) {
ds.subsong[0]->pat[k].effectCols=(effectCol[k]>>1)+1;
}
}
@ -1346,14 +1340,14 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
if (j==totalRows-1) {
// place end of pattern marker
DivPattern* p=ds.subsong[0]->pat[0].getPattern(i,true);
p->data[j][effectCol[0]++]=0x0d;
p->data[j][effectCol[0]++]=0;
p->newData[j][DIV_PAT_FX(0)+effectCol[0]++]=0x0d;
p->newData[j][DIV_PAT_FX(0)+effectCol[0]++]=0;
if ((effectCol[0]>>1)-2>ds.subsong[0]->pat[0].effectCols) {
ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)-1;
if ((effectCol[0]>>1)>=ds.subsong[0]->pat[0].effectCols) {
ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)+1;
}
}
memset(effectCol,4,64);
memset(effectCol,0,64);
}
logV("seeking to %x...",packedSeek);

View file

@ -45,7 +45,7 @@ std::vector<std::pair<int,int>> DivChannelData::optimize() {
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
if (j==i) continue;
if (data[j]==NULL) continue;
if (memcmp(data[i]->data,data[j]->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) {
if (memcmp(data[i]->newData,data[j]->newData,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) {
delete data[j];
data[j]=NULL;
logV("%d == %d",i,j);
@ -86,15 +86,11 @@ void DivChannelData::wipePatterns() {
void DivPattern::copyOn(DivPattern* dest) {
dest->name=name;
memcpy(dest->data,data,sizeof(data));
memcpy(dest->newData,newData,sizeof(newData));
}
void DivPattern::clear() {
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
for (int i=0; i<DIV_MAX_ROWS; i++) {
data[i][0]=0;
data[i][1]=0;
}
memset(newData,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
}
DivChannelData::DivChannelData():

View file

@ -22,7 +22,22 @@
struct DivPattern {
String name;
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
/**
* pattern data is stored in this order:
* - 0 note: 0 (min) is C-(-5), 60 is C-0, and 179 (max) is B-9.
252 is null/bug, 253 is note off, 254 is note release and
255 is macro release.
* - 1 instrument
* - 2 volume
* - 3 effect
* - 4 effect value
* - 5... (the rest of effects/effect values)
*
* use the DIV_PAT_* macros in defines.h for convenience.
*
* if a cell is -1, it means "empty".
*/
short newData[DIV_MAX_ROWS][DIV_MAX_COLS];
/**
* clear the pattern.
@ -39,14 +54,10 @@ struct DivPattern {
struct DivChannelData {
unsigned char effectCols;
// data goes as follows: data[ROW][TYPE]
// TYPE is:
// 0: note
// 1: octave
// 2: instrument
// 3: volume
// 4-5+: effect/effect value
// do NOT access directly unless you know what you're doing!
/**
* do NOT access directly unless you know what you're doing!
* use getPattern() instead.
*/
DivPattern* data[DIV_MAX_PATTERNS];
/**

File diff suppressed because it is too large Load diff

View file

@ -543,7 +543,11 @@ bool DivSample::saveRaw(const char* path) {
}
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
bool DivSample::initInternal(DivSampleDepth d, unsigned int count) {
bool DivSample::initInternal(DivSampleDepth d, int count) {
if (count<0) {
logE("initInternal(%d,%d) - NEGATIVE!",(int)d,count);
return false;
}
logV("initInternal(%d,%d)",(int)d,count);
switch (d) {
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
@ -650,8 +654,11 @@ bool DivSample::initInternal(DivSampleDepth d, unsigned int count) {
return true;
}
bool DivSample::init(unsigned int count) {
if (count>16777215) return false;
bool DivSample::init(int count) {
if (count<0 || count>16777215) {
logE("tried to init sample with length %d!",count);
return false;
}
if (!initInternal(depth,count)) return false;
setSampleCount(count);
return true;

View file

@ -236,14 +236,14 @@ struct DivSample {
* @param count number of samples.
* @return whether it was successful.
*/
bool initInternal(DivSampleDepth d, unsigned int count);
bool initInternal(DivSampleDepth d, int count);
/**
* initialize sample data. make sure you have set `depth` before doing so.
* @param count number of samples.
* @return whether it was successful.
*/
bool init(unsigned int count);
bool init(int count);
/**
* resize sample data. make sure the sample has been initialized before doing so.

View file

@ -54,9 +54,9 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int
}
for (int k=0; k<chans; k++) {
for (int l=0; l<pat[k].effectCols; l++) {
effectVal=subPat[k]->data[j][5+(l<<1)];
effectVal=subPat[k]->newData[j][DIV_PAT_FXVAL(l)];
if (effectVal<0) effectVal=0;
if (subPat[k]->data[j][4+(l<<1)]==0x0d) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0d) {
if (jumpTreatment==2) {
if ((i<ordersLen-1 || !ignoreJumpAtEnd)) {
nextOrder=i+1;
@ -78,7 +78,7 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int
nextRow=effectVal;
}
}
} else if (subPat[k]->data[j][4+(l<<1)]==0x0b) {
} else if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0b) {
if (nextOrder==-1 || jumpTreatment==0) {
nextOrder=effectVal;
if (jumpTreatment==1 || jumpTreatment==2 || !jumpingOrder) {
@ -160,10 +160,10 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
}
for (int k=0; k<chans; k++) {
for (int l=0; l<pat[k].effectCols; l++) {
effectVal=subPat[k]->data[j][5+(l<<1)];
effectVal=subPat[k]->newData[j][DIV_PAT_FXVAL(l)];
if (effectVal<0) effectVal=0;
if (subPat[k]->data[j][4+(l<<1)]==0xff) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0xff) {
hasFFxx=true;
// FFxx makes YOU SHALL NOT PASS!!! move
@ -173,7 +173,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
return;
}
switch (subPat[k]->data[j][4+(l<<1)]) {
switch (subPat[k]->newData[j][DIV_PAT_FX(l)]) {
case 0x09: { // select groove pattern/speed 1
if (grooves.empty()) {
if (effectVal>0) curSpeeds.val[0]=effectVal;
@ -208,7 +208,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
}
}
if (subPat[k]->data[j][4+(l<<1)]==0x0d) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0d) {
if (jumpTreatment==2) {
if ((i<ordersLen-1 || !ignoreJumpAtEnd)) {
nextOrder=i+1;
@ -230,7 +230,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
nextRow=effectVal;
}
}
} else if (subPat[k]->data[j][4+(l<<1)]==0x0b) {
} else if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0b) {
if (nextOrder==-1 || jumpTreatment==0) {
nextOrder=effectVal;
if (jumpTreatment==1 || jumpTreatment==2 || !jumpingOrder) {