Merge branch 'master' into feature/Moar-patch-bank-support-part3
This commit is contained in:
commit
0e07b745c7
28 changed files with 1206 additions and 611 deletions
|
|
@ -139,18 +139,18 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
|
|||
int nextRow=0;
|
||||
int effectVal=0;
|
||||
DivPattern* pat[DIV_MAX_CHANS];
|
||||
for (int i=0; i<song.ordersLen; i++) {
|
||||
for (int i=0; i<curSubSong->ordersLen; i++) {
|
||||
for (int j=0; j<chans; j++) {
|
||||
pat[j]=song.pat[j].getPattern(song.orders.ord[j][i],false);
|
||||
pat[j]=curPat[j].getPattern(curOrders->ord[j][i],false);
|
||||
}
|
||||
for (int j=nextRow; j<song.patLen; j++) {
|
||||
for (int j=nextRow; j<curSubSong->patLen; j++) {
|
||||
nextRow=0;
|
||||
for (int k=0; k<chans; k++) {
|
||||
for (int l=0; l<song.pat[k].effectCols; l++) {
|
||||
for (int l=0; l<curPat[k].effectCols; l++) {
|
||||
effectVal=pat[k]->data[j][5+(l<<1)];
|
||||
if (effectVal<0) effectVal=0;
|
||||
if (pat[k]->data[j][4+(l<<1)]==0x0d) {
|
||||
if (nextOrder==-1 && (i<song.ordersLen-1 || !song.ignoreJumpAtEnd)) {
|
||||
if (nextOrder==-1 && (i<curSubSong->ordersLen-1 || !song.ignoreJumpAtEnd)) {
|
||||
nextOrder=i+1;
|
||||
nextRow=effectVal;
|
||||
}
|
||||
|
|
@ -695,6 +695,7 @@ void DivEngine::createNew(const int* description) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=DivSong();
|
||||
changeSong(0);
|
||||
if (description!=NULL) {
|
||||
initSongWithDesc(description);
|
||||
}
|
||||
|
|
@ -716,45 +717,55 @@ void DivEngine::swapChannels(int src, int dest) {
|
|||
}
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
||||
song.orders.ord[src][i]^=song.orders.ord[dest][i];
|
||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
|
||||
DivPattern* prev=song.pat[src].data[i];
|
||||
song.pat[src].data[i]=song.pat[dest].data[i];
|
||||
song.pat[dest].data[i]=prev;
|
||||
DivPattern* prev=curPat[src].data[i];
|
||||
curPat[src].data[i]=curPat[dest].data[i];
|
||||
curPat[dest].data[i]=prev;
|
||||
}
|
||||
|
||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
||||
song.pat[dest].effectCols^=song.pat[src].effectCols;
|
||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
||||
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||
curPat[dest].effectCols^=curPat[src].effectCols;
|
||||
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||
|
||||
String prevChanName=song.chanName[src];
|
||||
String prevChanShortName=song.chanShortName[src];
|
||||
bool prevChanShow=song.chanShow[src];
|
||||
bool prevChanCollapse=song.chanCollapse[src];
|
||||
String prevChanName=curSubSong->chanName[src];
|
||||
String prevChanShortName=curSubSong->chanShortName[src];
|
||||
bool prevChanShow=curSubSong->chanShow[src];
|
||||
bool prevChanCollapse=curSubSong->chanCollapse[src];
|
||||
|
||||
song.chanName[src]=song.chanName[dest];
|
||||
song.chanShortName[src]=song.chanShortName[dest];
|
||||
song.chanShow[src]=song.chanShow[dest];
|
||||
song.chanCollapse[src]=song.chanCollapse[dest];
|
||||
song.chanName[dest]=prevChanName;
|
||||
song.chanShortName[dest]=prevChanShortName;
|
||||
song.chanShow[dest]=prevChanShow;
|
||||
song.chanCollapse[dest]=prevChanCollapse;
|
||||
curSubSong->chanName[src]=curSubSong->chanName[dest];
|
||||
curSubSong->chanShortName[src]=curSubSong->chanShortName[dest];
|
||||
curSubSong->chanShow[src]=curSubSong->chanShow[dest];
|
||||
curSubSong->chanCollapse[src]=curSubSong->chanCollapse[dest];
|
||||
curSubSong->chanName[dest]=prevChanName;
|
||||
curSubSong->chanShortName[dest]=prevChanShortName;
|
||||
curSubSong->chanShow[dest]=prevChanShow;
|
||||
curSubSong->chanCollapse[dest]=prevChanCollapse;
|
||||
}
|
||||
|
||||
void DivEngine::stompChannel(int ch) {
|
||||
logV("stomping channel %d",ch);
|
||||
for (int i=0; i<256; i++) {
|
||||
song.orders.ord[ch][i]=0;
|
||||
curOrders->ord[ch][i]=0;
|
||||
}
|
||||
song.pat[ch].wipePatterns();
|
||||
song.pat[ch].effectCols=1;
|
||||
song.chanName[ch]="";
|
||||
song.chanShortName[ch]="";
|
||||
song.chanShow[ch]=true;
|
||||
song.chanCollapse[ch]=false;
|
||||
curPat[ch].wipePatterns();
|
||||
curPat[ch].effectCols=1;
|
||||
curSubSong->chanName[ch]="";
|
||||
curSubSong->chanShortName[ch]="";
|
||||
curSubSong->chanShow[ch]=true;
|
||||
curSubSong->chanCollapse[ch]=false;
|
||||
}
|
||||
|
||||
void DivEngine::changeSong(size_t songIndex) {
|
||||
if (songIndex>=song.subsong.size()) return;
|
||||
curSubSong=song.subsong[songIndex];
|
||||
curPat=song.subsong[songIndex]->pat;
|
||||
curOrders=&song.subsong[songIndex]->orders;
|
||||
curSubSongIndex=songIndex;
|
||||
curOrder=0;
|
||||
curRow=0;
|
||||
}
|
||||
|
||||
void DivEngine::swapChannelsP(int src, int dest) {
|
||||
|
|
@ -767,6 +778,41 @@ void DivEngine::swapChannelsP(int src, int dest) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::changeSongP(size_t index) {
|
||||
if (index>=song.subsong.size()) return;
|
||||
if (index==curSubSongIndex) return;
|
||||
stop();
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
changeSong(index);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
int DivEngine::addSubSong() {
|
||||
if (song.subsong.size()>=127) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong.push_back(new DivSubSong);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.subsong.size()-1;
|
||||
}
|
||||
|
||||
bool DivEngine::removeSubSong(int index) {
|
||||
if (song.subsong.size()<=1) return false;
|
||||
stop();
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong[index]->clearData();
|
||||
delete song.subsong[index];
|
||||
song.subsong.erase(song.subsong.begin()+index);
|
||||
changeSong(0);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||
int chanCount=chans;
|
||||
quitDispatch();
|
||||
|
|
@ -1321,15 +1367,15 @@ void DivEngine::reset() {
|
|||
}
|
||||
extValue=0;
|
||||
extValuePresent=0;
|
||||
speed1=song.speed1;
|
||||
speed2=song.speed2;
|
||||
speed1=curSubSong->speed1;
|
||||
speed2=curSubSong->speed2;
|
||||
firstTick=false;
|
||||
nextSpeed=speed1;
|
||||
divider=60;
|
||||
if (song.customTempo) {
|
||||
divider=song.hz;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
} else {
|
||||
if (song.pal) {
|
||||
if (curSubSong->pal) {
|
||||
divider=60;
|
||||
} else {
|
||||
divider=50;
|
||||
|
|
@ -1476,6 +1522,10 @@ int DivEngine::getRow() {
|
|||
return curRow;
|
||||
}
|
||||
|
||||
size_t DivEngine::getCurrentSubSong() {
|
||||
return curSubSongIndex;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed1() {
|
||||
return speed1;
|
||||
}
|
||||
|
|
@ -1485,9 +1535,9 @@ unsigned char DivEngine::getSpeed2() {
|
|||
}
|
||||
|
||||
float DivEngine::getHz() {
|
||||
if (song.customTempo) {
|
||||
return song.hz;
|
||||
} else if (song.pal) {
|
||||
if (curSubSong->customTempo) {
|
||||
return curSubSong->hz;
|
||||
} else if (curSubSong->pal) {
|
||||
return 60.0;
|
||||
} else {
|
||||
return 50.0;
|
||||
|
|
@ -1594,6 +1644,7 @@ void DivEngine::unmuteAll() {
|
|||
}
|
||||
|
||||
int DivEngine::addInstrument(int refChan) {
|
||||
if (song.ins.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
int insCount=(int)song.ins.size();
|
||||
|
|
@ -1624,6 +1675,10 @@ int DivEngine::addInstrument(int refChan) {
|
|||
}
|
||||
|
||||
int DivEngine::addInstrumentPtr(DivInstrument* which) {
|
||||
if (song.ins.size()>=256) {
|
||||
delete which;
|
||||
return -1;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.ins.push_back(which);
|
||||
|
|
@ -1654,10 +1709,10 @@ void DivEngine::delInstrument(int index) {
|
|||
song.insLen=song.ins.size();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<256; j++) {
|
||||
if (song.pat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
if (song.pat[i].data[j]->data[k][2]>index) {
|
||||
song.pat[i].data[j]->data[k][2]--;
|
||||
if (curPat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<curSubSong->patLen; k++) {
|
||||
if (curPat[i].data[j]->data[k][2]>index) {
|
||||
curPat[i].data[j]->data[k][2]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1668,6 +1723,7 @@ void DivEngine::delInstrument(int index) {
|
|||
}
|
||||
|
||||
int DivEngine::addWave() {
|
||||
if (song.wave.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
DivWavetable* wave=new DivWavetable;
|
||||
|
|
@ -1680,37 +1736,48 @@ int DivEngine::addWave() {
|
|||
}
|
||||
|
||||
bool DivEngine::addWaveFromFile(const char* path) {
|
||||
if (song.wave.size()>=256) {
|
||||
lastError="too many wavetables!";
|
||||
return false;
|
||||
}
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
lastError=fmt::sprintf("%s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
unsigned char* buf;
|
||||
ssize_t len;
|
||||
if (fseek(f,0,SEEK_END)!=0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not seek to end: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
len=ftell(f);
|
||||
if (len<0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not determine file size: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
lastError="file size is invalid!";
|
||||
return false;
|
||||
}
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
lastError="file is empty";
|
||||
return false;
|
||||
}
|
||||
if (fseek(f,0,SEEK_SET)!=0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
buf=new unsigned char[len];
|
||||
if (fread(buf,1,len,f)!=(size_t)len) {
|
||||
logW("did not read entire wavetable file buffer!");
|
||||
delete[] buf;
|
||||
lastError=fmt::sprintf("could not read entire file: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
|
|
@ -1790,6 +1857,7 @@ bool DivEngine::addWaveFromFile(const char* path) {
|
|||
} catch (EndOfFileException& e) {
|
||||
delete wave;
|
||||
delete[] buf;
|
||||
lastError="premature end of file";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1816,6 +1884,7 @@ void DivEngine::delWave(int index) {
|
|||
}
|
||||
|
||||
int DivEngine::addSample() {
|
||||
if (song.sample.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
DivSample* sample=new DivSample;
|
||||
|
|
@ -1830,6 +1899,10 @@ int DivEngine::addSample() {
|
|||
}
|
||||
|
||||
int DivEngine::addSampleFromFile(const char* path) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return -1;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
|
|
@ -2025,18 +2098,18 @@ void DivEngine::delSample(int index) {
|
|||
|
||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (song.ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
order[i]=song.orders.ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
}
|
||||
} else {
|
||||
bool used[256];
|
||||
for (int i=0; i<chans; i++) {
|
||||
memset(used,0,sizeof(bool)*256);
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
used[song.orders.ord[i][j]]=true;
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
used[curOrders->ord[i][j]]=true;
|
||||
}
|
||||
order[i]=0xff;
|
||||
for (int j=0; j<256; j++) {
|
||||
|
|
@ -2050,19 +2123,19 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
if (where) { // at the end
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][song.ordersLen]=order[i];
|
||||
curOrders->ord[i][curSubSong->ordersLen]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
song.orders.ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (playing && !freelance) {
|
||||
|
|
@ -2074,21 +2147,21 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (song.ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
warnings="";
|
||||
BUSY_BEGIN_SOFT;
|
||||
for (int i=0; i<chans; i++) {
|
||||
bool didNotFind=true;
|
||||
logD("channel %d",i);
|
||||
order[i]=song.orders.ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
// find free slot
|
||||
for (int j=0; j<256; j++) {
|
||||
logD("finding free slot in %d...",j);
|
||||
if (song.pat[i].data[j]==NULL) {
|
||||
if (curPat[i].data[j]==NULL) {
|
||||
int origOrd=order[i];
|
||||
order[i]=j;
|
||||
DivPattern* oldPat=song.pat[i].getPattern(origOrd,false);
|
||||
DivPattern* pat=song.pat[i].getPattern(j,true);
|
||||
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||
DivPattern* pat=curPat[i].getPattern(j,true);
|
||||
memcpy(pat->data,oldPat->data,256*32*sizeof(short));
|
||||
logD("found at %d",j);
|
||||
didNotFind=false;
|
||||
|
|
@ -2102,19 +2175,19 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
if (where) { // at the end
|
||||
saveLock.lock();
|
||||
for (int i=0; i<chans; i++) {
|
||||
song.orders.ord[i][song.ordersLen]=order[i];
|
||||
curOrders->ord[i][curSubSong->ordersLen]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
song.orders.ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (playing && !freelance) {
|
||||
|
|
@ -2125,17 +2198,17 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
}
|
||||
|
||||
void DivEngine::deleteOrder() {
|
||||
if (song.ordersLen<=1) return;
|
||||
if (curSubSong->ordersLen<=1) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=curOrder; j<song.ordersLen; j++) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j+1];
|
||||
for (int j=curOrder; j<curSubSong->ordersLen; j++) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j+1];
|
||||
}
|
||||
}
|
||||
song.ordersLen--;
|
||||
curSubSong->ordersLen--;
|
||||
saveLock.unlock();
|
||||
if (curOrder>=song.ordersLen) curOrder=song.ordersLen-1;
|
||||
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
|
@ -2150,9 +2223,9 @@ void DivEngine::moveOrderUp() {
|
|||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
||||
song.orders.ord[i][curOrder-1]^=song.orders.ord[i][curOrder];
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder--;
|
||||
|
|
@ -2164,15 +2237,15 @@ void DivEngine::moveOrderUp() {
|
|||
|
||||
void DivEngine::moveOrderDown() {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder>=song.ordersLen-1) {
|
||||
if (curOrder>=curSubSong->ordersLen-1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
||||
song.orders.ord[i][curOrder+1]^=song.orders.ord[i][curOrder];
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
|
|
@ -2185,12 +2258,12 @@ void DivEngine::moveOrderDown() {
|
|||
void DivEngine::exchangeIns(int one, int two) {
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<256; j++) {
|
||||
if (song.pat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
if (song.pat[i].data[j]->data[k][2]==one) {
|
||||
song.pat[i].data[j]->data[k][2]=two;
|
||||
} else if (song.pat[i].data[j]->data[k][2]==two) {
|
||||
song.pat[i].data[j]->data[k][2]=one;
|
||||
if (curPat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<curSubSong->patLen; k++) {
|
||||
if (curPat[i].data[j]->data[k][2]==one) {
|
||||
curPat[i].data[j]->data[k][2]=two;
|
||||
} else if (curPat[i].data[j]->data[k][2]==two) {
|
||||
curPat[i].data[j]->data[k][2]=one;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2394,7 +2467,7 @@ void DivEngine::autoNoteOffAll() {
|
|||
void DivEngine::setOrder(unsigned char order) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
curOrder=order;
|
||||
if (order>=song.ordersLen) curOrder=0;
|
||||
if (order>=curSubSong->ordersLen) curOrder=0;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
|
@ -2417,15 +2490,15 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
|
|||
void DivEngine::setSongRate(float hz, bool pal) {
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.pal=!pal;
|
||||
song.hz=hz;
|
||||
curSubSong->pal=!pal;
|
||||
curSubSong->hz=hz;
|
||||
// what?
|
||||
song.customTempo=true;
|
||||
curSubSong->customTempo=true;
|
||||
divider=60;
|
||||
if (song.customTempo) {
|
||||
divider=song.hz;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
} else {
|
||||
if (song.pal) {
|
||||
if (curSubSong->pal) {
|
||||
divider=60;
|
||||
} else {
|
||||
divider=50;
|
||||
|
|
@ -2868,5 +2941,6 @@ bool DivEngine::quit() {
|
|||
if (yrw801ROM!=NULL) delete[] yrw801ROM;
|
||||
if (tg100ROM!=NULL) delete[] tg100ROM;
|
||||
if (mu5ROM!=NULL) delete[] mu5ROM;
|
||||
song.unload();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev94"
|
||||
#define DIV_ENGINE_VERSION 94
|
||||
#define DIV_VERSION "dev95"
|
||||
#define DIV_ENGINE_VERSION 95
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
|
@ -304,6 +304,7 @@ class DivEngine {
|
|||
bool hasLoadedSomething;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
|
||||
size_t curSubSongIndex;
|
||||
double divider;
|
||||
int cycles;
|
||||
double clockDrift;
|
||||
|
|
@ -414,8 +415,14 @@ class DivEngine {
|
|||
void swapChannels(int src, int dest);
|
||||
void stompChannel(int ch);
|
||||
|
||||
// change song (UNSAFE)
|
||||
void changeSong(size_t songIndex);
|
||||
|
||||
public:
|
||||
DivSong song;
|
||||
DivOrders* curOrders;
|
||||
DivChannelData* curPat;
|
||||
DivSubSong* curSubSong;
|
||||
DivInstrument* tempIns;
|
||||
DivSystem sysOfChan[DIV_MAX_CHANS];
|
||||
int dispatchOfChan[DIV_MAX_CHANS];
|
||||
|
|
@ -606,6 +613,9 @@ class DivEngine {
|
|||
// get current row
|
||||
int getRow();
|
||||
|
||||
// get current subsong
|
||||
size_t getCurrentSubSong();
|
||||
|
||||
// get speed 1
|
||||
unsigned char getSpeed1();
|
||||
|
||||
|
|
@ -802,6 +812,15 @@ class DivEngine {
|
|||
// public swap channels
|
||||
void swapChannelsP(int src, int dest);
|
||||
|
||||
// public change song
|
||||
void changeSongP(size_t index);
|
||||
|
||||
// add subsong
|
||||
int addSubSong();
|
||||
|
||||
// remove subsong
|
||||
bool removeSubSong(int index);
|
||||
|
||||
// change system
|
||||
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
||||
|
||||
|
|
@ -903,6 +922,7 @@ class DivEngine {
|
|||
curOrder(0),
|
||||
remainingLoops(-1),
|
||||
nextSpeed(3),
|
||||
curSubSongIndex(0),
|
||||
divider(60),
|
||||
cycles(0),
|
||||
clockDrift(0),
|
||||
|
|
@ -938,6 +958,8 @@ class DivEngine {
|
|||
metroAmp(0.0f),
|
||||
metroVol(1.0f),
|
||||
totalProcessed(0),
|
||||
curOrders(NULL),
|
||||
curPat(NULL),
|
||||
tempIns(NULL),
|
||||
oscBuf{NULL,NULL},
|
||||
oscSize(1),
|
||||
|
|
@ -962,6 +984,8 @@ class DivEngine {
|
|||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||
}
|
||||
|
||||
changeSong(0);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -184,57 +184,57 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
logI("reading module data...");
|
||||
if (ds.version>0x0c) {
|
||||
ds.hilightA=reader.readC();
|
||||
ds.hilightB=reader.readC();
|
||||
ds.subsong[0]->hilightA=reader.readC();
|
||||
ds.subsong[0]->hilightB=reader.readC();
|
||||
}
|
||||
|
||||
ds.timeBase=reader.readC();
|
||||
ds.speed1=reader.readC();
|
||||
if (ds.version>0x05) {
|
||||
ds.speed2=reader.readC();
|
||||
ds.pal=reader.readC();
|
||||
ds.hz=(ds.pal)?60:50;
|
||||
ds.customTempo=reader.readC();
|
||||
ds.subsong[0]->timeBase=reader.readC();
|
||||
ds.subsong[0]->speed1=reader.readC();
|
||||
if (ds.version>0x07) {
|
||||
ds.subsong[0]->speed2=reader.readC();
|
||||
ds.subsong[0]->pal=reader.readC();
|
||||
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||
ds.subsong[0]->customTempo=reader.readC();
|
||||
} else {
|
||||
ds.speed2=ds.speed1;
|
||||
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
||||
}
|
||||
if (ds.version>0x0a) {
|
||||
String hz=reader.readString(3);
|
||||
if (ds.customTempo) {
|
||||
if (ds.subsong[0]->customTempo) {
|
||||
try {
|
||||
ds.hz=std::stoi(hz);
|
||||
ds.subsong[0]->hz=std::stoi(hz);
|
||||
} catch (std::exception& e) {
|
||||
logW("invalid custom Hz!");
|
||||
ds.hz=60;
|
||||
ds.subsong[0]->hz=60;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ds.version>0x17) {
|
||||
ds.patLen=reader.readI();
|
||||
ds.subsong[0]->patLen=reader.readI();
|
||||
} else {
|
||||
ds.patLen=(unsigned char)reader.readC();
|
||||
ds.subsong[0]->patLen=(unsigned char)reader.readC();
|
||||
}
|
||||
ds.ordersLen=(unsigned char)reader.readC();
|
||||
ds.subsong[0]->ordersLen=(unsigned char)reader.readC();
|
||||
|
||||
if (ds.patLen<0) {
|
||||
if (ds.subsong[0]->patLen<0) {
|
||||
logE("pattern length is negative!");
|
||||
lastError="pattern lengrh is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.patLen>256) {
|
||||
if (ds.subsong[0]->patLen>256) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen<0) {
|
||||
if (ds.subsong[0]->ordersLen<0) {
|
||||
logE("song length is negative!");
|
||||
lastError="song length is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen>127) {
|
||||
if (ds.subsong[0]->ordersLen>127) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
|
@ -242,54 +242,54 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
if (ds.version<20 && ds.version>3) {
|
||||
ds.arpLen=reader.readC();
|
||||
ds.subsong[0]->arpLen=reader.readC();
|
||||
} else {
|
||||
ds.arpLen=1;
|
||||
ds.subsong[0]->arpLen=1;
|
||||
}
|
||||
|
||||
if (ds.system[0]==DIV_SYSTEM_YMU759) {
|
||||
switch (ds.timeBase) {
|
||||
switch (ds.subsong[0]->timeBase) {
|
||||
case 0:
|
||||
ds.hz=248;
|
||||
ds.subsong[0]->hz=248;
|
||||
break;
|
||||
case 1:
|
||||
ds.hz=200;
|
||||
ds.subsong[0]->hz=200;
|
||||
break;
|
||||
case 2:
|
||||
ds.hz=100;
|
||||
ds.subsong[0]->hz=100;
|
||||
break;
|
||||
case 3:
|
||||
ds.hz=50;
|
||||
ds.subsong[0]->hz=50;
|
||||
break;
|
||||
case 4:
|
||||
ds.hz=25;
|
||||
ds.subsong[0]->hz=25;
|
||||
break;
|
||||
case 5:
|
||||
ds.hz=20;
|
||||
ds.subsong[0]->hz=20;
|
||||
break;
|
||||
default:
|
||||
ds.hz=248;
|
||||
ds.subsong[0]->hz=248;
|
||||
break;
|
||||
}
|
||||
ds.customTempo=true;
|
||||
ds.timeBase=0;
|
||||
ds.subsong[0]->customTempo=true;
|
||||
ds.subsong[0]->timeBase=0;
|
||||
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
|
||||
}
|
||||
|
||||
logV("%x",reader.tell());
|
||||
|
||||
logI("reading pattern matrix (%d * %d = %d)...",ds.ordersLen,getChannelCount(ds.system[0]),ds.ordersLen*getChannelCount(ds.system[0]));
|
||||
logI("reading pattern matrix (%d * %d = %d)...",ds.subsong[0]->ordersLen,getChannelCount(ds.system[0]),ds.subsong[0]->ordersLen*getChannelCount(ds.system[0]));
|
||||
for (int i=0; i<getChannelCount(ds.system[0]); i++) {
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
ds.orders.ord[i][j]=reader.readC();
|
||||
if (ds.orders.ord[i][j]>0x7f) {
|
||||
logE("order at %d, %d out of range! (%d)",i,j,ds.orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order at %d, %d out of range! (%d)",i,j,ds.orders.ord[i][j]);
|
||||
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||
ds.subsong[0]->orders.ord[i][j]=reader.readC();
|
||||
if (ds.subsong[0]->orders.ord[i][j]>0x7f) {
|
||||
logE("order at %d, %d out of range! (%d)",i,j,ds.subsong[0]->orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order at %d, %d out of range! (%d)",i,j,ds.subsong[0]->orders.ord[i][j]);
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.version>0x18) { // 1.1 pattern names
|
||||
ds.pat[i].getPattern(j,true)->name=reader.readString((unsigned char)reader.readC());
|
||||
ds.subsong[0]->pat[i].getPattern(j,true)->name=reader.readString((unsigned char)reader.readC());
|
||||
}
|
||||
}
|
||||
if (ds.version>0x03 && ds.version<0x06 && i<16) {
|
||||
|
|
@ -637,9 +637,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
logV("%x",reader.tell());
|
||||
|
||||
logI("reading patterns (%d channels, %d orders)...",getChannelCount(ds.system[0]),ds.ordersLen);
|
||||
logI("reading patterns (%d channels, %d orders)...",getChannelCount(ds.system[0]),ds.subsong[0]->ordersLen);
|
||||
for (int i=0; i<getChannelCount(ds.system[0]); i++) {
|
||||
DivChannelData& chan=ds.pat[i];
|
||||
DivChannelData& chan=ds.subsong[0]->pat[i];
|
||||
if (ds.version<0x0a) {
|
||||
chan.effectCols=1;
|
||||
} else {
|
||||
|
|
@ -652,10 +652,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
DivPattern* pat=chan.getPattern(ds.orders.ord[i][j],true);
|
||||
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||
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.patLen; k++) {
|
||||
for (int k=0; k<ds.subsong[0]->patLen; k++) {
|
||||
// note
|
||||
pat->data[k][0]=reader.readS();
|
||||
// octave
|
||||
|
|
@ -723,7 +723,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
} else { // historic pattern format
|
||||
if (i<16) pat->data[0][2]=historicColIns[i];
|
||||
for (int k=0; k<ds.patLen; k++) {
|
||||
for (int k=0; k<ds.subsong[0]->patLen; k++) {
|
||||
// note
|
||||
pat->data[k][0]=reader.readC();
|
||||
// octave
|
||||
|
|
@ -788,7 +788,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
pitch=reader.readC();
|
||||
vol=reader.readC();
|
||||
}
|
||||
if (ds.version<=0x05) {
|
||||
if (ds.version<=0x08) {
|
||||
sample->rate=ymuSampleRate*400;
|
||||
}
|
||||
if (ds.version>0x15) {
|
||||
|
|
@ -798,15 +798,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
sample->depth=16;
|
||||
}
|
||||
} else {
|
||||
if (ds.version>0x05) {
|
||||
if (ds.version>0x08) {
|
||||
sample->depth=16;
|
||||
} else {
|
||||
// it appears samples were stored as ADPCM back then
|
||||
sample->depth=6;
|
||||
sample->depth=3;
|
||||
}
|
||||
}
|
||||
if (length>0) {
|
||||
if (ds.version>0x05) {
|
||||
if (ds.version>0x08) {
|
||||
if (ds.version<0x0b) {
|
||||
data=new short[1+(length/2)];
|
||||
reader.read(data,length);
|
||||
|
|
@ -842,8 +842,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
delete[] data;
|
||||
} else {
|
||||
// ADPCM?
|
||||
// it appears to be a slightly modified version of ADPCM-B!
|
||||
// YMZ ADPCM
|
||||
adpcmData=new unsigned char[length];
|
||||
logV("%x",reader.tell());
|
||||
reader.read(adpcmData,length);
|
||||
|
|
@ -854,7 +853,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
logE("%d: error while initializing sample!",i);
|
||||
}
|
||||
|
||||
memcpy(sample->dataB,adpcmData,length);
|
||||
memcpy(sample->dataZ,adpcmData,length);
|
||||
delete[] adpcmData;
|
||||
}
|
||||
}
|
||||
|
|
@ -906,6 +905,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -930,13 +930,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
unsigned int insPtr[256];
|
||||
unsigned int wavePtr[256];
|
||||
unsigned int samplePtr[256];
|
||||
unsigned int subSongPtr[256];
|
||||
std::vector<int> patPtr;
|
||||
int numberOfSubSongs=0;
|
||||
char magic[5];
|
||||
memset(magic,0,5);
|
||||
SafeReader reader=SafeReader(file,len);
|
||||
warnings="";
|
||||
try {
|
||||
DivSong ds;
|
||||
DivSubSong* subSong=ds.subsong[0];
|
||||
|
||||
if (!reader.seek(16,SEEK_SET)) {
|
||||
logE("premature end of file!");
|
||||
|
|
@ -1046,44 +1049,44 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
reader.readI();
|
||||
|
||||
ds.timeBase=reader.readC();
|
||||
ds.speed1=reader.readC();
|
||||
ds.speed2=reader.readC();
|
||||
ds.arpLen=reader.readC();
|
||||
ds.hz=reader.readF();
|
||||
ds.pal=(ds.hz>=53);
|
||||
ds.customTempo=true;
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
subSong->customTempo=true;
|
||||
|
||||
ds.patLen=reader.readS();
|
||||
ds.ordersLen=reader.readS();
|
||||
subSong->patLen=reader.readS();
|
||||
subSong->ordersLen=reader.readS();
|
||||
|
||||
ds.hilightA=reader.readC();
|
||||
ds.hilightB=reader.readC();
|
||||
subSong->hilightA=reader.readC();
|
||||
subSong->hilightB=reader.readC();
|
||||
|
||||
ds.insLen=reader.readS();
|
||||
ds.waveLen=reader.readS();
|
||||
ds.sampleLen=reader.readS();
|
||||
int numberOfPats=reader.readI();
|
||||
|
||||
if (ds.patLen<0) {
|
||||
if (subSong->patLen<0) {
|
||||
logE("pattern length is negative!");
|
||||
lastError="pattern lengrh is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.patLen>256) {
|
||||
if (subSong->patLen>256) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen<0) {
|
||||
if (subSong->ordersLen<0) {
|
||||
logE("song length is negative!");
|
||||
lastError="song length is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen>256) {
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
|
@ -1296,18 +1299,18 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.read(samplePtr,ds.sampleLen*4);
|
||||
for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
|
||||
|
||||
logD("reading orders (%d)...",ds.ordersLen);
|
||||
logD("reading orders (%d)...",subSong->ordersLen);
|
||||
for (int i=0; i<tchans; i++) {
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
ds.orders.ord[i][j]=reader.readC();
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
subSong->orders.ord[i][j]=reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.pat[i].effectCols=reader.readC();
|
||||
if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) {
|
||||
logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectCols);
|
||||
subSong->pat[i].effectCols=reader.readC();
|
||||
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>8) {
|
||||
logE("channel %d has zero or too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1315,25 +1318,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=39) {
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanShow[i]=reader.readC();
|
||||
subSong->chanShow[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanCollapse[i]=reader.readC();
|
||||
subSong->chanCollapse[i]=reader.readC();
|
||||
}
|
||||
|
||||
if (ds.version<92) {
|
||||
for (int i=0; i<tchans; i++) {
|
||||
if (ds.chanCollapse[i]>0) ds.chanCollapse[i]=3;
|
||||
if (subSong->chanCollapse[i]>0) subSong->chanCollapse[i]=3;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanName[i]=reader.readString();
|
||||
subSong->chanName[i]=reader.readString();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanShortName[i]=reader.readString();
|
||||
subSong->chanShortName[i]=reader.readString();
|
||||
}
|
||||
|
||||
ds.notes=reader.readString();
|
||||
|
|
@ -1406,6 +1409,88 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// subsongs
|
||||
if (ds.version>=95) {
|
||||
subSong->name=reader.readString();
|
||||
subSong->notes=reader.readString();
|
||||
numberOfSubSongs=(unsigned char)reader.readC();
|
||||
reader.readC(); // reserved
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
// pointers
|
||||
for (int i=0; i<numberOfSubSongs; i++) {
|
||||
subSongPtr[i]=reader.readI();
|
||||
}
|
||||
|
||||
for (int i=0; i<numberOfSubSongs; i++) {
|
||||
ds.subsong.push_back(new DivSubSong);
|
||||
if (!reader.seek(subSongPtr[i],SEEK_SET)) {
|
||||
logE("couldn't seek to subsong %d!",i+1);
|
||||
lastError=fmt::sprintf("couldn't seek to subsong %d!",i+1);
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
reader.read(magic,4);
|
||||
if (strcmp(magic,"SONG")!=0) {
|
||||
logE("%d: invalid subsong header!",i);
|
||||
lastError="invalid subsong header!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
reader.readI();
|
||||
|
||||
subSong=ds.subsong[i+1];
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
subSong->customTempo=true;
|
||||
|
||||
subSong->patLen=reader.readS();
|
||||
subSong->ordersLen=reader.readS();
|
||||
|
||||
subSong->hilightA=reader.readC();
|
||||
subSong->hilightB=reader.readC();
|
||||
|
||||
reader.readI(); // reserved
|
||||
|
||||
subSong->name=reader.readString();
|
||||
subSong->notes=reader.readString();
|
||||
|
||||
logD("reading orders of subsong %d (%d)...",i+1,subSong->ordersLen);
|
||||
for (int j=0; j<tchans; j++) {
|
||||
for (int k=0; k<subSong->ordersLen; k++) {
|
||||
subSong->orders.ord[j][k]=reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->pat[i].effectCols=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanShow[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanCollapse[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanName[i]=reader.readString();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanShortName[i]=reader.readString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read instruments
|
||||
for (int i=0; i<ds.insLen; i++) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
|
|
@ -1569,9 +1654,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
int chan=reader.readS();
|
||||
int index=reader.readS();
|
||||
reader.readI();
|
||||
int subs=0;
|
||||
if (ds.version>=95) {
|
||||
subs=reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
reader.readS();
|
||||
|
||||
logD("- %d, %d",chan,index);
|
||||
logD("- %d, %d, %d",subs,chan,index);
|
||||
|
||||
if (chan<0 || chan>=tchans) {
|
||||
logE("pattern channel out of range!",i);
|
||||
|
|
@ -1587,14 +1678,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||
logE("pattern subsong out of range!",i);
|
||||
lastError="pattern subsong out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
DivPattern* pat=ds.pat[chan].getPattern(index,true);
|
||||
for (int j=0; j<ds.patLen; j++) {
|
||||
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.pat[chan].effectCols; k++) {
|
||||
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();
|
||||
}
|
||||
|
|
@ -1616,6 +1714,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -1741,8 +1840,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
ds.sampleLen=ds.sample.size();
|
||||
|
||||
// orders
|
||||
ds.ordersLen=ordCount=reader.readC();
|
||||
if (ds.ordersLen<1 || ds.ordersLen>127) {
|
||||
ds.subsong[0]->ordersLen=ordCount=reader.readC();
|
||||
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) {
|
||||
logD("invalid order count!");
|
||||
throw EndOfFileException(&reader,reader.tell());
|
||||
}
|
||||
|
|
@ -1762,7 +1861,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
unsigned char pat=reader.readC();
|
||||
if (pat>patMax) patMax=pat;
|
||||
for (int j=0; j<chCount; j++) {
|
||||
ds.orders.ord[j][i]=pat;
|
||||
ds.subsong[0]->orders.ord[j][i]=pat;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1779,7 +1878,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// patterns
|
||||
ds.patLen=64;
|
||||
ds.subsong[0]->patLen=64;
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
for (int i=0; i<5; i++) {
|
||||
fxUsage[ch][i]=false;
|
||||
|
|
@ -1788,7 +1887,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
for (int pat=0; pat<=patMax; pat++) {
|
||||
DivPattern* chpats[DIV_MAX_CHANS];
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
chpats[ch]=ds.pat[ch].getPattern(pat,true);
|
||||
chpats[ch]=ds.subsong[0]->pat[ch].getPattern(pat,true);
|
||||
}
|
||||
for (int row=0; row<64; row++) {
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
|
|
@ -1861,7 +1960,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.pat[ch].getPattern(pat,true)->data;
|
||||
auto* data=ds.subsong[0]->pat[ch].getPattern(pat,true)->data;
|
||||
short lastPitchEffect=-1;
|
||||
short lastEffectState[5]={-1,-1,-1,-1,-1};
|
||||
short setEffectState[5]={-1,-1,-1,-1,-1};
|
||||
|
|
@ -1989,25 +2088,25 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
}
|
||||
ds.pat[ch].effectCols=fxCols;
|
||||
ds.subsong[0]->pat[ch].effectCols=fxCols;
|
||||
}
|
||||
|
||||
ds.pal=false;
|
||||
ds.hz=50;
|
||||
ds.customTempo=false;
|
||||
ds.subsong[0]->pal=false;
|
||||
ds.subsong[0]->hz=50;
|
||||
ds.subsong[0]->customTempo=false;
|
||||
ds.systemLen=(chCount+3)/4;
|
||||
for(int i=0; i<ds.systemLen; i++) {
|
||||
ds.system[i]=DIV_SYSTEM_AMIGA;
|
||||
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
||||
}
|
||||
for(int i=0; i<chCount; i++) {
|
||||
ds.chanShow[i]=true;
|
||||
ds.chanName[i]=fmt::sprintf("Channel %d",i+1);
|
||||
ds.chanShortName[i]=fmt::sprintf("C%d",i+1);
|
||||
ds.subsong[0]->chanShow[i]=true;
|
||||
ds.subsong[0]->chanName[i]=fmt::sprintf("Channel %d",i+1);
|
||||
ds.subsong[0]->chanShortName[i]=fmt::sprintf("C%d",i+1);
|
||||
}
|
||||
for(int i=chCount; i<ds.systemLen*4; i++) {
|
||||
ds.pat[i].effectCols=1;
|
||||
ds.chanShow[i]=false;
|
||||
ds.subsong[0]->pat[i].effectCols=1;
|
||||
ds.subsong[0]->chanShow[i]=false;
|
||||
}
|
||||
|
||||
// instrument creation
|
||||
|
|
@ -2025,6 +2124,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -2119,8 +2219,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
newVibrato=reader.readI();
|
||||
}
|
||||
if (blockVersion>=4) {
|
||||
ds.hilightA=reader.readI();
|
||||
ds.hilightB=reader.readI();
|
||||
ds.subsong[0]->hilightA=reader.readI();
|
||||
ds.subsong[0]->hilightB=reader.readI();
|
||||
}
|
||||
if (expansions&8) if (blockVersion>=5) { // N163 channels
|
||||
n163Chans=reader.readI();
|
||||
|
|
@ -2136,12 +2236,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
logV("custom Hz: %d",customHz);
|
||||
logV("new vibrato: %d",newVibrato);
|
||||
logV("N163 channels: %d",n163Chans);
|
||||
logV("highlight 1: %d",ds.hilightA);
|
||||
logV("highlight 2: %d",ds.hilightB);
|
||||
logV("highlight 1: %d",ds.subsong[0]->hilightA);
|
||||
logV("highlight 2: %d",ds.subsong[0]->hilightB);
|
||||
logV("split point: %d",speedSplitPoint);
|
||||
|
||||
if (customHz!=0) {
|
||||
ds.hz=customHz;
|
||||
ds.subsong[0]->hz=customHz;
|
||||
}
|
||||
|
||||
// initialize channels
|
||||
|
|
@ -2202,7 +2302,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
for (int j=0; j<=totalSongs; j++) {
|
||||
unsigned char effectCols=reader.readC();
|
||||
if (j==0) {
|
||||
ds.pat[i].effectCols=effectCols+1;
|
||||
ds.subsong[0]->pat[i].effectCols=effectCols+1;
|
||||
}
|
||||
logV("- song %d has %d effect columns",j,effectCols);
|
||||
}
|
||||
|
|
@ -2533,15 +2633,55 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
|||
return false;
|
||||
}
|
||||
|
||||
struct PatToWrite {
|
||||
unsigned short subsong, chan, pat;
|
||||
PatToWrite(unsigned short s, unsigned short c, unsigned short p):
|
||||
subsong(s),
|
||||
chan(c),
|
||||
pat(p) {}
|
||||
};
|
||||
|
||||
SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||
saveLock.lock();
|
||||
int insPtr[256];
|
||||
int wavePtr[256];
|
||||
int samplePtr[256];
|
||||
std::vector<int> subSongPtr;
|
||||
std::vector<int> insPtr;
|
||||
std::vector<int> wavePtr;
|
||||
std::vector<int> samplePtr;
|
||||
std::vector<int> patPtr;
|
||||
size_t ptrSeek;
|
||||
size_t ptrSeek, subSongPtrSeek;
|
||||
size_t subSongIndex=0;
|
||||
DivSubSong* subSong=song.subsong[subSongIndex];
|
||||
warnings="";
|
||||
|
||||
// fail if values are out of range
|
||||
/*
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("maximum song length is 256!");
|
||||
lastError="maximum song length is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
logE("maximum pattern length is 256!");
|
||||
lastError="maximum pattern length is 256";
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
if (song.ins.size()>256) {
|
||||
logE("maximum number of instruments is 256!");
|
||||
lastError="maximum number of instruments is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (song.wave.size()>256) {
|
||||
logE("maximum number of wavetables is 256!");
|
||||
lastError="maximum number of wavetables is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (song.sample.size()>256) {
|
||||
logE("maximum number of samples is 256!");
|
||||
lastError="maximum number of samples is 256";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!notPrimary) {
|
||||
song.isDMF=false;
|
||||
song.version=DIV_ENGINE_VERSION;
|
||||
|
|
@ -2568,14 +2708,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
// high short is channel
|
||||
// low short is pattern number
|
||||
std::vector<int> patsToWrite;
|
||||
std::vector<PatToWrite> patsToWrite;
|
||||
bool alreadyAdded[256];
|
||||
for (int i=0; i<chans; i++) {
|
||||
memset(alreadyAdded,0,256*sizeof(bool));
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
if (alreadyAdded[song.orders.ord[i][j]]) continue;
|
||||
patsToWrite.push_back((i<<16)|song.orders.ord[i][j]);
|
||||
alreadyAdded[song.orders.ord[i][j]]=true;
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
DivSubSong* subs=song.subsong[j];
|
||||
memset(alreadyAdded,0,256*sizeof(bool));
|
||||
for (int k=0; k<subs->ordersLen; k++) {
|
||||
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
||||
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
|
||||
alreadyAdded[subs->orders.ord[i][k]]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2583,15 +2726,15 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->write("INFO",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(song.timeBase);
|
||||
w->writeC(song.speed1);
|
||||
w->writeC(song.speed2);
|
||||
w->writeC(song.arpLen);
|
||||
w->writeF(song.hz);
|
||||
w->writeS(song.patLen);
|
||||
w->writeS(song.ordersLen);
|
||||
w->writeC(song.hilightA);
|
||||
w->writeC(song.hilightB);
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
w->writeS(subSong->ordersLen);
|
||||
w->writeC(subSong->hilightA);
|
||||
w->writeC(subSong->hilightB);
|
||||
w->writeS(song.insLen);
|
||||
w->writeS(song.waveLen);
|
||||
w->writeS(song.sampleLen);
|
||||
|
|
@ -2668,29 +2811,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
w->writeC(song.orders.ord[i][j]);
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
w->writeC(subSong->orders.ord[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.pat[i].effectCols);
|
||||
w->writeC(subSong->pat[i].effectCols);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.chanShow[i]);
|
||||
w->writeC(subSong->chanShow[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.chanCollapse[i]);
|
||||
w->writeC(subSong->chanCollapse[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(song.chanName[i],false);
|
||||
w->writeString(subSong->chanName[i],false);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(song.chanShortName[i],false);
|
||||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
|
||||
w->writeString(song.notes,false);
|
||||
|
|
@ -2715,25 +2858,86 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<18; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
// subsong list
|
||||
w->writeString(subSong->name,false);
|
||||
w->writeString(subSong->notes,false);
|
||||
w->writeC((unsigned char)(song.subsong.size()-1));
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(0);
|
||||
w->writeC(0);
|
||||
subSongPtrSeek=w->tell();
|
||||
// subsong pointers (we'll seek here later)
|
||||
for (size_t i=0; i<(song.subsong.size()-1); i++) {
|
||||
w->writeI(0);
|
||||
}
|
||||
|
||||
/// SUBSONGS
|
||||
for (subSongIndex=1; subSongIndex<song.subsong.size(); subSongIndex++) {
|
||||
subSong=song.subsong[subSongIndex];
|
||||
subSongPtr.push_back(w->tell());
|
||||
w->write("SONG",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
w->writeS(subSong->ordersLen);
|
||||
w->writeC(subSong->hilightA);
|
||||
w->writeC(subSong->hilightB);
|
||||
w->writeI(0); // reserved
|
||||
|
||||
w->writeString(subSong->name,false);
|
||||
w->writeString(subSong->notes,false);
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
w->writeC(subSong->orders.ord[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->pat[i].effectCols);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->chanShow[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->chanCollapse[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(subSong->chanName[i],false);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
}
|
||||
|
||||
/// INSTRUMENT
|
||||
for (int i=0; i<song.insLen; i++) {
|
||||
DivInstrument* ins=song.ins[i];
|
||||
insPtr[i]=w->tell();
|
||||
insPtr.push_back(w->tell());
|
||||
ins->putInsData(w);
|
||||
}
|
||||
|
||||
/// WAVETABLE
|
||||
for (int i=0; i<song.waveLen; i++) {
|
||||
DivWavetable* wave=song.wave[i];
|
||||
wavePtr[i]=w->tell();
|
||||
wavePtr.push_back(w->tell());
|
||||
wave->putWaveData(w);
|
||||
}
|
||||
|
||||
/// SAMPLE
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
samplePtr[i]=w->tell();
|
||||
samplePtr.push_back(w->tell());
|
||||
w->write("SMPL",4);
|
||||
w->writeI(0);
|
||||
|
||||
|
|
@ -2750,23 +2954,24 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
/// PATTERN
|
||||
for (int i: patsToWrite) {
|
||||
DivPattern* pat=song.pat[i>>16].getPattern(i&0xffff,false);
|
||||
for (PatToWrite& i: patsToWrite) {
|
||||
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
|
||||
patPtr.push_back(w->tell());
|
||||
w->write("PATR",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(i>>16);
|
||||
w->writeS(i&0xffff);
|
||||
w->writeS(i.chan);
|
||||
w->writeS(i.pat);
|
||||
w->writeS(i.subsong);
|
||||
|
||||
w->writeI(0); // reserved
|
||||
w->writeS(0); // reserved
|
||||
|
||||
for (int j=0; j<song.patLen; j++) {
|
||||
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
|
||||
w->write(&pat->data[j][4],2*song.pat[i>>16].effectCols*2); // effects
|
||||
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
|
||||
}
|
||||
|
||||
w->writeString(pat->name,false);
|
||||
|
|
@ -2779,21 +2984,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(insPtr[i]);
|
||||
}
|
||||
|
||||
// wavetable pointers (we'll seek here later)
|
||||
// wavetable pointers
|
||||
for (int i=0; i<song.waveLen; i++) {
|
||||
w->writeI(wavePtr[i]);
|
||||
}
|
||||
|
||||
// sample pointers (we'll seek here later)
|
||||
// sample pointers
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
w->writeI(samplePtr[i]);
|
||||
}
|
||||
|
||||
// pattern pointers (we'll seek here later)
|
||||
// pattern pointers
|
||||
for (int i: patPtr) {
|
||||
w->writeI(i);
|
||||
}
|
||||
|
||||
/// SUBSONG POINTERS
|
||||
w->seek(subSongPtrSeek,SEEK_SET);
|
||||
|
||||
// subsong pointers
|
||||
for (size_t i=0; i<(song.subsong.size()-1); i++) {
|
||||
w->writeI(subSongPtr[i]);
|
||||
}
|
||||
|
||||
saveLock.unlock();
|
||||
return w;
|
||||
}
|
||||
|
|
@ -2864,7 +3077,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
return NULL;
|
||||
}
|
||||
// fail if values are out of range
|
||||
if (song.ordersLen>127) {
|
||||
if (curSubSong->ordersLen>127) {
|
||||
logE("maximum .dmf song length is 127!");
|
||||
lastError="maximum .dmf song length is 127";
|
||||
return NULL;
|
||||
|
|
@ -2880,10 +3093,10 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
return NULL;
|
||||
}
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
if (song.orders.ord[i][j]>0x7f) {
|
||||
logE("order %d, %d is out of range (0-127)!",song.orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",song.orders.ord[i][j]);
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
if (curOrders->ord[i][j]>0x7f) {
|
||||
logE("order %d, %d is out of range (0-127)!",curOrders->ord[i][j]);
|
||||
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",curOrders->ord[i][j]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -2926,31 +3139,35 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
// song info
|
||||
w->writeString(song.name,true);
|
||||
w->writeString(song.author,true);
|
||||
w->writeC(song.hilightA);
|
||||
w->writeC(song.hilightB);
|
||||
w->writeC(curSubSong->hilightA);
|
||||
w->writeC(curSubSong->hilightB);
|
||||
|
||||
w->writeC(song.timeBase);
|
||||
w->writeC(song.speed1);
|
||||
w->writeC(song.speed2);
|
||||
w->writeC(song.pal);
|
||||
w->writeC(song.customTempo);
|
||||
w->writeC(curSubSong->timeBase);
|
||||
w->writeC(curSubSong->speed1);
|
||||
w->writeC(curSubSong->speed2);
|
||||
w->writeC(curSubSong->pal);
|
||||
w->writeC(curSubSong->customTempo);
|
||||
char customHz[4];
|
||||
memset(customHz,0,4);
|
||||
snprintf(customHz,4,"%d",(int)song.hz);
|
||||
snprintf(customHz,4,"%d",(int)curSubSong->hz);
|
||||
w->write(customHz,3);
|
||||
w->writeI(song.patLen);
|
||||
w->writeC(song.ordersLen);
|
||||
w->writeI(curSubSong->patLen);
|
||||
w->writeC(curSubSong->ordersLen);
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
w->writeC(song.orders.ord[i][j]);
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
w->writeC(curOrders->ord[i][j]);
|
||||
if (version>=25) {
|
||||
DivPattern* pat=song.pat[i].getPattern(j,false);
|
||||
DivPattern* pat=curPat[i].getPattern(j,false);
|
||||
w->writeString(pat->name,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (song.subsong.size()>1) {
|
||||
addWarning("only the currently selected subsong will be saved");
|
||||
}
|
||||
|
||||
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
||||
addWarning("duty precision will be lost");
|
||||
|
|
@ -3112,15 +3329,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
}
|
||||
|
||||
for (int i=0; i<getChannelCount(sys); i++) {
|
||||
w->writeC(song.pat[i].effectCols);
|
||||
w->writeC(curPat[i].effectCols);
|
||||
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false);
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
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++) {
|
||||
w->writeS(pat->data[k][0]); // note
|
||||
w->writeS(pat->data[k][1]); // octave
|
||||
w->writeS(pat->data[k][3]); // volume
|
||||
w->write(&pat->data[k][4],2*song.pat[i].effectCols*2); // effects
|
||||
w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects
|
||||
w->writeS(pat->data[k][2]); // instrument
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ void DivChannelData::wipePatterns() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPattern::copyOn(DivPattern *dest) {
|
||||
void DivPattern::copyOn(DivPattern* dest) {
|
||||
dest->name=name;
|
||||
memcpy(dest->data,data,sizeof(data));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,20 +17,20 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ym2610bext.h"
|
||||
#include "ym2203ext.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
||||
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
return DivPlatformYM2203::dispatch(c);
|
||||
}
|
||||
if (c.chan>5) {
|
||||
c.chan-=3;
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
return DivPlatformYM2203::dispatch(c);
|
||||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
|
|
@ -384,7 +384,7 @@ static int opChanOffsH[4]={
|
|||
0xad, 0xae, 0xac, 0xa6
|
||||
};
|
||||
|
||||
void DivPlatformYM2610BExt::tick(bool sysTick) {
|
||||
void DivPlatformYM2203Ext::tick(bool sysTick) {
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -401,7 +401,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2610B::tick(sysTick);
|
||||
DivPlatformYM2203::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -438,13 +438,13 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
||||
void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
||||
if (ch<2) {
|
||||
DivPlatformYM2610B::muteChannel(ch,mute);
|
||||
DivPlatformYM2203::muteChannel(ch,mute);
|
||||
return;
|
||||
}
|
||||
if (ch>5) {
|
||||
DivPlatformYM2610B::muteChannel(ch-3,mute);
|
||||
DivPlatformYM2203::muteChannel(ch-3,mute);
|
||||
return;
|
||||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
|
@ -462,7 +462,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::forceIns() {
|
||||
void DivPlatformYM2203Ext::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
|
|
@ -494,7 +494,6 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
|
@ -518,23 +517,23 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
}
|
||||
}
|
||||
|
||||
void* DivPlatformYM2610BExt::getChanState(int ch) {
|
||||
void* DivPlatformYM2203Ext::getChanState(int ch) {
|
||||
if (ch>=6) return &chan[ch-3];
|
||||
if (ch>=2) return &opChan[ch-2];
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
|
||||
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
|
||||
if (ch>=6) return oscBuf[ch-3];
|
||||
if (ch<3) return oscBuf[ch];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::reset() {
|
||||
DivPlatformYM2610B::reset();
|
||||
void DivPlatformYM2203Ext::reset() {
|
||||
DivPlatformYM2203::reset();
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformYM2610BExt::OpChannel();
|
||||
opChan[i]=DivPlatformYM2203Ext::OpChannel();
|
||||
opChan[i].vol=127;
|
||||
}
|
||||
|
||||
|
|
@ -543,12 +542,12 @@ void DivPlatformYM2610BExt::reset() {
|
|||
extMode=true;
|
||||
}
|
||||
|
||||
bool DivPlatformYM2610BExt::keyOffAffectsArp(int ch) {
|
||||
bool DivPlatformYM2203Ext::keyOffAffectsArp(int ch) {
|
||||
return (ch>8);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::notifyInsChange(int ins) {
|
||||
DivPlatformYM2610B::notifyInsChange(ins);
|
||||
void DivPlatformYM2203Ext::notifyInsChange(int ins) {
|
||||
DivPlatformYM2203::notifyInsChange(ins);
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].ins==ins) {
|
||||
opChan[i].insChanged=true;
|
||||
|
|
@ -556,8 +555,8 @@ void DivPlatformYM2610BExt::notifyInsChange(int ins) {
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610B::init(parent,channels,sugRate,flags);
|
||||
int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2203::init(parent,channels,sugRate,flags);
|
||||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
|
|
@ -566,9 +565,9 @@ int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, un
|
|||
return 19;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::quit() {
|
||||
DivPlatformYM2610B::quit();
|
||||
void DivPlatformYM2203Ext::quit() {
|
||||
DivPlatformYM2203::quit();
|
||||
}
|
||||
|
||||
DivPlatformYM2610BExt::~DivPlatformYM2610BExt() {
|
||||
DivPlatformYM2203Ext::~DivPlatformYM2203Ext() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
|
||||
#include "ym2610b.h"
|
||||
#include "ym2203.h"
|
||||
|
||||
class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
||||
class DivPlatformYM2203Ext: public DivPlatformYM2203 {
|
||||
struct OpChannel {
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
|
|
@ -47,5 +47,5 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
|||
void notifyInsChange(int ins);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2610BExt();
|
||||
~DivPlatformYM2203Ext();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
|||
void DivEngine::nextOrder() {
|
||||
curRow=0;
|
||||
if (repeatPattern) return;
|
||||
if (++curOrder>=song.ordersLen) {
|
||||
if (++curOrder>=curSubSong->ordersLen) {
|
||||
endOfSong=true;
|
||||
curOrder=0;
|
||||
}
|
||||
|
|
@ -266,11 +266,11 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
|||
void DivEngine::processRow(int i, bool afterDelay) {
|
||||
int whatOrder=afterDelay?chan[i].delayOrder:curOrder;
|
||||
int whatRow=afterDelay?chan[i].delayRow:curRow;
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false);
|
||||
// pre effects
|
||||
if (!afterDelay) {
|
||||
bool returnAfterPre=false;
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
}
|
||||
break;
|
||||
case 0x0d: // next order
|
||||
if (changeOrd<0 && (curOrder<(song.ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
||||
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
||||
changeOrd=-2;
|
||||
changePos=effectVal;
|
||||
}
|
||||
|
|
@ -405,7 +405,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
bool panChanged=false;
|
||||
|
||||
// effects
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
|
@ -548,7 +548,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
break;
|
||||
case 0xe0: // arp speed
|
||||
if (effectVal>0) {
|
||||
song.arpLen=effectVal;
|
||||
curSubSong->arpLen=effectVal;
|
||||
}
|
||||
break;
|
||||
case 0xe1: // portamento up
|
||||
|
|
@ -727,7 +727,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].noteOnInhibit=false;
|
||||
|
||||
// post effects
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
|
@ -745,10 +745,10 @@ void DivEngine::nextRow() {
|
|||
strcpy(pb1,"");
|
||||
strcpy(pb3,"");
|
||||
for (int i=0; i<chans; i++) {
|
||||
snprintf(pb,4095," %.2x",song.orders.ord[i][curOrder]);
|
||||
snprintf(pb,4095," %.2x",curOrders->ord[i][curOrder]);
|
||||
strcat(pb1,pb);
|
||||
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
|
||||
snprintf(pb2,4095,"\x1b[37m %s",
|
||||
formatNote(pat->data[curRow][0],pat->data[curRow][1]));
|
||||
strcat(pb3,pb2);
|
||||
|
|
@ -764,7 +764,7 @@ void DivEngine::nextRow() {
|
|||
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
|
||||
strcat(pb3,pb2);
|
||||
}
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==-1) {
|
||||
strcat(pb3,"\x1b[m--");
|
||||
} else {
|
||||
|
|
@ -796,32 +796,32 @@ void DivEngine::nextRow() {
|
|||
if (changeOrd==-2) changeOrd=curOrder+1;
|
||||
if (changeOrd<=curOrder) endOfSong=true;
|
||||
curOrder=changeOrd;
|
||||
if (curOrder>=song.ordersLen) {
|
||||
if (curOrder>=curSubSong->ordersLen) {
|
||||
curOrder=0;
|
||||
endOfSong=true;
|
||||
}
|
||||
changeOrd=-1;
|
||||
}
|
||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||
} else if (playing) if (++curRow>=song.patLen) {
|
||||
} else if (playing) if (++curRow>=curSubSong->patLen) {
|
||||
nextOrder();
|
||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||
}
|
||||
|
||||
if (song.brokenSpeedSel) {
|
||||
if ((song.patLen&1) && curOrder&1) {
|
||||
ticks=((curRow&1)?speed2:speed1)*(song.timeBase+1);
|
||||
if ((curSubSong->patLen&1) && curOrder&1) {
|
||||
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
|
||||
nextSpeed=(curRow&1)?speed1:speed2;
|
||||
} else {
|
||||
ticks=((curRow&1)?speed1:speed2)*(song.timeBase+1);
|
||||
ticks=((curRow&1)?speed1:speed2)*(curSubSong->timeBase+1);
|
||||
nextSpeed=(curRow&1)?speed2:speed1;
|
||||
}
|
||||
} else {
|
||||
if (speedAB) {
|
||||
ticks=speed2*(song.timeBase+1);
|
||||
ticks=speed2*(curSubSong->timeBase+1);
|
||||
nextSpeed=speed1;
|
||||
} else {
|
||||
ticks=speed1*(song.timeBase+1);
|
||||
ticks=speed1*(curSubSong->timeBase+1);
|
||||
nextSpeed=speed2;
|
||||
}
|
||||
speedAB=!speedAB;
|
||||
|
|
@ -829,7 +829,7 @@ void DivEngine::nextRow() {
|
|||
|
||||
// post row details
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
|
||||
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
||||
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
|
||||
if (!chan[i].legato) {
|
||||
|
|
@ -838,7 +838,7 @@ void DivEngine::nextRow() {
|
|||
if (song.oneTickCut) {
|
||||
bool doPrepareCut=true;
|
||||
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||
doPrepareCut=false;
|
||||
break;
|
||||
|
|
@ -1007,7 +1007,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
|
||||
if (--chan[i].arpTicks<1) {
|
||||
chan[i].arpTicks=song.arpLen;
|
||||
chan[i].arpTicks=curSubSong->arpLen;
|
||||
chan[i].arpStage++;
|
||||
if (chan[i].arpStage>2) chan[i].arpStage=0;
|
||||
switch (chan[i].arpStage) {
|
||||
|
|
@ -1048,7 +1048,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
}
|
||||
|
||||
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond);
|
||||
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
|
||||
}
|
||||
|
||||
if (haltOn==DIV_HALT_TICK) halted=true;
|
||||
|
|
@ -1240,11 +1240,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
if (!freelance && stepPlay!=-1 && subticks==1) {
|
||||
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
||||
if (realPos>=size) realPos=size-1;
|
||||
if (song.hilightA>0) {
|
||||
if ((curRow%song.hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
||||
if (curSubSong->hilightA>0) {
|
||||
if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
||||
}
|
||||
if (song.hilightB>0) {
|
||||
if ((curRow%song.hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
||||
if (curSubSong->hilightB>0) {
|
||||
if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
||||
}
|
||||
}
|
||||
if (nextTick()) {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,18 @@ bool DivSample::initInternal(unsigned char d, int count) {
|
|||
dataDPCM=new unsigned char[lengthDPCM];
|
||||
memset(dataDPCM,0,lengthDPCM);
|
||||
break;
|
||||
case 2: // AICA ADPCM
|
||||
if (dataAICA!=NULL) delete[] dataAICA;
|
||||
lengthAICA=(count+1)/2;
|
||||
dataAICA=new unsigned char[(lengthAICA+255)&(~0xff)];
|
||||
memset(dataAICA,0,(lengthAICA+255)&(~0xff));
|
||||
break;
|
||||
case 3: // YMZ ADPCM
|
||||
if (dataZ!=NULL) delete[] dataZ;
|
||||
lengthZ=(count+1)/2;
|
||||
dataZ=new unsigned char[(lengthZ+255)&(~0xff)];
|
||||
memset(dataZ,0,(lengthZ+255)&(~0xff));
|
||||
break;
|
||||
case 4: // QSound ADPCM
|
||||
if (dataQSoundA!=NULL) delete[] dataQSoundA;
|
||||
lengthQSoundA=(count+1)/2;
|
||||
|
|
@ -657,6 +669,12 @@ void DivSample::render() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 2: // AICA ADPCM
|
||||
aica_decode(dataAICA,data16,samples);
|
||||
break;
|
||||
case 3: // YMZ ADPCM
|
||||
ymz_decode(dataZ,data16,samples);
|
||||
break;
|
||||
case 4: // QSound ADPCM
|
||||
bs_decode(dataQSoundA,data16,samples);
|
||||
break;
|
||||
|
|
@ -709,6 +727,14 @@ void DivSample::render() {
|
|||
if (accum>127) accum=127;
|
||||
}
|
||||
}
|
||||
if (depth!=2) { // AICA ADPCM
|
||||
if (!initInternal(2,samples)) return;
|
||||
aica_encode(data16,dataAICA,(samples+511)&(~0x1ff));
|
||||
}
|
||||
if (depth!=3) { // YMZ ADPCM
|
||||
if (!initInternal(3,samples)) return;
|
||||
ymz_encode(data16,dataZ,(samples+511)&(~0x1ff));
|
||||
}
|
||||
if (depth!=4) { // QSound ADPCM
|
||||
if (!initInternal(4,samples)) return;
|
||||
bs_encode(data16,dataQSoundA,samples);
|
||||
|
|
@ -745,6 +771,10 @@ void* DivSample::getCurBuf() {
|
|||
return data1;
|
||||
case 1:
|
||||
return dataDPCM;
|
||||
case 2:
|
||||
return dataAICA;
|
||||
case 3:
|
||||
return dataZ;
|
||||
case 4:
|
||||
return dataQSoundA;
|
||||
case 5:
|
||||
|
|
@ -771,6 +801,10 @@ unsigned int DivSample::getCurBufLen() {
|
|||
return length1;
|
||||
case 1:
|
||||
return lengthDPCM;
|
||||
case 2:
|
||||
return lengthAICA;
|
||||
case 3:
|
||||
return lengthZ;
|
||||
case 4:
|
||||
return lengthQSoundA;
|
||||
case 5:
|
||||
|
|
@ -881,6 +915,8 @@ DivSample::~DivSample() {
|
|||
if (data16) delete[] data16;
|
||||
if (data1) delete[] data1;
|
||||
if (dataDPCM) delete[] dataDPCM;
|
||||
if (dataAICA) delete[] dataAICA;
|
||||
if (dataZ) delete[] dataZ;
|
||||
if (dataQSoundA) delete[] dataQSoundA;
|
||||
if (dataA) delete[] dataA;
|
||||
if (dataB) delete[] dataB;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ struct DivSample {
|
|||
// valid values are:
|
||||
// - 0: ZX Spectrum overlay drum (1-bit)
|
||||
// - 1: 1-bit NES DPCM (1-bit)
|
||||
// - 2: AICA ADPCM
|
||||
// - 3: YMZ ADPCM
|
||||
// - 4: QSound ADPCM
|
||||
// - 5: ADPCM-A
|
||||
// - 6: ADPCM-B
|
||||
|
|
@ -77,6 +79,8 @@ struct DivSample {
|
|||
short* data16; // 16
|
||||
unsigned char* data1; // 0
|
||||
unsigned char* dataDPCM; // 1
|
||||
unsigned char* dataAICA; // 2
|
||||
unsigned char* dataZ; // 3
|
||||
unsigned char* dataQSoundA; // 4
|
||||
unsigned char* dataA; // 5
|
||||
unsigned char* dataB; // 6
|
||||
|
|
@ -84,8 +88,8 @@ struct DivSample {
|
|||
unsigned char* dataBRR; // 9
|
||||
unsigned char* dataVOX; // 10
|
||||
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthAICA, lengthZ, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||
unsigned int off8, off16, off1, offDPCM, offAICA, offZ, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||
unsigned int offSegaPCM, offQSound, offX1_010, offSU;
|
||||
|
||||
unsigned int samples;
|
||||
|
|
@ -218,6 +222,8 @@ struct DivSample {
|
|||
data16(NULL),
|
||||
data1(NULL),
|
||||
dataDPCM(NULL),
|
||||
dataAICA(NULL),
|
||||
dataZ(NULL),
|
||||
dataQSoundA(NULL),
|
||||
dataA(NULL),
|
||||
dataB(NULL),
|
||||
|
|
@ -228,6 +234,8 @@ struct DivSample {
|
|||
length16(0),
|
||||
length1(0),
|
||||
lengthDPCM(0),
|
||||
lengthAICA(0),
|
||||
lengthZ(0),
|
||||
lengthQSoundA(0),
|
||||
lengthA(0),
|
||||
lengthB(0),
|
||||
|
|
@ -238,6 +246,8 @@ struct DivSample {
|
|||
off16(0),
|
||||
off1(0),
|
||||
offDPCM(0),
|
||||
offAICA(0),
|
||||
offZ(0),
|
||||
offQSoundA(0),
|
||||
offA(0),
|
||||
offB(0),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "song.h"
|
||||
|
||||
void DivSong::clearSongData() {
|
||||
void DivSubSong::clearData() {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
pat[i].wipePatterns();
|
||||
}
|
||||
|
|
@ -28,6 +28,15 @@ void DivSong::clearSongData() {
|
|||
ordersLen=1;
|
||||
}
|
||||
|
||||
void DivSong::clearSongData() {
|
||||
for (DivSubSong* i: subsong) {
|
||||
i->clearData();
|
||||
delete i;
|
||||
}
|
||||
subsong.clear();
|
||||
subsong.push_back(new DivSubSong);
|
||||
}
|
||||
|
||||
void DivSong::clearInstruments() {
|
||||
for (DivInstrument* i: ins) {
|
||||
delete i;
|
||||
|
|
@ -71,7 +80,9 @@ void DivSong::unload() {
|
|||
sample.clear();
|
||||
sampleLen=0;
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
pat[i].wipePatterns();
|
||||
for (DivSubSong* i: subsong) {
|
||||
i->clearData();
|
||||
delete i;
|
||||
}
|
||||
subsong.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,44 @@ enum DivSystem {
|
|||
DIV_SYSTEM_DUMMY
|
||||
};
|
||||
|
||||
struct DivSubSong {
|
||||
String name, notes;
|
||||
unsigned char hilightA, hilightB;
|
||||
unsigned char timeBase, speed1, speed2, arpLen;
|
||||
bool pal;
|
||||
bool customTempo;
|
||||
float hz;
|
||||
int patLen, ordersLen;
|
||||
|
||||
DivOrders orders;
|
||||
DivChannelData pat[DIV_MAX_CHANS];
|
||||
|
||||
bool chanShow[DIV_MAX_CHANS];
|
||||
unsigned char chanCollapse[DIV_MAX_CHANS];
|
||||
String chanName[DIV_MAX_CHANS];
|
||||
String chanShortName[DIV_MAX_CHANS];
|
||||
|
||||
void clearData();
|
||||
|
||||
DivSubSong():
|
||||
hilightA(4),
|
||||
hilightB(16),
|
||||
timeBase(0),
|
||||
speed1(6),
|
||||
speed2(6),
|
||||
arpLen(1),
|
||||
pal(true),
|
||||
customTempo(false),
|
||||
hz(60.0),
|
||||
patLen(64),
|
||||
ordersLen(1) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
chanShow[i]=true;
|
||||
chanCollapse[i]=0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DivSong {
|
||||
// version number used for saving the song.
|
||||
// Furnace will save using the latest possible version,
|
||||
|
|
@ -156,14 +194,21 @@ struct DivSong {
|
|||
// - introduces Genesis system
|
||||
// - introduces system number
|
||||
// - patterns now stored in current known format
|
||||
// - 8: ???
|
||||
// - only used in the Medivo YMU cover
|
||||
// - 7: ???
|
||||
// - only present in a later version of First.dmf
|
||||
// - pattern format changes: empty field is 0xFF instead of 0x80
|
||||
// - instrument now stored in pattern
|
||||
// - 5: BETA 3
|
||||
// - adds arpeggio tick
|
||||
// - 4: BETA 2
|
||||
// - possibly adds instrument number (stored in channel)?
|
||||
// - cannot confirm as I don't have any version 4 modules
|
||||
// - 3: BETA 1
|
||||
// - possibly the first version that could save
|
||||
// - basic format, no system number, 16 instruments, one speed, YMU759-only
|
||||
// - patterns were stored in a different format (chars instead of shorts)
|
||||
// - patterns were stored in a different format (chars instead of shorts) and no instrument
|
||||
// - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle
|
||||
unsigned short version;
|
||||
bool isDMF;
|
||||
|
|
@ -285,19 +330,10 @@ struct DivSong {
|
|||
String nameJ, authorJ, categoryJ;
|
||||
|
||||
// other things
|
||||
String chanName[DIV_MAX_CHANS];
|
||||
String chanShortName[DIV_MAX_CHANS];
|
||||
String notes;
|
||||
|
||||
// highlight
|
||||
unsigned char hilightA, hilightB;
|
||||
|
||||
// module details
|
||||
unsigned char timeBase, speed1, speed2, arpLen;
|
||||
bool pal;
|
||||
bool customTempo;
|
||||
float hz;
|
||||
int patLen, ordersLen, insLen, waveLen, sampleLen;
|
||||
int insLen, waveLen, sampleLen;
|
||||
float masterVol;
|
||||
float tuning;
|
||||
|
||||
|
|
@ -345,14 +381,11 @@ struct DivSong {
|
|||
bool snDutyReset;
|
||||
bool pitchMacroIsLinear;
|
||||
|
||||
DivOrders orders;
|
||||
std::vector<DivInstrument*> ins;
|
||||
DivChannelData pat[DIV_MAX_CHANS];
|
||||
std::vector<DivWavetable*> wave;
|
||||
std::vector<DivSample*> sample;
|
||||
|
||||
bool chanShow[DIV_MAX_CHANS];
|
||||
unsigned char chanCollapse[DIV_MAX_CHANS];
|
||||
std::vector<DivSubSong*> subsong;
|
||||
|
||||
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound;
|
||||
DivWavetable nullWave;
|
||||
|
|
@ -401,17 +434,6 @@ struct DivSong {
|
|||
manInfo(""),
|
||||
createdDate(""),
|
||||
revisionDate(""),
|
||||
hilightA(4),
|
||||
hilightB(16),
|
||||
timeBase(0),
|
||||
speed1(6),
|
||||
speed2(6),
|
||||
arpLen(1),
|
||||
pal(true),
|
||||
customTempo(false),
|
||||
hz(60.0),
|
||||
patLen(64),
|
||||
ordersLen(1),
|
||||
insLen(0),
|
||||
waveLen(0),
|
||||
sampleLen(0),
|
||||
|
|
@ -457,10 +479,7 @@ struct DivSong {
|
|||
systemPan[i]=0;
|
||||
systemFlags[i]=0;
|
||||
}
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
chanShow[i]=true;
|
||||
chanCollapse[i]=0;
|
||||
}
|
||||
subsong.push_back(new DivSubSong);
|
||||
system[0]=DIV_SYSTEM_YM2612;
|
||||
system[1]=DIV_SYSTEM_SMS;
|
||||
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) {
|
|||
|
||||
const char* DivEngine::getChannelName(int chan) {
|
||||
if (chan<0 || chan>chans) return "??";
|
||||
if (!song.chanName[chan].empty()) return song.chanName[chan].c_str();
|
||||
if (!curSubSong->chanName[chan].empty()) return curSubSong->chanName[chan].c_str();
|
||||
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||
|
||||
const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]];
|
||||
|
|
@ -293,7 +293,7 @@ const char* DivEngine::getChannelName(int chan) {
|
|||
|
||||
const char* DivEngine::getChannelShortName(int chan) {
|
||||
if (chan<0 || chan>chans) return "??";
|
||||
if (!song.chanShortName[chan].empty()) return song.chanShortName[chan].c_str();
|
||||
if (!curSubSong->chanShortName[chan].empty()) return curSubSong->chanShortName[chan].c_str();
|
||||
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||
|
||||
const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue