dev95 - multiple songs in a single file (READ)
experimental feature! proceed with caution. if you experience song corruption or crashes, report issue immediately! files with multiple songs will be readable in older versions of Furnace, but only the first song will be read in those versions. issue #199
This commit is contained in:
parent
14053f70cb
commit
c5786b61fb
|
@ -445,6 +445,7 @@ src/gui/settings.cpp
|
||||||
src/gui/songInfo.cpp
|
src/gui/songInfo.cpp
|
||||||
src/gui/songNotes.cpp
|
src/gui/songNotes.cpp
|
||||||
src/gui/stats.cpp
|
src/gui/stats.cpp
|
||||||
|
src/gui/subSongs.cpp
|
||||||
src/gui/sysConf.cpp
|
src/gui/sysConf.cpp
|
||||||
src/gui/sysEx.cpp
|
src/gui/sysEx.cpp
|
||||||
src/gui/util.cpp
|
src/gui/util.cpp
|
||||||
|
|
|
@ -29,6 +29,11 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 95: Furnace dev95
|
||||||
|
- 94: Furnace dev94
|
||||||
|
- 93: Furnace dev93
|
||||||
|
- 92: Furnace dev92
|
||||||
|
- 91: Furnace dev91
|
||||||
- 90: Furnace dev90
|
- 90: Furnace dev90
|
||||||
- 89: Furnace dev89
|
- 89: Furnace dev89
|
||||||
- 88: Furnace dev88
|
- 88: Furnace dev88
|
||||||
|
@ -122,26 +127,26 @@ size | description
|
||||||
-----|------------------------------------
|
-----|------------------------------------
|
||||||
4 | "INFO" block ID
|
4 | "INFO" block ID
|
||||||
4 | reserved
|
4 | reserved
|
||||||
1 | time base
|
1 | time base (of first song)
|
||||||
1 | speed 1
|
1 | speed 1 (of first song)
|
||||||
1 | speed 2
|
1 | speed 2 (of first song)
|
||||||
1 | initial arpeggio time
|
1 | initial arpeggio time (of first song)
|
||||||
4f | ticks per second
|
4f | ticks per second (of first song)
|
||||||
| - 60 is NTSC
|
| - 60 is NTSC
|
||||||
| - 50 is PAL
|
| - 50 is PAL
|
||||||
2 | pattern length
|
2 | pattern length (of first song)
|
||||||
| - the limit is 256.
|
| - the limit is 256.
|
||||||
2 | orders length
|
2 | orders length (of first song)
|
||||||
| - the limit is 256 (>=80) or 127 (<80).
|
| - the limit is 256 (>=80) or 127 (<80).
|
||||||
1 | highlight A
|
1 | highlight A (of first song)
|
||||||
1 | highlight B
|
1 | highlight B (of first song)
|
||||||
2 | instrument count
|
2 | instrument count
|
||||||
| - the limit is 256.
|
| - the limit is 256.
|
||||||
2 | wavetable count
|
2 | wavetable count
|
||||||
| - the limit is 256.
|
| - the limit is 256.
|
||||||
2 | sample count
|
2 | sample count
|
||||||
| - the limit is 256.
|
| - the limit is 256.
|
||||||
4 | pattern count
|
4 | pattern count (global)
|
||||||
32 | list of sound chips
|
32 | list of sound chips
|
||||||
| - possible soundchips:
|
| - possible soundchips:
|
||||||
| - 0x00: end of list
|
| - 0x00: end of list
|
||||||
|
@ -258,20 +263,20 @@ size | description
|
||||||
4?? | pointers to wavetables
|
4?? | pointers to wavetables
|
||||||
4?? | pointers to samples
|
4?? | pointers to samples
|
||||||
4?? | pointers to patterns
|
4?? | pointers to patterns
|
||||||
??? | orders
|
??? | orders (of first song)
|
||||||
| - a table of bytes
|
| - a table of bytes
|
||||||
| - size=channels*ordLen
|
| - size=channels*ordLen
|
||||||
| - read orders then channels
|
| - read orders then channels
|
||||||
| - the maximum value of a cell is FF (>=80) or 7F (<80).
|
| - the maximum value of a cell is FF (>=80) or 7F (<80).
|
||||||
??? | effect columns
|
??? | effect columns (of first song)
|
||||||
| - size=channels
|
| - size=channels
|
||||||
1?? | channel hide status
|
1?? | channel hide status (of first song)
|
||||||
| - size=channels
|
| - size=channels
|
||||||
1?? | channel collapse status
|
1?? | channel collapse status (of first song)
|
||||||
| - size=channels
|
| - size=channels
|
||||||
S?? | channel names
|
S?? | channel names (of first song)
|
||||||
| - a list of channelCount C strings
|
| - a list of channelCount C strings
|
||||||
S?? | channel short names
|
S?? | channel short names (of first song)
|
||||||
| - same as above
|
| - same as above
|
||||||
STR | song comment
|
STR | song comment
|
||||||
4f | master volume, 1.0f=100% (>=59)
|
4f | master volume, 1.0f=100% (>=59)
|
||||||
|
@ -292,6 +297,55 @@ size | description
|
||||||
1 | pitch macro is linear (>=90) or reserved
|
1 | pitch macro is linear (>=90) or reserved
|
||||||
1 | pitch slide speed in full linear pitch mode (>=94) or reserved
|
1 | pitch slide speed in full linear pitch mode (>=94) or reserved
|
||||||
18 | reserved
|
18 | reserved
|
||||||
|
--- | **additional subsongs** (>=95)
|
||||||
|
STR | first subsong name
|
||||||
|
STR | first subsong comment
|
||||||
|
1 | number of additional subsongs
|
||||||
|
3 | reserved
|
||||||
|
4?? | pointers to subsong data
|
||||||
|
```
|
||||||
|
|
||||||
|
# subsong
|
||||||
|
|
||||||
|
from version 95 onwards, Furnace supports storing multiple songs on a single file.
|
||||||
|
the way it's currently done is really weird, but it provides for some backwards compatibility (previous versions will only load the first subsong which is already defined in the `INFO` block).
|
||||||
|
|
||||||
|
```
|
||||||
|
size | description
|
||||||
|
-----|------------------------------------
|
||||||
|
4 | "SONG" block ID
|
||||||
|
4 | reserved
|
||||||
|
1 | time base
|
||||||
|
1 | speed 1
|
||||||
|
1 | speed 2
|
||||||
|
1 | initial arpeggio time
|
||||||
|
4f | ticks per second
|
||||||
|
| - 60 is NTSC
|
||||||
|
| - 50 is PAL
|
||||||
|
2 | pattern length
|
||||||
|
| - the limit is 256.
|
||||||
|
2 | orders length
|
||||||
|
| - the limit is 256.
|
||||||
|
1 | highlight A
|
||||||
|
1 | highlight B
|
||||||
|
4 | reserved
|
||||||
|
STR | subsong name
|
||||||
|
STR | subsong comment
|
||||||
|
??? | orders
|
||||||
|
| - a table of bytes
|
||||||
|
| - size=channels*ordLen
|
||||||
|
| - read orders then channels
|
||||||
|
| - the maximum value of a cell is FF.
|
||||||
|
??? | effect columns
|
||||||
|
| - size=channels
|
||||||
|
1?? | channel hide status
|
||||||
|
| - size=channels
|
||||||
|
1?? | channel collapse status
|
||||||
|
| - size=channels
|
||||||
|
S?? | channel names
|
||||||
|
| - a list of channelCount C strings
|
||||||
|
S?? | channel short names
|
||||||
|
| - same as above
|
||||||
```
|
```
|
||||||
|
|
||||||
# instrument
|
# instrument
|
||||||
|
@ -772,7 +826,8 @@ size | description
|
||||||
4 | reserved
|
4 | reserved
|
||||||
2 | channel
|
2 | channel
|
||||||
2 | pattern index
|
2 | pattern index
|
||||||
4 | reserved
|
2 | subsong (>=95) or reserved
|
||||||
|
2 | reserved
|
||||||
??? | pattern data
|
??? | pattern data
|
||||||
| - size: rows*(4+effectColumns*2)*2
|
| - size: rows*(4+effectColumns*2)*2
|
||||||
| - read shorts in this order:
|
| - read shorts in this order:
|
||||||
|
|
|
@ -139,18 +139,18 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
|
||||||
int nextRow=0;
|
int nextRow=0;
|
||||||
int effectVal=0;
|
int effectVal=0;
|
||||||
DivPattern* pat[DIV_MAX_CHANS];
|
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++) {
|
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;
|
nextRow=0;
|
||||||
for (int k=0; k<chans; k++) {
|
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)];
|
effectVal=pat[k]->data[j][5+(l<<1)];
|
||||||
if (effectVal<0) effectVal=0;
|
if (effectVal<0) effectVal=0;
|
||||||
if (pat[k]->data[j][4+(l<<1)]==0x0d) {
|
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;
|
nextOrder=i+1;
|
||||||
nextRow=effectVal;
|
nextRow=effectVal;
|
||||||
}
|
}
|
||||||
|
@ -695,6 +695,7 @@ void DivEngine::createNew(const int* description) {
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
song.unload();
|
song.unload();
|
||||||
song=DivSong();
|
song=DivSong();
|
||||||
|
changeSong(0);
|
||||||
if (description!=NULL) {
|
if (description!=NULL) {
|
||||||
initSongWithDesc(description);
|
initSongWithDesc(description);
|
||||||
}
|
}
|
||||||
|
@ -716,45 +717,55 @@ void DivEngine::swapChannels(int src, int dest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<256; i++) {
|
||||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||||
song.orders.ord[src][i]^=song.orders.ord[dest][i];
|
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
||||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||||
|
|
||||||
DivPattern* prev=song.pat[src].data[i];
|
DivPattern* prev=curPat[src].data[i];
|
||||||
song.pat[src].data[i]=song.pat[dest].data[i];
|
curPat[src].data[i]=curPat[dest].data[i];
|
||||||
song.pat[dest].data[i]=prev;
|
curPat[dest].data[i]=prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||||
song.pat[dest].effectCols^=song.pat[src].effectCols;
|
curPat[dest].effectCols^=curPat[src].effectCols;
|
||||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||||
|
|
||||||
String prevChanName=song.chanName[src];
|
String prevChanName=curSubSong->chanName[src];
|
||||||
String prevChanShortName=song.chanShortName[src];
|
String prevChanShortName=curSubSong->chanShortName[src];
|
||||||
bool prevChanShow=song.chanShow[src];
|
bool prevChanShow=curSubSong->chanShow[src];
|
||||||
bool prevChanCollapse=song.chanCollapse[src];
|
bool prevChanCollapse=curSubSong->chanCollapse[src];
|
||||||
|
|
||||||
song.chanName[src]=song.chanName[dest];
|
curSubSong->chanName[src]=curSubSong->chanName[dest];
|
||||||
song.chanShortName[src]=song.chanShortName[dest];
|
curSubSong->chanShortName[src]=curSubSong->chanShortName[dest];
|
||||||
song.chanShow[src]=song.chanShow[dest];
|
curSubSong->chanShow[src]=curSubSong->chanShow[dest];
|
||||||
song.chanCollapse[src]=song.chanCollapse[dest];
|
curSubSong->chanCollapse[src]=curSubSong->chanCollapse[dest];
|
||||||
song.chanName[dest]=prevChanName;
|
curSubSong->chanName[dest]=prevChanName;
|
||||||
song.chanShortName[dest]=prevChanShortName;
|
curSubSong->chanShortName[dest]=prevChanShortName;
|
||||||
song.chanShow[dest]=prevChanShow;
|
curSubSong->chanShow[dest]=prevChanShow;
|
||||||
song.chanCollapse[dest]=prevChanCollapse;
|
curSubSong->chanCollapse[dest]=prevChanCollapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivEngine::stompChannel(int ch) {
|
void DivEngine::stompChannel(int ch) {
|
||||||
logV("stomping channel %d",ch);
|
logV("stomping channel %d",ch);
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<256; i++) {
|
||||||
song.orders.ord[ch][i]=0;
|
curOrders->ord[ch][i]=0;
|
||||||
}
|
}
|
||||||
song.pat[ch].wipePatterns();
|
curPat[ch].wipePatterns();
|
||||||
song.pat[ch].effectCols=1;
|
curPat[ch].effectCols=1;
|
||||||
song.chanName[ch]="";
|
curSubSong->chanName[ch]="";
|
||||||
song.chanShortName[ch]="";
|
curSubSong->chanShortName[ch]="";
|
||||||
song.chanShow[ch]=true;
|
curSubSong->chanShow[ch]=true;
|
||||||
song.chanCollapse[ch]=false;
|
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) {
|
void DivEngine::swapChannelsP(int src, int dest) {
|
||||||
|
@ -767,6 +778,41 @@ void DivEngine::swapChannelsP(int src, int dest) {
|
||||||
BUSY_END;
|
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) {
|
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||||
int chanCount=chans;
|
int chanCount=chans;
|
||||||
quitDispatch();
|
quitDispatch();
|
||||||
|
@ -1321,15 +1367,15 @@ void DivEngine::reset() {
|
||||||
}
|
}
|
||||||
extValue=0;
|
extValue=0;
|
||||||
extValuePresent=0;
|
extValuePresent=0;
|
||||||
speed1=song.speed1;
|
speed1=curSubSong->speed1;
|
||||||
speed2=song.speed2;
|
speed2=curSubSong->speed2;
|
||||||
firstTick=false;
|
firstTick=false;
|
||||||
nextSpeed=speed1;
|
nextSpeed=speed1;
|
||||||
divider=60;
|
divider=60;
|
||||||
if (song.customTempo) {
|
if (curSubSong->customTempo) {
|
||||||
divider=song.hz;
|
divider=curSubSong->hz;
|
||||||
} else {
|
} else {
|
||||||
if (song.pal) {
|
if (curSubSong->pal) {
|
||||||
divider=60;
|
divider=60;
|
||||||
} else {
|
} else {
|
||||||
divider=50;
|
divider=50;
|
||||||
|
@ -1476,6 +1522,10 @@ int DivEngine::getRow() {
|
||||||
return curRow;
|
return curRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t DivEngine::getCurrentSubSong() {
|
||||||
|
return curSubSongIndex;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char DivEngine::getSpeed1() {
|
unsigned char DivEngine::getSpeed1() {
|
||||||
return speed1;
|
return speed1;
|
||||||
}
|
}
|
||||||
|
@ -1485,9 +1535,9 @@ unsigned char DivEngine::getSpeed2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float DivEngine::getHz() {
|
float DivEngine::getHz() {
|
||||||
if (song.customTempo) {
|
if (curSubSong->customTempo) {
|
||||||
return song.hz;
|
return curSubSong->hz;
|
||||||
} else if (song.pal) {
|
} else if (curSubSong->pal) {
|
||||||
return 60.0;
|
return 60.0;
|
||||||
} else {
|
} else {
|
||||||
return 50.0;
|
return 50.0;
|
||||||
|
@ -1659,10 +1709,10 @@ void DivEngine::delInstrument(int index) {
|
||||||
song.insLen=song.ins.size();
|
song.insLen=song.ins.size();
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<256; j++) {
|
||||||
if (song.pat[i].data[j]==NULL) continue;
|
if (curPat[i].data[j]==NULL) continue;
|
||||||
for (int k=0; k<song.patLen; k++) {
|
for (int k=0; k<curSubSong->patLen; k++) {
|
||||||
if (song.pat[i].data[j]->data[k][2]>index) {
|
if (curPat[i].data[j]->data[k][2]>index) {
|
||||||
song.pat[i].data[j]->data[k][2]--;
|
curPat[i].data[j]->data[k][2]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2048,18 +2098,18 @@ void DivEngine::delSample(int index) {
|
||||||
|
|
||||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||||
unsigned char order[DIV_MAX_CHANS];
|
unsigned char order[DIV_MAX_CHANS];
|
||||||
if (song.ordersLen>=0xff) return;
|
if (curSubSong->ordersLen>=0xff) return;
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
order[i]=song.orders.ord[i][curOrder];
|
order[i]=curOrders->ord[i][curOrder];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool used[256];
|
bool used[256];
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
memset(used,0,sizeof(bool)*256);
|
memset(used,0,sizeof(bool)*256);
|
||||||
for (int j=0; j<song.ordersLen; j++) {
|
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||||
used[song.orders.ord[i][j]]=true;
|
used[curOrders->ord[i][j]]=true;
|
||||||
}
|
}
|
||||||
order[i]=0xff;
|
order[i]=0xff;
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<256; j++) {
|
||||||
|
@ -2073,19 +2123,19 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
||||||
if (where) { // at the end
|
if (where) { // at the end
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
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();
|
saveLock.unlock();
|
||||||
} else { // after current order
|
} else { // after current order
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
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();
|
saveLock.unlock();
|
||||||
curOrder++;
|
curOrder++;
|
||||||
if (playing && !freelance) {
|
if (playing && !freelance) {
|
||||||
|
@ -2097,21 +2147,21 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
||||||
|
|
||||||
void DivEngine::deepCloneOrder(bool where) {
|
void DivEngine::deepCloneOrder(bool where) {
|
||||||
unsigned char order[DIV_MAX_CHANS];
|
unsigned char order[DIV_MAX_CHANS];
|
||||||
if (song.ordersLen>=0xff) return;
|
if (curSubSong->ordersLen>=0xff) return;
|
||||||
warnings="";
|
warnings="";
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
bool didNotFind=true;
|
bool didNotFind=true;
|
||||||
logD("channel %d",i);
|
logD("channel %d",i);
|
||||||
order[i]=song.orders.ord[i][curOrder];
|
order[i]=curOrders->ord[i][curOrder];
|
||||||
// find free slot
|
// find free slot
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<256; j++) {
|
||||||
logD("finding free slot in %d...",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];
|
int origOrd=order[i];
|
||||||
order[i]=j;
|
order[i]=j;
|
||||||
DivPattern* oldPat=song.pat[i].getPattern(origOrd,false);
|
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||||
DivPattern* pat=song.pat[i].getPattern(j,true);
|
DivPattern* pat=curPat[i].getPattern(j,true);
|
||||||
memcpy(pat->data,oldPat->data,256*32*sizeof(short));
|
memcpy(pat->data,oldPat->data,256*32*sizeof(short));
|
||||||
logD("found at %d",j);
|
logD("found at %d",j);
|
||||||
didNotFind=false;
|
didNotFind=false;
|
||||||
|
@ -2125,19 +2175,19 @@ void DivEngine::deepCloneOrder(bool where) {
|
||||||
if (where) { // at the end
|
if (where) { // at the end
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<chans; i++) {
|
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();
|
saveLock.unlock();
|
||||||
} else { // after current order
|
} else { // after current order
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
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();
|
saveLock.unlock();
|
||||||
curOrder++;
|
curOrder++;
|
||||||
if (playing && !freelance) {
|
if (playing && !freelance) {
|
||||||
|
@ -2148,17 +2198,17 @@ void DivEngine::deepCloneOrder(bool where) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivEngine::deleteOrder() {
|
void DivEngine::deleteOrder() {
|
||||||
if (song.ordersLen<=1) return;
|
if (curSubSong->ordersLen<=1) return;
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
for (int j=curOrder; j<song.ordersLen; j++) {
|
for (int j=curOrder; j<curSubSong->ordersLen; j++) {
|
||||||
song.orders.ord[i][j]=song.orders.ord[i][j+1];
|
curOrders->ord[i][j]=curOrders->ord[i][j+1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
song.ordersLen--;
|
curSubSong->ordersLen--;
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
if (curOrder>=song.ordersLen) curOrder=song.ordersLen-1;
|
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
|
||||||
if (playing && !freelance) {
|
if (playing && !freelance) {
|
||||||
playSub(false);
|
playSub(false);
|
||||||
}
|
}
|
||||||
|
@ -2173,9 +2223,9 @@ void DivEngine::moveOrderUp() {
|
||||||
}
|
}
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||||
song.orders.ord[i][curOrder-1]^=song.orders.ord[i][curOrder];
|
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
|
||||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||||
}
|
}
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
curOrder--;
|
curOrder--;
|
||||||
|
@ -2187,15 +2237,15 @@ void DivEngine::moveOrderUp() {
|
||||||
|
|
||||||
void DivEngine::moveOrderDown() {
|
void DivEngine::moveOrderDown() {
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
if (curOrder>=song.ordersLen-1) {
|
if (curOrder>=curSubSong->ordersLen-1) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||||
song.orders.ord[i][curOrder+1]^=song.orders.ord[i][curOrder];
|
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
|
||||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||||
}
|
}
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
curOrder++;
|
curOrder++;
|
||||||
|
@ -2208,12 +2258,12 @@ void DivEngine::moveOrderDown() {
|
||||||
void DivEngine::exchangeIns(int one, int two) {
|
void DivEngine::exchangeIns(int one, int two) {
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<256; j++) {
|
||||||
if (song.pat[i].data[j]==NULL) continue;
|
if (curPat[i].data[j]==NULL) continue;
|
||||||
for (int k=0; k<song.patLen; k++) {
|
for (int k=0; k<curSubSong->patLen; k++) {
|
||||||
if (song.pat[i].data[j]->data[k][2]==one) {
|
if (curPat[i].data[j]->data[k][2]==one) {
|
||||||
song.pat[i].data[j]->data[k][2]=two;
|
curPat[i].data[j]->data[k][2]=two;
|
||||||
} else if (song.pat[i].data[j]->data[k][2]==two) {
|
} else if (curPat[i].data[j]->data[k][2]==two) {
|
||||||
song.pat[i].data[j]->data[k][2]=one;
|
curPat[i].data[j]->data[k][2]=one;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2417,7 +2467,7 @@ void DivEngine::autoNoteOffAll() {
|
||||||
void DivEngine::setOrder(unsigned char order) {
|
void DivEngine::setOrder(unsigned char order) {
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
curOrder=order;
|
curOrder=order;
|
||||||
if (order>=song.ordersLen) curOrder=0;
|
if (order>=curSubSong->ordersLen) curOrder=0;
|
||||||
if (playing && !freelance) {
|
if (playing && !freelance) {
|
||||||
playSub(false);
|
playSub(false);
|
||||||
}
|
}
|
||||||
|
@ -2440,15 +2490,15 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
|
||||||
void DivEngine::setSongRate(float hz, bool pal) {
|
void DivEngine::setSongRate(float hz, bool pal) {
|
||||||
BUSY_BEGIN;
|
BUSY_BEGIN;
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
song.pal=!pal;
|
curSubSong->pal=!pal;
|
||||||
song.hz=hz;
|
curSubSong->hz=hz;
|
||||||
// what?
|
// what?
|
||||||
song.customTempo=true;
|
curSubSong->customTempo=true;
|
||||||
divider=60;
|
divider=60;
|
||||||
if (song.customTempo) {
|
if (curSubSong->customTempo) {
|
||||||
divider=song.hz;
|
divider=curSubSong->hz;
|
||||||
} else {
|
} else {
|
||||||
if (song.pal) {
|
if (curSubSong->pal) {
|
||||||
divider=60;
|
divider=60;
|
||||||
} else {
|
} else {
|
||||||
divider=50;
|
divider=50;
|
||||||
|
@ -2891,5 +2941,6 @@ bool DivEngine::quit() {
|
||||||
if (yrw801ROM!=NULL) delete[] yrw801ROM;
|
if (yrw801ROM!=NULL) delete[] yrw801ROM;
|
||||||
if (tg100ROM!=NULL) delete[] tg100ROM;
|
if (tg100ROM!=NULL) delete[] tg100ROM;
|
||||||
if (mu5ROM!=NULL) delete[] mu5ROM;
|
if (mu5ROM!=NULL) delete[] mu5ROM;
|
||||||
|
song.unload();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@
|
||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev94"
|
#define DIV_VERSION "dev95"
|
||||||
#define DIV_ENGINE_VERSION 94
|
#define DIV_ENGINE_VERSION 95
|
||||||
|
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
|
@ -304,6 +304,7 @@ class DivEngine {
|
||||||
bool hasLoadedSomething;
|
bool hasLoadedSomething;
|
||||||
int softLockCount;
|
int softLockCount;
|
||||||
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
|
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
|
||||||
|
size_t curSubSongIndex;
|
||||||
double divider;
|
double divider;
|
||||||
int cycles;
|
int cycles;
|
||||||
double clockDrift;
|
double clockDrift;
|
||||||
|
@ -411,8 +412,14 @@ class DivEngine {
|
||||||
void swapChannels(int src, int dest);
|
void swapChannels(int src, int dest);
|
||||||
void stompChannel(int ch);
|
void stompChannel(int ch);
|
||||||
|
|
||||||
|
// change song (UNSAFE)
|
||||||
|
void changeSong(size_t songIndex);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DivSong song;
|
DivSong song;
|
||||||
|
DivOrders* curOrders;
|
||||||
|
DivChannelData* curPat;
|
||||||
|
DivSubSong* curSubSong;
|
||||||
DivInstrument* tempIns;
|
DivInstrument* tempIns;
|
||||||
DivSystem sysOfChan[DIV_MAX_CHANS];
|
DivSystem sysOfChan[DIV_MAX_CHANS];
|
||||||
int dispatchOfChan[DIV_MAX_CHANS];
|
int dispatchOfChan[DIV_MAX_CHANS];
|
||||||
|
@ -603,6 +610,9 @@ class DivEngine {
|
||||||
// get current row
|
// get current row
|
||||||
int getRow();
|
int getRow();
|
||||||
|
|
||||||
|
// get current subsong
|
||||||
|
size_t getCurrentSubSong();
|
||||||
|
|
||||||
// get speed 1
|
// get speed 1
|
||||||
unsigned char getSpeed1();
|
unsigned char getSpeed1();
|
||||||
|
|
||||||
|
@ -799,6 +809,15 @@ class DivEngine {
|
||||||
// public swap channels
|
// public swap channels
|
||||||
void swapChannelsP(int src, int dest);
|
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
|
// change system
|
||||||
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
||||||
|
|
||||||
|
@ -900,6 +919,7 @@ class DivEngine {
|
||||||
curOrder(0),
|
curOrder(0),
|
||||||
remainingLoops(-1),
|
remainingLoops(-1),
|
||||||
nextSpeed(3),
|
nextSpeed(3),
|
||||||
|
curSubSongIndex(0),
|
||||||
divider(60),
|
divider(60),
|
||||||
cycles(0),
|
cycles(0),
|
||||||
clockDrift(0),
|
clockDrift(0),
|
||||||
|
@ -935,6 +955,8 @@ class DivEngine {
|
||||||
metroAmp(0.0f),
|
metroAmp(0.0f),
|
||||||
metroVol(1.0f),
|
metroVol(1.0f),
|
||||||
totalProcessed(0),
|
totalProcessed(0),
|
||||||
|
curOrders(NULL),
|
||||||
|
curPat(NULL),
|
||||||
tempIns(NULL),
|
tempIns(NULL),
|
||||||
oscBuf{NULL,NULL},
|
oscBuf{NULL,NULL},
|
||||||
oscSize(1),
|
oscSize(1),
|
||||||
|
@ -959,6 +981,8 @@ class DivEngine {
|
||||||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeSong(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -184,57 +184,57 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
logI("reading module data...");
|
logI("reading module data...");
|
||||||
if (ds.version>0x0c) {
|
if (ds.version>0x0c) {
|
||||||
ds.hilightA=reader.readC();
|
ds.subsong[0]->hilightA=reader.readC();
|
||||||
ds.hilightB=reader.readC();
|
ds.subsong[0]->hilightB=reader.readC();
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.timeBase=reader.readC();
|
ds.subsong[0]->timeBase=reader.readC();
|
||||||
ds.speed1=reader.readC();
|
ds.subsong[0]->speed1=reader.readC();
|
||||||
if (ds.version>0x07) {
|
if (ds.version>0x07) {
|
||||||
ds.speed2=reader.readC();
|
ds.subsong[0]->speed2=reader.readC();
|
||||||
ds.pal=reader.readC();
|
ds.subsong[0]->pal=reader.readC();
|
||||||
ds.hz=(ds.pal)?60:50;
|
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||||
ds.customTempo=reader.readC();
|
ds.subsong[0]->customTempo=reader.readC();
|
||||||
} else {
|
} else {
|
||||||
ds.speed2=ds.speed1;
|
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
||||||
}
|
}
|
||||||
if (ds.version>0x0a) {
|
if (ds.version>0x0a) {
|
||||||
String hz=reader.readString(3);
|
String hz=reader.readString(3);
|
||||||
if (ds.customTempo) {
|
if (ds.subsong[0]->customTempo) {
|
||||||
try {
|
try {
|
||||||
ds.hz=std::stoi(hz);
|
ds.subsong[0]->hz=std::stoi(hz);
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
logW("invalid custom Hz!");
|
logW("invalid custom Hz!");
|
||||||
ds.hz=60;
|
ds.subsong[0]->hz=60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ds.version>0x17) {
|
if (ds.version>0x17) {
|
||||||
ds.patLen=reader.readI();
|
ds.subsong[0]->patLen=reader.readI();
|
||||||
} else {
|
} 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!");
|
logE("pattern length is negative!");
|
||||||
lastError="pattern lengrh is negative!";
|
lastError="pattern lengrh is negative!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.patLen>256) {
|
if (ds.subsong[0]->patLen>256) {
|
||||||
logE("pattern length is too large!");
|
logE("pattern length is too large!");
|
||||||
lastError="pattern length is too large!";
|
lastError="pattern length is too large!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.ordersLen<0) {
|
if (ds.subsong[0]->ordersLen<0) {
|
||||||
logE("song length is negative!");
|
logE("song length is negative!");
|
||||||
lastError="song length is negative!";
|
lastError="song length is negative!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.ordersLen>127) {
|
if (ds.subsong[0]->ordersLen>127) {
|
||||||
logE("song is too long!");
|
logE("song is too long!");
|
||||||
lastError="song is too long!";
|
lastError="song is too long!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
@ -242,54 +242,54 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.version<20 && ds.version>3) {
|
if (ds.version<20 && ds.version>3) {
|
||||||
ds.arpLen=reader.readC();
|
ds.subsong[0]->arpLen=reader.readC();
|
||||||
} else {
|
} else {
|
||||||
ds.arpLen=1;
|
ds.subsong[0]->arpLen=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.system[0]==DIV_SYSTEM_YMU759) {
|
if (ds.system[0]==DIV_SYSTEM_YMU759) {
|
||||||
switch (ds.timeBase) {
|
switch (ds.subsong[0]->timeBase) {
|
||||||
case 0:
|
case 0:
|
||||||
ds.hz=248;
|
ds.subsong[0]->hz=248;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ds.hz=200;
|
ds.subsong[0]->hz=200;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ds.hz=100;
|
ds.subsong[0]->hz=100;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
ds.hz=50;
|
ds.subsong[0]->hz=50;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
ds.hz=25;
|
ds.subsong[0]->hz=25;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
ds.hz=20;
|
ds.subsong[0]->hz=20;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ds.hz=248;
|
ds.subsong[0]->hz=248;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ds.customTempo=true;
|
ds.subsong[0]->customTempo=true;
|
||||||
ds.timeBase=0;
|
ds.subsong[0]->timeBase=0;
|
||||||
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
|
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
|
||||||
}
|
}
|
||||||
|
|
||||||
logV("%x",reader.tell());
|
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 i=0; i<getChannelCount(ds.system[0]); i++) {
|
||||||
for (int j=0; j<ds.ordersLen; j++) {
|
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||||
ds.orders.ord[i][j]=reader.readC();
|
ds.subsong[0]->orders.ord[i][j]=reader.readC();
|
||||||
if (ds.orders.ord[i][j]>0x7f) {
|
if (ds.subsong[0]->orders.ord[i][j]>0x7f) {
|
||||||
logE("order at %d, %d out of range! (%d)",i,j,ds.orders.ord[i][j]);
|
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.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;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.version>0x18) { // 1.1 pattern names
|
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) {
|
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());
|
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++) {
|
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) {
|
if (ds.version<0x0a) {
|
||||||
chan.effectCols=1;
|
chan.effectCols=1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -652,10 +652,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int j=0; j<ds.ordersLen; j++) {
|
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||||
DivPattern* pat=chan.getPattern(ds.orders.ord[i][j],true);
|
DivPattern* pat=chan.getPattern(ds.subsong[0]->orders.ord[i][j],true);
|
||||||
if (ds.version>0x08) { // current pattern format
|
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
|
// note
|
||||||
pat->data[k][0]=reader.readS();
|
pat->data[k][0]=reader.readS();
|
||||||
// octave
|
// octave
|
||||||
|
@ -723,7 +723,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
} else { // historic pattern format
|
} else { // historic pattern format
|
||||||
if (i<16) pat->data[0][2]=historicColIns[i];
|
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
|
// note
|
||||||
pat->data[k][0]=reader.readC();
|
pat->data[k][0]=reader.readC();
|
||||||
// octave
|
// octave
|
||||||
|
@ -905,6 +905,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
song.unload();
|
song.unload();
|
||||||
song=ds;
|
song=ds;
|
||||||
|
changeSong(0);
|
||||||
recalcChans();
|
recalcChans();
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
|
@ -929,13 +930,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
unsigned int insPtr[256];
|
unsigned int insPtr[256];
|
||||||
unsigned int wavePtr[256];
|
unsigned int wavePtr[256];
|
||||||
unsigned int samplePtr[256];
|
unsigned int samplePtr[256];
|
||||||
|
unsigned int subSongPtr[256];
|
||||||
std::vector<int> patPtr;
|
std::vector<int> patPtr;
|
||||||
|
int numberOfSubSongs=0;
|
||||||
char magic[5];
|
char magic[5];
|
||||||
memset(magic,0,5);
|
memset(magic,0,5);
|
||||||
SafeReader reader=SafeReader(file,len);
|
SafeReader reader=SafeReader(file,len);
|
||||||
warnings="";
|
warnings="";
|
||||||
try {
|
try {
|
||||||
DivSong ds;
|
DivSong ds;
|
||||||
|
DivSubSong* subSong=ds.subsong[0];
|
||||||
|
|
||||||
if (!reader.seek(16,SEEK_SET)) {
|
if (!reader.seek(16,SEEK_SET)) {
|
||||||
logE("premature end of file!");
|
logE("premature end of file!");
|
||||||
|
@ -1045,44 +1049,44 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
reader.readI();
|
reader.readI();
|
||||||
|
|
||||||
ds.timeBase=reader.readC();
|
subSong->timeBase=reader.readC();
|
||||||
ds.speed1=reader.readC();
|
subSong->speed1=reader.readC();
|
||||||
ds.speed2=reader.readC();
|
subSong->speed2=reader.readC();
|
||||||
ds.arpLen=reader.readC();
|
subSong->arpLen=reader.readC();
|
||||||
ds.hz=reader.readF();
|
subSong->hz=reader.readF();
|
||||||
ds.pal=(ds.hz>=53);
|
subSong->pal=(subSong->hz>=53);
|
||||||
ds.customTempo=true;
|
subSong->customTempo=true;
|
||||||
|
|
||||||
ds.patLen=reader.readS();
|
subSong->patLen=reader.readS();
|
||||||
ds.ordersLen=reader.readS();
|
subSong->ordersLen=reader.readS();
|
||||||
|
|
||||||
ds.hilightA=reader.readC();
|
subSong->hilightA=reader.readC();
|
||||||
ds.hilightB=reader.readC();
|
subSong->hilightB=reader.readC();
|
||||||
|
|
||||||
ds.insLen=reader.readS();
|
ds.insLen=reader.readS();
|
||||||
ds.waveLen=reader.readS();
|
ds.waveLen=reader.readS();
|
||||||
ds.sampleLen=reader.readS();
|
ds.sampleLen=reader.readS();
|
||||||
int numberOfPats=reader.readI();
|
int numberOfPats=reader.readI();
|
||||||
|
|
||||||
if (ds.patLen<0) {
|
if (subSong->patLen<0) {
|
||||||
logE("pattern length is negative!");
|
logE("pattern length is negative!");
|
||||||
lastError="pattern lengrh is negative!";
|
lastError="pattern lengrh is negative!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.patLen>256) {
|
if (subSong->patLen>256) {
|
||||||
logE("pattern length is too large!");
|
logE("pattern length is too large!");
|
||||||
lastError="pattern length is too large!";
|
lastError="pattern length is too large!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.ordersLen<0) {
|
if (subSong->ordersLen<0) {
|
||||||
logE("song length is negative!");
|
logE("song length is negative!");
|
||||||
lastError="song length is negative!";
|
lastError="song length is negative!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ds.ordersLen>256) {
|
if (subSong->ordersLen>256) {
|
||||||
logE("song is too long!");
|
logE("song is too long!");
|
||||||
lastError="song is too long!";
|
lastError="song is too long!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
@ -1295,18 +1299,18 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
reader.read(samplePtr,ds.sampleLen*4);
|
reader.read(samplePtr,ds.sampleLen*4);
|
||||||
for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
|
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 i=0; i<tchans; i++) {
|
||||||
for (int j=0; j<ds.ordersLen; j++) {
|
for (int j=0; j<subSong->ordersLen; j++) {
|
||||||
ds.orders.ord[i][j]=reader.readC();
|
subSong->orders.ord[i][j]=reader.readC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
ds.pat[i].effectCols=reader.readC();
|
subSong->pat[i].effectCols=reader.readC();
|
||||||
if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) {
|
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>8) {
|
||||||
logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols);
|
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,ds.pat[i].effectCols);
|
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1314,25 +1318,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
if (ds.version>=39) {
|
if (ds.version>=39) {
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
ds.chanShow[i]=reader.readC();
|
subSong->chanShow[i]=reader.readC();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
ds.chanCollapse[i]=reader.readC();
|
subSong->chanCollapse[i]=reader.readC();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds.version<92) {
|
if (ds.version<92) {
|
||||||
for (int i=0; i<tchans; i++) {
|
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++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
ds.chanName[i]=reader.readString();
|
subSong->chanName[i]=reader.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
ds.chanShortName[i]=reader.readString();
|
subSong->chanShortName[i]=reader.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.notes=reader.readString();
|
ds.notes=reader.readString();
|
||||||
|
@ -1405,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
|
// read instruments
|
||||||
for (int i=0; i<ds.insLen; i++) {
|
for (int i=0; i<ds.insLen; i++) {
|
||||||
DivInstrument* ins=new DivInstrument;
|
DivInstrument* ins=new DivInstrument;
|
||||||
|
@ -1568,9 +1654,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
int chan=reader.readS();
|
int chan=reader.readS();
|
||||||
int index=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) {
|
if (chan<0 || chan>=tchans) {
|
||||||
logE("pattern channel out of range!",i);
|
logE("pattern channel out of range!",i);
|
||||||
|
@ -1586,14 +1678,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
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);
|
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
|
||||||
for (int j=0; j<ds.patLen; j++) {
|
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
|
||||||
pat->data[j][0]=reader.readS();
|
pat->data[j][0]=reader.readS();
|
||||||
pat->data[j][1]=reader.readS();
|
pat->data[j][1]=reader.readS();
|
||||||
pat->data[j][2]=reader.readS();
|
pat->data[j][2]=reader.readS();
|
||||||
pat->data[j][3]=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][4+(k<<1)]=reader.readS();
|
||||||
pat->data[j][5+(k<<1)]=reader.readS();
|
pat->data[j][5+(k<<1)]=reader.readS();
|
||||||
}
|
}
|
||||||
|
@ -1615,6 +1714,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
song.unload();
|
song.unload();
|
||||||
song=ds;
|
song=ds;
|
||||||
|
changeSong(0);
|
||||||
recalcChans();
|
recalcChans();
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
|
@ -1740,8 +1840,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
ds.sampleLen=ds.sample.size();
|
ds.sampleLen=ds.sample.size();
|
||||||
|
|
||||||
// orders
|
// orders
|
||||||
ds.ordersLen=ordCount=reader.readC();
|
ds.subsong[0]->ordersLen=ordCount=reader.readC();
|
||||||
if (ds.ordersLen<1 || ds.ordersLen>127) {
|
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) {
|
||||||
logD("invalid order count!");
|
logD("invalid order count!");
|
||||||
throw EndOfFileException(&reader,reader.tell());
|
throw EndOfFileException(&reader,reader.tell());
|
||||||
}
|
}
|
||||||
|
@ -1761,7 +1861,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
unsigned char pat=reader.readC();
|
unsigned char pat=reader.readC();
|
||||||
if (pat>patMax) patMax=pat;
|
if (pat>patMax) patMax=pat;
|
||||||
for (int j=0; j<chCount; j++) {
|
for (int j=0; j<chCount; j++) {
|
||||||
ds.orders.ord[j][i]=pat;
|
ds.subsong[0]->orders.ord[j][i]=pat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,7 +1878,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// patterns
|
// patterns
|
||||||
ds.patLen=64;
|
ds.subsong[0]->patLen=64;
|
||||||
for (int ch=0; ch<chCount; ch++) {
|
for (int ch=0; ch<chCount; ch++) {
|
||||||
for (int i=0; i<5; i++) {
|
for (int i=0; i<5; i++) {
|
||||||
fxUsage[ch][i]=false;
|
fxUsage[ch][i]=false;
|
||||||
|
@ -1787,7 +1887,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
for (int pat=0; pat<=patMax; pat++) {
|
for (int pat=0; pat<=patMax; pat++) {
|
||||||
DivPattern* chpats[DIV_MAX_CHANS];
|
DivPattern* chpats[DIV_MAX_CHANS];
|
||||||
for (int ch=0; ch<chCount; ch++) {
|
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 row=0; row<64; row++) {
|
||||||
for (int ch=0; ch<chCount; ch++) {
|
for (int ch=0; ch<chCount; ch++) {
|
||||||
|
@ -1860,7 +1960,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
for (int ch=0; ch<=chCount; ch++) {
|
for (int ch=0; ch<=chCount; ch++) {
|
||||||
unsigned char fxCols=1;
|
unsigned char fxCols=1;
|
||||||
for (int pat=0; pat<=patMax; pat++) {
|
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 lastPitchEffect=-1;
|
||||||
short lastEffectState[5]={-1,-1,-1,-1,-1};
|
short lastEffectState[5]={-1,-1,-1,-1,-1};
|
||||||
short setEffectState[5]={-1,-1,-1,-1,-1};
|
short setEffectState[5]={-1,-1,-1,-1,-1};
|
||||||
|
@ -1988,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.subsong[0]->pal=false;
|
||||||
ds.hz=50;
|
ds.subsong[0]->hz=50;
|
||||||
ds.customTempo=false;
|
ds.subsong[0]->customTempo=false;
|
||||||
ds.systemLen=(chCount+3)/4;
|
ds.systemLen=(chCount+3)/4;
|
||||||
for(int i=0; i<ds.systemLen; i++) {
|
for(int i=0; i<ds.systemLen; i++) {
|
||||||
ds.system[i]=DIV_SYSTEM_AMIGA;
|
ds.system[i]=DIV_SYSTEM_AMIGA;
|
||||||
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
||||||
}
|
}
|
||||||
for(int i=0; i<chCount; i++) {
|
for(int i=0; i<chCount; i++) {
|
||||||
ds.chanShow[i]=true;
|
ds.subsong[0]->chanShow[i]=true;
|
||||||
ds.chanName[i]=fmt::sprintf("Channel %d",i+1);
|
ds.subsong[0]->chanName[i]=fmt::sprintf("Channel %d",i+1);
|
||||||
ds.chanShortName[i]=fmt::sprintf("C%d",i+1);
|
ds.subsong[0]->chanShortName[i]=fmt::sprintf("C%d",i+1);
|
||||||
}
|
}
|
||||||
for(int i=chCount; i<ds.systemLen*4; i++) {
|
for(int i=chCount; i<ds.systemLen*4; i++) {
|
||||||
ds.pat[i].effectCols=1;
|
ds.subsong[0]->pat[i].effectCols=1;
|
||||||
ds.chanShow[i]=false;
|
ds.subsong[0]->chanShow[i]=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// instrument creation
|
// instrument creation
|
||||||
|
@ -2024,6 +2124,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
song.unload();
|
song.unload();
|
||||||
song=ds;
|
song=ds;
|
||||||
|
changeSong(0);
|
||||||
recalcChans();
|
recalcChans();
|
||||||
saveLock.unlock();
|
saveLock.unlock();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
|
@ -2118,8 +2219,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
||||||
newVibrato=reader.readI();
|
newVibrato=reader.readI();
|
||||||
}
|
}
|
||||||
if (blockVersion>=4) {
|
if (blockVersion>=4) {
|
||||||
ds.hilightA=reader.readI();
|
ds.subsong[0]->hilightA=reader.readI();
|
||||||
ds.hilightB=reader.readI();
|
ds.subsong[0]->hilightB=reader.readI();
|
||||||
}
|
}
|
||||||
if (expansions&8) if (blockVersion>=5) { // N163 channels
|
if (expansions&8) if (blockVersion>=5) { // N163 channels
|
||||||
n163Chans=reader.readI();
|
n163Chans=reader.readI();
|
||||||
|
@ -2135,12 +2236,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
||||||
logV("custom Hz: %d",customHz);
|
logV("custom Hz: %d",customHz);
|
||||||
logV("new vibrato: %d",newVibrato);
|
logV("new vibrato: %d",newVibrato);
|
||||||
logV("N163 channels: %d",n163Chans);
|
logV("N163 channels: %d",n163Chans);
|
||||||
logV("highlight 1: %d",ds.hilightA);
|
logV("highlight 1: %d",ds.subsong[0]->hilightA);
|
||||||
logV("highlight 2: %d",ds.hilightB);
|
logV("highlight 2: %d",ds.subsong[0]->hilightB);
|
||||||
logV("split point: %d",speedSplitPoint);
|
logV("split point: %d",speedSplitPoint);
|
||||||
|
|
||||||
if (customHz!=0) {
|
if (customHz!=0) {
|
||||||
ds.hz=customHz;
|
ds.subsong[0]->hz=customHz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize channels
|
// initialize channels
|
||||||
|
@ -2201,7 +2302,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
||||||
for (int j=0; j<=totalSongs; j++) {
|
for (int j=0; j<=totalSongs; j++) {
|
||||||
unsigned char effectCols=reader.readC();
|
unsigned char effectCols=reader.readC();
|
||||||
if (j==0) {
|
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);
|
logV("- song %d has %d effect columns",j,effectCols);
|
||||||
}
|
}
|
||||||
|
@ -2532,26 +2633,39 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
return false;
|
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) {
|
SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
saveLock.lock();
|
saveLock.lock();
|
||||||
|
std::vector<int> subSongPtr;
|
||||||
std::vector<int> insPtr;
|
std::vector<int> insPtr;
|
||||||
std::vector<int> wavePtr;
|
std::vector<int> wavePtr;
|
||||||
std::vector<int> samplePtr;
|
std::vector<int> samplePtr;
|
||||||
std::vector<int> patPtr;
|
std::vector<int> patPtr;
|
||||||
size_t ptrSeek;
|
size_t ptrSeek, subSongPtrSeek;
|
||||||
|
size_t subSongIndex=0;
|
||||||
|
DivSubSong* subSong=song.subsong[subSongIndex];
|
||||||
warnings="";
|
warnings="";
|
||||||
|
|
||||||
// fail if values are out of range
|
// fail if values are out of range
|
||||||
if (song.ordersLen>256) {
|
/*
|
||||||
|
if (subSong->ordersLen>256) {
|
||||||
logE("maximum song length is 256!");
|
logE("maximum song length is 256!");
|
||||||
lastError="maximum song length is 256";
|
lastError="maximum song length is 256";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (song.patLen>256) {
|
if (subSong->patLen>256) {
|
||||||
logE("maximum pattern length is 256!");
|
logE("maximum pattern length is 256!");
|
||||||
lastError="maximum pattern length is 256";
|
lastError="maximum pattern length is 256";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
if (song.ins.size()>256) {
|
if (song.ins.size()>256) {
|
||||||
logE("maximum number of instruments is 256!");
|
logE("maximum number of instruments is 256!");
|
||||||
lastError="maximum number of instruments is 256";
|
lastError="maximum number of instruments is 256";
|
||||||
|
@ -2594,14 +2708,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
|
|
||||||
// high short is channel
|
// high short is channel
|
||||||
// low short is pattern number
|
// low short is pattern number
|
||||||
std::vector<int> patsToWrite;
|
std::vector<PatToWrite> patsToWrite;
|
||||||
bool alreadyAdded[256];
|
bool alreadyAdded[256];
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
memset(alreadyAdded,0,256*sizeof(bool));
|
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||||
for (int j=0; j<song.ordersLen; j++) {
|
DivSubSong* subs=song.subsong[j];
|
||||||
if (alreadyAdded[song.orders.ord[i][j]]) continue;
|
memset(alreadyAdded,0,256*sizeof(bool));
|
||||||
patsToWrite.push_back((i<<16)|song.orders.ord[i][j]);
|
for (int k=0; k<subs->ordersLen; k++) {
|
||||||
alreadyAdded[song.orders.ord[i][j]]=true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2609,15 +2726,15 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->write("INFO",4);
|
w->write("INFO",4);
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
||||||
w->writeC(song.timeBase);
|
w->writeC(subSong->timeBase);
|
||||||
w->writeC(song.speed1);
|
w->writeC(subSong->speed1);
|
||||||
w->writeC(song.speed2);
|
w->writeC(subSong->speed2);
|
||||||
w->writeC(song.arpLen);
|
w->writeC(subSong->arpLen);
|
||||||
w->writeF(song.hz);
|
w->writeF(subSong->hz);
|
||||||
w->writeS(song.patLen);
|
w->writeS(subSong->patLen);
|
||||||
w->writeS(song.ordersLen);
|
w->writeS(subSong->ordersLen);
|
||||||
w->writeC(song.hilightA);
|
w->writeC(subSong->hilightA);
|
||||||
w->writeC(song.hilightB);
|
w->writeC(subSong->hilightB);
|
||||||
w->writeS(song.insLen);
|
w->writeS(song.insLen);
|
||||||
w->writeS(song.waveLen);
|
w->writeS(song.waveLen);
|
||||||
w->writeS(song.sampleLen);
|
w->writeS(song.sampleLen);
|
||||||
|
@ -2694,29 +2811,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=0; j<song.ordersLen; j++) {
|
for (int j=0; j<subSong->ordersLen; j++) {
|
||||||
w->writeC(song.orders.ord[i][j]);
|
w->writeC(subSong->orders.ord[i][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<chans; i++) {
|
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++) {
|
for (int i=0; i<chans; i++) {
|
||||||
w->writeC(song.chanShow[i]);
|
w->writeC(subSong->chanShow[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<chans; 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++) {
|
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++) {
|
for (int i=0; i<chans; i++) {
|
||||||
w->writeString(song.chanShortName[i],false);
|
w->writeString(subSong->chanShortName[i],false);
|
||||||
}
|
}
|
||||||
|
|
||||||
w->writeString(song.notes,false);
|
w->writeString(song.notes,false);
|
||||||
|
@ -2741,6 +2858,67 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
for (int i=0; i<18; i++) {
|
for (int i=0; i<18; i++) {
|
||||||
w->writeC(0);
|
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
|
/// INSTRUMENT
|
||||||
for (int i=0; i<song.insLen; i++) {
|
for (int i=0; i<song.insLen; i++) {
|
||||||
|
@ -2776,23 +2954,24 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PATTERN
|
/// PATTERN
|
||||||
for (int i: patsToWrite) {
|
for (PatToWrite& i: patsToWrite) {
|
||||||
DivPattern* pat=song.pat[i>>16].getPattern(i&0xffff,false);
|
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
|
||||||
patPtr.push_back(w->tell());
|
patPtr.push_back(w->tell());
|
||||||
w->write("PATR",4);
|
w->write("PATR",4);
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
||||||
w->writeS(i>>16);
|
w->writeS(i.chan);
|
||||||
w->writeS(i&0xffff);
|
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][0]); // note
|
||||||
w->writeS(pat->data[j][1]); // octave
|
w->writeS(pat->data[j][1]); // octave
|
||||||
w->writeS(pat->data[j][2]); // instrument
|
w->writeS(pat->data[j][2]); // instrument
|
||||||
w->writeS(pat->data[j][3]); // volume
|
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);
|
w->writeString(pat->name,false);
|
||||||
|
@ -2805,21 +2984,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeI(insPtr[i]);
|
w->writeI(insPtr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wavetable pointers (we'll seek here later)
|
// wavetable pointers
|
||||||
for (int i=0; i<song.waveLen; i++) {
|
for (int i=0; i<song.waveLen; i++) {
|
||||||
w->writeI(wavePtr[i]);
|
w->writeI(wavePtr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sample pointers (we'll seek here later)
|
// sample pointers
|
||||||
for (int i=0; i<song.sampleLen; i++) {
|
for (int i=0; i<song.sampleLen; i++) {
|
||||||
w->writeI(samplePtr[i]);
|
w->writeI(samplePtr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pattern pointers (we'll seek here later)
|
// pattern pointers
|
||||||
for (int i: patPtr) {
|
for (int i: patPtr) {
|
||||||
w->writeI(i);
|
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();
|
saveLock.unlock();
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
@ -2890,7 +3077,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// fail if values are out of range
|
// fail if values are out of range
|
||||||
if (song.ordersLen>127) {
|
if (curSubSong->ordersLen>127) {
|
||||||
logE("maximum .dmf song length is 127!");
|
logE("maximum .dmf song length is 127!");
|
||||||
lastError="maximum .dmf song length is 127";
|
lastError="maximum .dmf song length is 127";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2906,10 +3093,10 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=0; j<song.ordersLen; j++) {
|
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||||
if (song.orders.ord[i][j]>0x7f) {
|
if (curOrders->ord[i][j]>0x7f) {
|
||||||
logE("order %d, %d is out of range (0-127)!",song.orders.ord[i][j]);
|
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)",song.orders.ord[i][j]);
|
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",curOrders->ord[i][j]);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2952,31 +3139,35 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
// song info
|
// song info
|
||||||
w->writeString(song.name,true);
|
w->writeString(song.name,true);
|
||||||
w->writeString(song.author,true);
|
w->writeString(song.author,true);
|
||||||
w->writeC(song.hilightA);
|
w->writeC(curSubSong->hilightA);
|
||||||
w->writeC(song.hilightB);
|
w->writeC(curSubSong->hilightB);
|
||||||
|
|
||||||
w->writeC(song.timeBase);
|
w->writeC(curSubSong->timeBase);
|
||||||
w->writeC(song.speed1);
|
w->writeC(curSubSong->speed1);
|
||||||
w->writeC(song.speed2);
|
w->writeC(curSubSong->speed2);
|
||||||
w->writeC(song.pal);
|
w->writeC(curSubSong->pal);
|
||||||
w->writeC(song.customTempo);
|
w->writeC(curSubSong->customTempo);
|
||||||
char customHz[4];
|
char customHz[4];
|
||||||
memset(customHz,0,4);
|
memset(customHz,0,4);
|
||||||
snprintf(customHz,4,"%d",(int)song.hz);
|
snprintf(customHz,4,"%d",(int)curSubSong->hz);
|
||||||
w->write(customHz,3);
|
w->write(customHz,3);
|
||||||
w->writeI(song.patLen);
|
w->writeI(curSubSong->patLen);
|
||||||
w->writeC(song.ordersLen);
|
w->writeC(curSubSong->ordersLen);
|
||||||
|
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (int j=0; j<song.ordersLen; j++) {
|
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||||
w->writeC(song.orders.ord[i][j]);
|
w->writeC(curOrders->ord[i][j]);
|
||||||
if (version>=25) {
|
if (version>=25) {
|
||||||
DivPattern* pat=song.pat[i].getPattern(j,false);
|
DivPattern* pat=curPat[i].getPattern(j,false);
|
||||||
w->writeString(pat->name,true);
|
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) {
|
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||||
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
||||||
addWarning("duty precision will be lost");
|
addWarning("duty precision will be lost");
|
||||||
|
@ -3138,15 +3329,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<getChannelCount(sys); i++) {
|
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++) {
|
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false);
|
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
|
||||||
for (int k=0; k<song.patLen; k++) {
|
for (int k=0; k<curSubSong->patLen; k++) {
|
||||||
w->writeS(pat->data[k][0]); // note
|
w->writeS(pat->data[k][0]); // note
|
||||||
w->writeS(pat->data[k][1]); // octave
|
w->writeS(pat->data[k][1]); // octave
|
||||||
w->writeS(pat->data[k][3]); // volume
|
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
|
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;
|
dest->name=name;
|
||||||
memcpy(dest->data,data,sizeof(data));
|
memcpy(dest->data,data,sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||||
void DivEngine::nextOrder() {
|
void DivEngine::nextOrder() {
|
||||||
curRow=0;
|
curRow=0;
|
||||||
if (repeatPattern) return;
|
if (repeatPattern) return;
|
||||||
if (++curOrder>=song.ordersLen) {
|
if (++curOrder>=curSubSong->ordersLen) {
|
||||||
endOfSong=true;
|
endOfSong=true;
|
||||||
curOrder=0;
|
curOrder=0;
|
||||||
}
|
}
|
||||||
|
@ -266,11 +266,11 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
||||||
void DivEngine::processRow(int i, bool afterDelay) {
|
void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
int whatOrder=afterDelay?chan[i].delayOrder:curOrder;
|
int whatOrder=afterDelay?chan[i].delayOrder:curOrder;
|
||||||
int whatRow=afterDelay?chan[i].delayRow:curRow;
|
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
|
// pre effects
|
||||||
if (!afterDelay) {
|
if (!afterDelay) {
|
||||||
bool returnAfterPre=false;
|
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 effect=pat->data[whatRow][4+(j<<1)];
|
||||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x0d: // next order
|
case 0x0d: // next order
|
||||||
if (changeOrd<0 && (curOrder<(song.ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
||||||
changeOrd=-2;
|
changeOrd=-2;
|
||||||
changePos=effectVal;
|
changePos=effectVal;
|
||||||
}
|
}
|
||||||
|
@ -405,7 +405,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
bool panChanged=false;
|
bool panChanged=false;
|
||||||
|
|
||||||
// effects
|
// 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 effect=pat->data[whatRow][4+(j<<1)];
|
||||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||||
|
|
||||||
|
@ -548,7 +548,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
break;
|
break;
|
||||||
case 0xe0: // arp speed
|
case 0xe0: // arp speed
|
||||||
if (effectVal>0) {
|
if (effectVal>0) {
|
||||||
song.arpLen=effectVal;
|
curSubSong->arpLen=effectVal;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xe1: // portamento up
|
case 0xe1: // portamento up
|
||||||
|
@ -727,7 +727,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
chan[i].noteOnInhibit=false;
|
chan[i].noteOnInhibit=false;
|
||||||
|
|
||||||
// post effects
|
// 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 effect=pat->data[whatRow][4+(j<<1)];
|
||||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||||
|
|
||||||
|
@ -745,10 +745,10 @@ void DivEngine::nextRow() {
|
||||||
strcpy(pb1,"");
|
strcpy(pb1,"");
|
||||||
strcpy(pb3,"");
|
strcpy(pb3,"");
|
||||||
for (int i=0; i<chans; i++) {
|
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);
|
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",
|
snprintf(pb2,4095,"\x1b[37m %s",
|
||||||
formatNote(pat->data[curRow][0],pat->data[curRow][1]));
|
formatNote(pat->data[curRow][0],pat->data[curRow][1]));
|
||||||
strcat(pb3,pb2);
|
strcat(pb3,pb2);
|
||||||
|
@ -764,7 +764,7 @@ void DivEngine::nextRow() {
|
||||||
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
|
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
|
||||||
strcat(pb3,pb2);
|
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) {
|
if (pat->data[curRow][4+(j<<1)]==-1) {
|
||||||
strcat(pb3,"\x1b[m--");
|
strcat(pb3,"\x1b[m--");
|
||||||
} else {
|
} else {
|
||||||
|
@ -796,32 +796,32 @@ void DivEngine::nextRow() {
|
||||||
if (changeOrd==-2) changeOrd=curOrder+1;
|
if (changeOrd==-2) changeOrd=curOrder+1;
|
||||||
if (changeOrd<=curOrder) endOfSong=true;
|
if (changeOrd<=curOrder) endOfSong=true;
|
||||||
curOrder=changeOrd;
|
curOrder=changeOrd;
|
||||||
if (curOrder>=song.ordersLen) {
|
if (curOrder>=curSubSong->ordersLen) {
|
||||||
curOrder=0;
|
curOrder=0;
|
||||||
endOfSong=true;
|
endOfSong=true;
|
||||||
}
|
}
|
||||||
changeOrd=-1;
|
changeOrd=-1;
|
||||||
}
|
}
|
||||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||||
} else if (playing) if (++curRow>=song.patLen) {
|
} else if (playing) if (++curRow>=curSubSong->patLen) {
|
||||||
nextOrder();
|
nextOrder();
|
||||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (song.brokenSpeedSel) {
|
if (song.brokenSpeedSel) {
|
||||||
if ((song.patLen&1) && curOrder&1) {
|
if ((curSubSong->patLen&1) && curOrder&1) {
|
||||||
ticks=((curRow&1)?speed2:speed1)*(song.timeBase+1);
|
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
|
||||||
nextSpeed=(curRow&1)?speed1:speed2;
|
nextSpeed=(curRow&1)?speed1:speed2;
|
||||||
} else {
|
} else {
|
||||||
ticks=((curRow&1)?speed1:speed2)*(song.timeBase+1);
|
ticks=((curRow&1)?speed1:speed2)*(curSubSong->timeBase+1);
|
||||||
nextSpeed=(curRow&1)?speed2:speed1;
|
nextSpeed=(curRow&1)?speed2:speed1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (speedAB) {
|
if (speedAB) {
|
||||||
ticks=speed2*(song.timeBase+1);
|
ticks=speed2*(curSubSong->timeBase+1);
|
||||||
nextSpeed=speed1;
|
nextSpeed=speed1;
|
||||||
} else {
|
} else {
|
||||||
ticks=speed1*(song.timeBase+1);
|
ticks=speed1*(curSubSong->timeBase+1);
|
||||||
nextSpeed=speed2;
|
nextSpeed=speed2;
|
||||||
}
|
}
|
||||||
speedAB=!speedAB;
|
speedAB=!speedAB;
|
||||||
|
@ -829,7 +829,7 @@ void DivEngine::nextRow() {
|
||||||
|
|
||||||
// post row details
|
// post row details
|
||||||
for (int i=0; i<chans; i++) {
|
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]==0 && pat->data[curRow][1]==0)) {
|
||||||
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
|
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
|
||||||
if (!chan[i].legato) {
|
if (!chan[i].legato) {
|
||||||
|
@ -838,7 +838,7 @@ void DivEngine::nextRow() {
|
||||||
if (song.oneTickCut) {
|
if (song.oneTickCut) {
|
||||||
bool doPrepareCut=true;
|
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) {
|
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||||
doPrepareCut=false;
|
doPrepareCut=false;
|
||||||
break;
|
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].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
|
||||||
if (--chan[i].arpTicks<1) {
|
if (--chan[i].arpTicks<1) {
|
||||||
chan[i].arpTicks=song.arpLen;
|
chan[i].arpTicks=curSubSong->arpLen;
|
||||||
chan[i].arpStage++;
|
chan[i].arpStage++;
|
||||||
if (chan[i].arpStage>2) chan[i].arpStage=0;
|
if (chan[i].arpStage>2) chan[i].arpStage=0;
|
||||||
switch (chan[i].arpStage) {
|
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;
|
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) {
|
if (!freelance && stepPlay!=-1 && subticks==1) {
|
||||||
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
||||||
if (realPos>=size) realPos=size-1;
|
if (realPos>=size) realPos=size-1;
|
||||||
if (song.hilightA>0) {
|
if (curSubSong->hilightA>0) {
|
||||||
if ((curRow%song.hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
||||||
}
|
}
|
||||||
if (song.hilightB>0) {
|
if (curSubSong->hilightB>0) {
|
||||||
if ((curRow%song.hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextTick()) {
|
if (nextTick()) {
|
||||||
|
|
|
@ -19,9 +19,7 @@
|
||||||
|
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
|
||||||
DivOrders emptyOrders;
|
void DivSubSong::clearData() {
|
||||||
|
|
||||||
void DivSong::clearSongData() {
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
pat[i].wipePatterns();
|
pat[i].wipePatterns();
|
||||||
}
|
}
|
||||||
|
@ -30,6 +28,15 @@ void DivSong::clearSongData() {
|
||||||
ordersLen=1;
|
ordersLen=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivSong::clearSongData() {
|
||||||
|
for (DivSubSong* i: subsong) {
|
||||||
|
i->clearData();
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
subsong.clear();
|
||||||
|
subsong.push_back(new DivSubSong);
|
||||||
|
}
|
||||||
|
|
||||||
void DivSong::clearInstruments() {
|
void DivSong::clearInstruments() {
|
||||||
for (DivInstrument* i: ins) {
|
for (DivInstrument* i: ins) {
|
||||||
delete i;
|
delete i;
|
||||||
|
@ -73,7 +80,9 @@ void DivSong::unload() {
|
||||||
sample.clear();
|
sample.clear();
|
||||||
sampleLen=0;
|
sampleLen=0;
|
||||||
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (DivSubSong* i: subsong) {
|
||||||
pat[i].wipePatterns();
|
i->clearData();
|
||||||
|
delete i;
|
||||||
}
|
}
|
||||||
|
subsong.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,8 +110,6 @@ enum DivSystem {
|
||||||
DIV_SYSTEM_DUMMY
|
DIV_SYSTEM_DUMMY
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DivOrders emptyOrders;
|
|
||||||
|
|
||||||
struct DivSubSong {
|
struct DivSubSong {
|
||||||
String name, notes;
|
String name, notes;
|
||||||
unsigned char hilightA, hilightB;
|
unsigned char hilightA, hilightB;
|
||||||
|
@ -126,8 +124,23 @@ struct DivSubSong {
|
||||||
|
|
||||||
bool chanShow[DIV_MAX_CHANS];
|
bool chanShow[DIV_MAX_CHANS];
|
||||||
unsigned char chanCollapse[DIV_MAX_CHANS];
|
unsigned char chanCollapse[DIV_MAX_CHANS];
|
||||||
|
String chanName[DIV_MAX_CHANS];
|
||||||
|
String chanShortName[DIV_MAX_CHANS];
|
||||||
|
|
||||||
DivSubSong() {
|
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++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
chanShow[i]=true;
|
chanShow[i]=true;
|
||||||
chanCollapse[i]=0;
|
chanCollapse[i]=0;
|
||||||
|
@ -317,19 +330,10 @@ struct DivSong {
|
||||||
String nameJ, authorJ, categoryJ;
|
String nameJ, authorJ, categoryJ;
|
||||||
|
|
||||||
// other things
|
// other things
|
||||||
String chanName[DIV_MAX_CHANS];
|
|
||||||
String chanShortName[DIV_MAX_CHANS];
|
|
||||||
String notes;
|
String notes;
|
||||||
|
|
||||||
// highlight
|
|
||||||
unsigned char hilightA, hilightB;
|
|
||||||
|
|
||||||
// module details
|
// module details
|
||||||
unsigned char timeBase, speed1, speed2, arpLen;
|
int insLen, waveLen, sampleLen;
|
||||||
bool pal;
|
|
||||||
bool customTempo;
|
|
||||||
float hz;
|
|
||||||
int patLen, ordersLen, insLen, waveLen, sampleLen;
|
|
||||||
float masterVol;
|
float masterVol;
|
||||||
float tuning;
|
float tuning;
|
||||||
|
|
||||||
|
@ -377,16 +381,11 @@ struct DivSong {
|
||||||
bool snDutyReset;
|
bool snDutyReset;
|
||||||
bool pitchMacroIsLinear;
|
bool pitchMacroIsLinear;
|
||||||
|
|
||||||
DivOrders* orders;
|
|
||||||
std::vector<DivInstrument*> ins;
|
std::vector<DivInstrument*> ins;
|
||||||
DivChannelData* pat;
|
|
||||||
std::vector<DivWavetable*> wave;
|
std::vector<DivWavetable*> wave;
|
||||||
std::vector<DivSample*> sample;
|
std::vector<DivSample*> sample;
|
||||||
|
|
||||||
std::vector<DivSubSong> subsongs;
|
std::vector<DivSubSong*> subsong;
|
||||||
|
|
||||||
bool* chanShow;
|
|
||||||
unsigned char* chanCollapse;
|
|
||||||
|
|
||||||
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound;
|
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound;
|
||||||
DivWavetable nullWave;
|
DivWavetable nullWave;
|
||||||
|
@ -435,17 +434,6 @@ struct DivSong {
|
||||||
manInfo(""),
|
manInfo(""),
|
||||||
createdDate(""),
|
createdDate(""),
|
||||||
revisionDate(""),
|
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),
|
insLen(0),
|
||||||
waveLen(0),
|
waveLen(0),
|
||||||
sampleLen(0),
|
sampleLen(0),
|
||||||
|
@ -491,11 +479,7 @@ struct DivSong {
|
||||||
systemPan[i]=0;
|
systemPan[i]=0;
|
||||||
systemFlags[i]=0;
|
systemFlags[i]=0;
|
||||||
}
|
}
|
||||||
subsongs.push_back(DivSubSong());
|
subsong.push_back(new DivSubSong);
|
||||||
chanShow=subsongs[0].chanShow;
|
|
||||||
chanCollapse=subsongs[0].chanCollapse;
|
|
||||||
orders=&subsongs[0].orders;
|
|
||||||
pat=subsongs[0].pat;
|
|
||||||
system[0]=DIV_SYSTEM_YM2612;
|
system[0]=DIV_SYSTEM_YM2612;
|
||||||
system[1]=DIV_SYSTEM_SMS;
|
system[1]=DIV_SYSTEM_SMS;
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) {
|
||||||
|
|
||||||
const char* DivEngine::getChannelName(int chan) {
|
const char* DivEngine::getChannelName(int chan) {
|
||||||
if (chan<0 || chan>chans) return "??";
|
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 "??";
|
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||||
|
|
||||||
const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]];
|
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) {
|
const char* DivEngine::getChannelShortName(int chan) {
|
||||||
if (chan<0 || chan>chans) return "??";
|
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 "??";
|
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||||
|
|
||||||
const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]];
|
const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]];
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#define DETERMINE_FIRST \
|
#define DETERMINE_FIRST \
|
||||||
int firstChannel=0; \
|
int firstChannel=0; \
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) { \
|
for (int i=0; i<e->getTotalChannelCount(); i++) { \
|
||||||
if (e->song.chanShow[i]) { \
|
if (e->curSubSong->chanShow[i]) { \
|
||||||
firstChannel=i; \
|
firstChannel=i; \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
#define DETERMINE_LAST \
|
#define DETERMINE_LAST \
|
||||||
int lastChannel=0; \
|
int lastChannel=0; \
|
||||||
for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \
|
for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \
|
||||||
if (e->song.chanShow[i]) { \
|
if (e->curSubSong->chanShow[i]) { \
|
||||||
lastChannel=i+1; \
|
lastChannel=i+1; \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
|
|
|
@ -38,7 +38,7 @@ void FurnaceGUI::drawChannels() {
|
||||||
ImGui::PushID(i);
|
ImGui::PushID(i);
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Checkbox("##Visible",&e->song.chanShow[i]);
|
ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(i==0);
|
ImGui::BeginDisabled(i==0);
|
||||||
if (ImGui::Button(ICON_FA_CHEVRON_UP)) {
|
if (ImGui::Button(ICON_FA_CHEVRON_UP)) {
|
||||||
|
@ -53,10 +53,10 @@ void FurnaceGUI::drawChannels() {
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]);
|
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i]);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]);
|
ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->curSubSong->chanShortName[i]);
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
|
|
|
@ -32,7 +32,7 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
||||||
selStart.xCoarse=firstChannel;
|
selStart.xCoarse=firstChannel;
|
||||||
selStart.xFine=0;
|
selStart.xFine=0;
|
||||||
selEnd.xCoarse=lastChannel-1;
|
selEnd.xCoarse=lastChannel-1;
|
||||||
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2;
|
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
|
||||||
selStart.y=y;
|
selStart.y=y;
|
||||||
selEnd.y=y;
|
selEnd.y=y;
|
||||||
} else {
|
} else {
|
||||||
|
@ -56,7 +56,7 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
||||||
if (selectingFull) {
|
if (selectingFull) {
|
||||||
DETERMINE_LAST;
|
DETERMINE_LAST;
|
||||||
selEnd.xCoarse=lastChannel-1;
|
selEnd.xCoarse=lastChannel-1;
|
||||||
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2;
|
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
|
||||||
selEnd.y=y;
|
selEnd.y=y;
|
||||||
} else {
|
} else {
|
||||||
selEnd.xCoarse=xCoarse;
|
selEnd.xCoarse=xCoarse;
|
||||||
|
@ -94,21 +94,21 @@ void FurnaceGUI::finishSelection() {
|
||||||
if (selStart.xCoarse<0) selStart.xCoarse=0;
|
if (selStart.xCoarse<0) selStart.xCoarse=0;
|
||||||
if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1;
|
if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1;
|
||||||
if (selStart.y<0) selStart.y=0;
|
if (selStart.y<0) selStart.y=0;
|
||||||
if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1;
|
if (selStart.y>=e->curSubSong->patLen) selStart.y=e->curSubSong->patLen-1;
|
||||||
if (selEnd.xCoarse<0) selEnd.xCoarse=0;
|
if (selEnd.xCoarse<0) selEnd.xCoarse=0;
|
||||||
if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1;
|
if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1;
|
||||||
if (selEnd.y<0) selEnd.y=0;
|
if (selEnd.y<0) selEnd.y=0;
|
||||||
if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1;
|
if (selEnd.y>=e->curSubSong->patLen) selEnd.y=e->curSubSong->patLen-1;
|
||||||
if (cursor.xCoarse<0) cursor.xCoarse=0;
|
if (cursor.xCoarse<0) cursor.xCoarse=0;
|
||||||
if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1;
|
if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1;
|
||||||
if (cursor.y<0) cursor.y=0;
|
if (cursor.y<0) cursor.y=0;
|
||||||
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
|
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||||
|
|
||||||
if (e->song.chanCollapse[selStart.xCoarse]==3) {
|
if (e->curSubSong->chanCollapse[selStart.xCoarse]==3) {
|
||||||
selStart.xFine=0;
|
selStart.xFine=0;
|
||||||
}
|
}
|
||||||
if (e->song.chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->song.chanCollapse[selEnd.xCoarse])) {
|
if (e->curSubSong->chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->curSubSong->chanCollapse[selEnd.xCoarse])) {
|
||||||
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2;
|
selEnd.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->setMidiBaseChan(cursor.xCoarse);
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
|
@ -126,7 +126,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
demandScrollX=true;
|
demandScrollX=true;
|
||||||
if (x>0) {
|
if (x>0) {
|
||||||
for (int i=0; i<x; i++) {
|
for (int i=0; i<x; i++) {
|
||||||
if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?(4-e->song.chanCollapse[cursor.xCoarse]):(3+e->song.pat[cursor.xCoarse].effectCols*2))) {
|
if (++cursor.xFine>=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2))) {
|
||||||
cursor.xFine=0;
|
cursor.xFine=0;
|
||||||
if (++cursor.xCoarse>=lastChannel) {
|
if (++cursor.xCoarse>=lastChannel) {
|
||||||
if (settings.wrapHorizontal!=0 && !select) {
|
if (settings.wrapHorizontal!=0 && !select) {
|
||||||
|
@ -134,10 +134,10 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
if (settings.wrapHorizontal==2) y++;
|
if (settings.wrapHorizontal==2) y++;
|
||||||
} else {
|
} else {
|
||||||
cursor.xCoarse=lastChannel-1;
|
cursor.xCoarse=lastChannel-1;
|
||||||
cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?(3-e->song.chanCollapse[cursor.xCoarse]):(2+e->song.pat[cursor.xCoarse].effectCols*2);
|
cursor.xFine=e->curSubSong->chanCollapse[cursor.xCoarse]?(3-e->curSubSong->chanCollapse[cursor.xCoarse]):(2+e->curPat[cursor.xCoarse].effectCols*2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (!e->song.chanShow[cursor.xCoarse]) {
|
while (!e->curSubSong->chanShow[cursor.xCoarse]) {
|
||||||
cursor.xCoarse++;
|
cursor.xCoarse++;
|
||||||
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||||
}
|
}
|
||||||
|
@ -150,21 +150,21 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
if (--cursor.xCoarse<firstChannel) {
|
if (--cursor.xCoarse<firstChannel) {
|
||||||
if (settings.wrapHorizontal!=0 && !select) {
|
if (settings.wrapHorizontal!=0 && !select) {
|
||||||
cursor.xCoarse=lastChannel-1;
|
cursor.xCoarse=lastChannel-1;
|
||||||
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2;
|
cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
|
||||||
if (settings.wrapHorizontal==2) y--;
|
if (settings.wrapHorizontal==2) y--;
|
||||||
} else {
|
} else {
|
||||||
cursor.xCoarse=firstChannel;
|
cursor.xCoarse=firstChannel;
|
||||||
cursor.xFine=0;
|
cursor.xFine=0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (!e->song.chanShow[cursor.xCoarse]) {
|
while (!e->curSubSong->chanShow[cursor.xCoarse]) {
|
||||||
cursor.xCoarse--;
|
cursor.xCoarse--;
|
||||||
if (cursor.xCoarse<0) break;
|
if (cursor.xCoarse<0) break;
|
||||||
}
|
}
|
||||||
if (e->song.chanCollapse[cursor.xCoarse]) {
|
if (e->curSubSong->chanCollapse[cursor.xCoarse]) {
|
||||||
cursor.xFine=3-e->song.chanCollapse[cursor.xCoarse];
|
cursor.xFine=3-e->curSubSong->chanCollapse[cursor.xCoarse];
|
||||||
} else {
|
} else {
|
||||||
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2;
|
cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,18 +175,18 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
if (y>0) {
|
if (y>0) {
|
||||||
for (int i=0; i<y; i++) {
|
for (int i=0; i<y; i++) {
|
||||||
cursor.y++;
|
cursor.y++;
|
||||||
if (cursor.y>=e->song.patLen) {
|
if (cursor.y>=e->curSubSong->patLen) {
|
||||||
if (settings.wrapVertical!=0 && !select) {
|
if (settings.wrapVertical!=0 && !select) {
|
||||||
cursor.y=0;
|
cursor.y=0;
|
||||||
if (settings.wrapVertical==2) {
|
if (settings.wrapVertical==2) {
|
||||||
if ((!e->isPlaying() || !followPattern) && curOrder<(e->song.ordersLen-1)) {
|
if ((!e->isPlaying() || !followPattern) && curOrder<(e->curSubSong->ordersLen-1)) {
|
||||||
setOrder(curOrder+1);
|
setOrder(curOrder+1);
|
||||||
} else {
|
} else {
|
||||||
cursor.y=e->song.patLen-1;
|
cursor.y=e->curSubSong->patLen-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cursor.y=e->song.patLen-1;
|
cursor.y=e->curSubSong->patLen-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
cursor.y--;
|
cursor.y--;
|
||||||
if (cursor.y<0) {
|
if (cursor.y<0) {
|
||||||
if (settings.wrapVertical!=0 && !select) {
|
if (settings.wrapVertical!=0 && !select) {
|
||||||
cursor.y=e->song.patLen-1;
|
cursor.y=e->curSubSong->patLen-1;
|
||||||
if (settings.wrapVertical==2) {
|
if (settings.wrapVertical==2) {
|
||||||
if ((!e->isPlaying() || !followPattern) && curOrder>0) {
|
if ((!e->isPlaying() || !followPattern) && curOrder>0) {
|
||||||
setOrder(curOrder-1);
|
setOrder(curOrder-1);
|
||||||
|
@ -229,7 +229,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
||||||
do {
|
do {
|
||||||
cursor.xCoarse--;
|
cursor.xCoarse--;
|
||||||
if (cursor.xCoarse<0) break;
|
if (cursor.xCoarse<0) break;
|
||||||
} while (!e->song.chanShow[cursor.xCoarse]);
|
} while (!e->curSubSong->chanShow[cursor.xCoarse]);
|
||||||
if (cursor.xCoarse<firstChannel) {
|
if (cursor.xCoarse<firstChannel) {
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
cursor.xCoarse=lastChannel-1;
|
cursor.xCoarse=lastChannel-1;
|
||||||
|
@ -253,7 +253,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
||||||
do {
|
do {
|
||||||
cursor.xCoarse++;
|
cursor.xCoarse++;
|
||||||
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||||
} while (!e->song.chanShow[cursor.xCoarse]);
|
} while (!e->curSubSong->chanShow[cursor.xCoarse]);
|
||||||
if (cursor.xCoarse>=lastChannel) {
|
if (cursor.xCoarse>=lastChannel) {
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
cursor.xCoarse=firstChannel;
|
cursor.xCoarse=firstChannel;
|
||||||
|
@ -290,14 +290,14 @@ void FurnaceGUI::moveCursorTop(bool select) {
|
||||||
void FurnaceGUI::moveCursorBottom(bool select) {
|
void FurnaceGUI::moveCursorBottom(bool select) {
|
||||||
finishSelection();
|
finishSelection();
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
if (cursor.y==e->song.patLen-1) {
|
if (cursor.y==e->curSubSong->patLen-1) {
|
||||||
DETERMINE_LAST;
|
DETERMINE_LAST;
|
||||||
cursor.xCoarse=lastChannel-1;
|
cursor.xCoarse=lastChannel-1;
|
||||||
if (cursor.xCoarse<0) cursor.xCoarse=0;
|
if (cursor.xCoarse<0) cursor.xCoarse=0;
|
||||||
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2;
|
cursor.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
|
||||||
demandScrollX=true;
|
demandScrollX=true;
|
||||||
} else {
|
} else {
|
||||||
cursor.y=e->song.patLen-1;
|
cursor.y=e->curSubSong->patLen-1;
|
||||||
}
|
}
|
||||||
if (!select) {
|
if (!select) {
|
||||||
selStart=cursor;
|
selStart=cursor;
|
||||||
|
@ -310,7 +310,7 @@ void FurnaceGUI::moveCursorBottom(bool select) {
|
||||||
void FurnaceGUI::editAdvance() {
|
void FurnaceGUI::editAdvance() {
|
||||||
finishSelection();
|
finishSelection();
|
||||||
cursor.y+=editStep;
|
cursor.y+=editStep;
|
||||||
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
|
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||||
selStart=cursor;
|
selStart=cursor;
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
|
|
|
@ -470,7 +470,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
e->unmuteAll();
|
e->unmuteAll();
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_PAT_NEXT_ORDER:
|
case GUI_ACTION_PAT_NEXT_ORDER:
|
||||||
if (curOrder<e->song.ordersLen-1) {
|
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||||
setOrder(curOrder+1);
|
setOrder(curOrder+1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -481,21 +481,21 @@ void FurnaceGUI::doAction(int what) {
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_PAT_COLLAPSE:
|
case GUI_ACTION_PAT_COLLAPSE:
|
||||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||||
if (e->song.chanCollapse[cursor.xCoarse]==0) {
|
if (e->curSubSong->chanCollapse[cursor.xCoarse]==0) {
|
||||||
e->song.chanCollapse[cursor.xCoarse]=3;
|
e->curSubSong->chanCollapse[cursor.xCoarse]=3;
|
||||||
} else if (e->song.chanCollapse[cursor.xCoarse]>0) {
|
} else if (e->curSubSong->chanCollapse[cursor.xCoarse]>0) {
|
||||||
e->song.chanCollapse[cursor.xCoarse]--;
|
e->curSubSong->chanCollapse[cursor.xCoarse]--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_PAT_INCREASE_COLUMNS:
|
case GUI_ACTION_PAT_INCREASE_COLUMNS:
|
||||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||||
e->song.pat[cursor.xCoarse].effectCols++;
|
e->curPat[cursor.xCoarse].effectCols++;
|
||||||
if (e->song.pat[cursor.xCoarse].effectCols>8) e->song.pat[cursor.xCoarse].effectCols=8;
|
if (e->curPat[cursor.xCoarse].effectCols>8) e->curPat[cursor.xCoarse].effectCols=8;
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_PAT_DECREASE_COLUMNS:
|
case GUI_ACTION_PAT_DECREASE_COLUMNS:
|
||||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||||
e->song.pat[cursor.xCoarse].effectCols--;
|
e->curPat[cursor.xCoarse].effectCols--;
|
||||||
if (e->song.pat[cursor.xCoarse].effectCols<1) e->song.pat[cursor.xCoarse].effectCols=1;
|
if (e->curPat[cursor.xCoarse].effectCols<1) e->curPat[cursor.xCoarse].effectCols=1;
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_PAT_INTERPOLATE:
|
case GUI_ACTION_PAT_INTERPOLATE:
|
||||||
doInterpolate();
|
doInterpolate();
|
||||||
|
@ -1196,7 +1196,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_ACTION_ORDERS_DOWN:
|
case GUI_ACTION_ORDERS_DOWN:
|
||||||
if (curOrder<e->song.ordersLen-1) {
|
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||||
setOrder(curOrder+1);
|
setOrder(curOrder+1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1209,7 +1209,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
orderCursor=firstChannel;
|
orderCursor=firstChannel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!e->song.chanShow[orderCursor]);
|
} while (!e->curSubSong->chanShow[orderCursor]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_ACTION_ORDERS_RIGHT: {
|
case GUI_ACTION_ORDERS_RIGHT: {
|
||||||
|
@ -1221,20 +1221,20 @@ void FurnaceGUI::doAction(int what) {
|
||||||
orderCursor=lastChannel-1;
|
orderCursor=lastChannel-1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!e->song.chanShow[orderCursor]);
|
} while (!e->curSubSong->chanShow[orderCursor]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_ACTION_ORDERS_INCREASE: {
|
case GUI_ACTION_ORDERS_INCREASE: {
|
||||||
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
||||||
if (e->song.orders.ord[orderCursor][curOrder]<0x7f) {
|
if (e->curOrders->ord[orderCursor][curOrder]<0x7f) {
|
||||||
e->song.orders.ord[orderCursor][curOrder]++;
|
e->curOrders->ord[orderCursor][curOrder]++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_ACTION_ORDERS_DECREASE: {
|
case GUI_ACTION_ORDERS_DECREASE: {
|
||||||
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
||||||
if (e->song.orders.ord[orderCursor][curOrder]>0) {
|
if (e->curOrders->ord[orderCursor][curOrder]>0) {
|
||||||
e->song.orders.ord[orderCursor][curOrder]--;
|
e->curOrders->ord[orderCursor][curOrder]--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ void FurnaceGUI::drawEditControls() {
|
||||||
ImGui::Text("Edit Step");
|
ImGui::Text("Edit Step");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
||||||
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
|
if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
|
||||||
if (editStep<0) editStep=0;
|
if (editStep<0) editStep=0;
|
||||||
|
|
||||||
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||||
|
@ -148,7 +148,7 @@ void FurnaceGUI::drawEditControls() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(96.0f*dpiScale);
|
ImGui::SetNextItemWidth(96.0f*dpiScale);
|
||||||
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
||||||
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
|
if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
|
||||||
if (editStep<0) editStep=0;
|
if (editStep<0) editStep=0;
|
||||||
|
|
||||||
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||||
|
@ -217,7 +217,7 @@ void FurnaceGUI::drawEditControls() {
|
||||||
ImGui::Text("Step");
|
ImGui::Text("Step");
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputInt("##EditStep",&editStep,0,0)) {
|
if (ImGui::InputInt("##EditStep",&editStep,0,0)) {
|
||||||
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
|
if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
|
||||||
if (editStep<0) editStep=0;
|
if (editStep<0) editStep=0;
|
||||||
|
|
||||||
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||||
|
@ -317,7 +317,7 @@ void FurnaceGUI::drawEditControls() {
|
||||||
ImGui::SetCursorPosX(cursor);
|
ImGui::SetCursorPosX(cursor);
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
|
||||||
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
|
if (editStep>=e->curSubSong->patLen) editStep=e->curSubSong->patLen-1;
|
||||||
if (editStep<0) editStep=0;
|
if (editStep<0) editStep=0;
|
||||||
|
|
||||||
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||||
|
|
|
@ -44,8 +44,8 @@ const char* noteNameNormal(short note, short octave) {
|
||||||
void FurnaceGUI::prepareUndo(ActionType action) {
|
void FurnaceGUI::prepareUndo(ActionType action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case GUI_UNDO_CHANGE_ORDER:
|
case GUI_UNDO_CHANGE_ORDER:
|
||||||
oldOrders=e->song.orders;
|
memcpy(&oldOrders,e->curOrders,sizeof(DivOrders));
|
||||||
oldOrdersLen=e->song.ordersLen;
|
oldOrdersLen=e->curSubSong->ordersLen;
|
||||||
break;
|
break;
|
||||||
case GUI_UNDO_PATTERN_EDIT:
|
case GUI_UNDO_PATTERN_EDIT:
|
||||||
case GUI_UNDO_PATTERN_DELETE:
|
case GUI_UNDO_PATTERN_DELETE:
|
||||||
|
@ -63,7 +63,7 @@ void FurnaceGUI::prepareUndo(ActionType action) {
|
||||||
case GUI_UNDO_PATTERN_COLLAPSE:
|
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||||
case GUI_UNDO_PATTERN_EXPAND:
|
case GUI_UNDO_PATTERN_EXPAND:
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false)->copyOn(oldPat[i]);
|
e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -78,18 +78,19 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
||||||
s.selEnd=selEnd;
|
s.selEnd=selEnd;
|
||||||
s.order=curOrder;
|
s.order=curOrder;
|
||||||
s.nibble=curNibble;
|
s.nibble=curNibble;
|
||||||
|
size_t subSong=e->getCurrentSubSong();
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case GUI_UNDO_CHANGE_ORDER:
|
case GUI_UNDO_CHANGE_ORDER:
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
for (int j=0; j<128; j++) {
|
for (int j=0; j<128; j++) {
|
||||||
if (oldOrders.ord[i][j]!=e->song.orders.ord[i][j]) {
|
if (oldOrders.ord[i][j]!=e->curOrders->ord[i][j]) {
|
||||||
s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j]));
|
s.ord.push_back(UndoOrderData(subSong,i,j,oldOrders.ord[i][j],e->curOrders->ord[i][j]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.oldOrdersLen=oldOrdersLen;
|
s.oldOrdersLen=oldOrdersLen;
|
||||||
s.newOrdersLen=e->song.ordersLen;
|
s.newOrdersLen=e->curSubSong->ordersLen;
|
||||||
if (oldOrdersLen!=e->song.ordersLen) {
|
if (oldOrdersLen!=e->curSubSong->ordersLen) {
|
||||||
doPush=true;
|
doPush=true;
|
||||||
}
|
}
|
||||||
if (!s.ord.empty()) {
|
if (!s.ord.empty()) {
|
||||||
|
@ -112,11 +113,11 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
||||||
case GUI_UNDO_PATTERN_COLLAPSE:
|
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||||
case GUI_UNDO_PATTERN_EXPAND:
|
case GUI_UNDO_PATTERN_EXPAND:
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false);
|
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
||||||
for (int j=0; j<e->song.patLen; j++) {
|
for (int j=0; j<e->curSubSong->patLen; j++) {
|
||||||
for (int k=0; k<32; k++) {
|
for (int k=0; k<32; k++) {
|
||||||
if (p->data[j][k]!=oldPat[i]->data[j][k]) {
|
if (p->data[j][k]!=oldPat[i]->data[j][k]) {
|
||||||
s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k]));
|
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,15 +138,15 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
||||||
void FurnaceGUI::doSelectAll() {
|
void FurnaceGUI::doSelectAll() {
|
||||||
finishSelection();
|
finishSelection();
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectCols*2) {
|
if (selStart.xFine==0 && selEnd.xFine==2+e->curPat[selEnd.xCoarse].effectCols*2) {
|
||||||
if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern
|
if (selStart.y==0 && selEnd.y==e->curSubSong->patLen-1) { // select entire pattern
|
||||||
selStart.xCoarse=0;
|
selStart.xCoarse=0;
|
||||||
selStart.xFine=0;
|
selStart.xFine=0;
|
||||||
selEnd.xCoarse=e->getTotalChannelCount()-1;
|
selEnd.xCoarse=e->getTotalChannelCount()-1;
|
||||||
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2;
|
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
|
||||||
} else { // select entire column
|
} else { // select entire column
|
||||||
selStart.y=0;
|
selStart.y=0;
|
||||||
selEnd.y=e->song.patLen-1;
|
selEnd.y=e->curSubSong->patLen-1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int selStartX=0;
|
int selStartX=0;
|
||||||
|
@ -153,26 +154,26 @@ void FurnaceGUI::doSelectAll() {
|
||||||
// find row position
|
// find row position
|
||||||
for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) {
|
for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) {
|
||||||
i.xFine++;
|
i.xFine++;
|
||||||
if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) {
|
if (i.xFine>=3+e->curPat[i.xCoarse].effectCols*2) {
|
||||||
i.xFine=0;
|
i.xFine=0;
|
||||||
i.xCoarse++;
|
i.xCoarse++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) {
|
for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) {
|
||||||
i.xFine++;
|
i.xFine++;
|
||||||
if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) {
|
if (i.xFine>=3+e->curPat[i.xCoarse].effectCols*2) {
|
||||||
i.xFine=0;
|
i.xFine=0;
|
||||||
i.xCoarse++;
|
i.xCoarse++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1);
|
float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1);
|
||||||
if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down
|
if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->curSubSong->patLen-1)) { // up-down
|
||||||
selStart.y=0;
|
selStart.y=0;
|
||||||
selEnd.y=e->song.patLen-1;
|
selEnd.y=e->curSubSong->patLen-1;
|
||||||
} else { // left-right
|
} else { // left-right
|
||||||
selStart.xFine=0;
|
selStart.xFine=0;
|
||||||
selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2;
|
selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,9 +199,9 @@ void FurnaceGUI::doDelete() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskDelete,iFine);
|
maskOut(opMaskDelete,iFine);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
|
@ -235,12 +236,12 @@ void FurnaceGUI::doPullDelete() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskPullDelete,iFine);
|
maskOut(opMaskPullDelete,iFine);
|
||||||
for (int j=selStart.y; j<e->song.patLen; j++) {
|
for (int j=selStart.y; j<e->curSubSong->patLen; j++) {
|
||||||
if (j<e->song.patLen-1) {
|
if (j<e->curSubSong->patLen-1) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
pat->data[j][iFine]=pat->data[j+1][iFine];
|
pat->data[j][iFine]=pat->data[j+1][iFine];
|
||||||
}
|
}
|
||||||
|
@ -267,11 +268,11 @@ void FurnaceGUI::doInsert() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskInsert,iFine);
|
maskOut(opMaskInsert,iFine);
|
||||||
for (int j=e->song.patLen-1; j>=selStart.y; j--) {
|
for (int j=e->curSubSong->patLen-1; j>=selStart.y; j--) {
|
||||||
if (j==selStart.y) {
|
if (j==selStart.y) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
pat->data[j][iFine]=0;
|
pat->data[j][iFine]=0;
|
||||||
|
@ -299,9 +300,9 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(mask,iFine);
|
maskOut(mask,iFine);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
|
@ -367,9 +368,9 @@ void FurnaceGUI::doCopy(bool cut) {
|
||||||
}
|
}
|
||||||
clipboard+='\n';
|
clipboard+='\n';
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
clipboard+=noteNameNormal(pat->data[j][0],pat->data[j][1]);
|
clipboard+=noteNameNormal(pat->data[j][0],pat->data[j][1]);
|
||||||
if (cut) {
|
if (cut) {
|
||||||
|
@ -432,7 +433,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
|
|
||||||
int j=cursor.y;
|
int j=cursor.y;
|
||||||
char note[4];
|
char note[4];
|
||||||
for (size_t i=2; i<data.size() && j<e->song.patLen; i++) {
|
for (size_t i=2; i<data.size() && j<e->curSubSong->patLen; i++) {
|
||||||
size_t charPos=0;
|
size_t charPos=0;
|
||||||
int iCoarse=cursor.xCoarse;
|
int iCoarse=cursor.xCoarse;
|
||||||
int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff;
|
int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff;
|
||||||
|
@ -440,10 +441,10 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
String& line=data[i];
|
String& line=data[i];
|
||||||
|
|
||||||
while (charPos<line.size() && iCoarse<lastChannel) {
|
while (charPos<line.size() && iCoarse<lastChannel) {
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
if (line[charPos]=='|') {
|
if (line[charPos]=='|') {
|
||||||
iCoarse++;
|
iCoarse++;
|
||||||
if (iCoarse<lastChannel) while (!e->song.chanShow[iCoarse]) {
|
if (iCoarse<lastChannel) while (!e->curSubSong->chanShow[iCoarse]) {
|
||||||
iCoarse++;
|
iCoarse++;
|
||||||
if (iCoarse>=lastChannel) break;
|
if (iCoarse>=lastChannel) break;
|
||||||
}
|
}
|
||||||
|
@ -530,7 +531,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) {
|
if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) {
|
||||||
if (iFine<(3+e->song.pat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
|
if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -543,7 +544,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
j++;
|
j++;
|
||||||
if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && curOrder<e->song.ordersLen-1) {
|
if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->curSubSong->patLen && curOrder<e->curSubSong->ordersLen-1) {
|
||||||
j=0;
|
j=0;
|
||||||
curOrder++;
|
curOrder++;
|
||||||
}
|
}
|
||||||
|
@ -554,7 +555,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
||||||
}
|
}
|
||||||
if (settings.cursorPastePos) {
|
if (settings.cursorPastePos) {
|
||||||
cursor.y=j;
|
cursor.y=j;
|
||||||
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
|
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,8 +568,8 @@ void FurnaceGUI::doChangeIns(int ins) {
|
||||||
|
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) {
|
if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) {
|
||||||
pat->data[j][2]=ins;
|
pat->data[j][2]=ins;
|
||||||
|
@ -587,9 +588,9 @@ void FurnaceGUI::doInterpolate() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskInterpolate,iFine);
|
maskOut(opMaskInterpolate,iFine);
|
||||||
points.clear();
|
points.clear();
|
||||||
if (iFine!=0) {
|
if (iFine!=0) {
|
||||||
|
@ -646,9 +647,9 @@ void FurnaceGUI::doFade(int p0, int p1, bool mode) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskFade,iFine);
|
maskOut(opMaskFade,iFine);
|
||||||
if (iFine!=0) {
|
if (iFine!=0) {
|
||||||
int absoluteTop=255;
|
int absoluteTop=255;
|
||||||
|
@ -684,9 +685,9 @@ void FurnaceGUI::doInvertValues() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskInvertVal,iFine);
|
maskOut(opMaskInvertVal,iFine);
|
||||||
if (iFine!=0) {
|
if (iFine!=0) {
|
||||||
int top=255;
|
int top=255;
|
||||||
|
@ -715,9 +716,9 @@ void FurnaceGUI::doScale(float top) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskScale,iFine);
|
maskOut(opMaskScale,iFine);
|
||||||
if (iFine!=0) {
|
if (iFine!=0) {
|
||||||
int absoluteTop=255;
|
int absoluteTop=255;
|
||||||
|
@ -746,9 +747,9 @@ void FurnaceGUI::doRandomize(int bottom, int top, bool mode) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskRandomize,iFine);
|
maskOut(opMaskRandomize,iFine);
|
||||||
if (iFine!=0) {
|
if (iFine!=0) {
|
||||||
int absoluteTop=255;
|
int absoluteTop=255;
|
||||||
|
@ -792,9 +793,9 @@ void FurnaceGUI::doFlip() {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskFlip,iFine);
|
maskOut(opMaskFlip,iFine);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
|
@ -823,9 +824,9 @@ void FurnaceGUI::doCollapse(int divider) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskCollapseExpand,iFine);
|
maskOut(opMaskCollapseExpand,iFine);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
|
@ -880,9 +881,9 @@ void FurnaceGUI::doExpand(int multiplier) {
|
||||||
int iCoarse=selStart.xCoarse;
|
int iCoarse=selStart.xCoarse;
|
||||||
int iFine=selStart.xFine;
|
int iFine=selStart.xFine;
|
||||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||||
if (!e->song.chanShow[iCoarse]) continue;
|
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||||
maskOut(opMaskCollapseExpand,iFine);
|
maskOut(opMaskCollapseExpand,iFine);
|
||||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
|
@ -891,7 +892,7 @@ void FurnaceGUI::doExpand(int multiplier) {
|
||||||
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
|
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
|
||||||
}
|
}
|
||||||
for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) {
|
for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) {
|
||||||
if ((j+selStart.y)>=e->song.patLen) break;
|
if ((j+selStart.y)>=e->curSubSong->patLen) break;
|
||||||
if ((j%multiplier)!=0) {
|
if ((j%multiplier)!=0) {
|
||||||
if (iFine==0) {
|
if (iFine==0) {
|
||||||
pat->data[j+selStart.y][0]=0;
|
pat->data[j+selStart.y][0]=0;
|
||||||
|
@ -921,9 +922,10 @@ void FurnaceGUI::doUndo() {
|
||||||
|
|
||||||
switch (us.type) {
|
switch (us.type) {
|
||||||
case GUI_UNDO_CHANGE_ORDER:
|
case GUI_UNDO_CHANGE_ORDER:
|
||||||
e->song.ordersLen=us.oldOrdersLen;
|
e->curSubSong->ordersLen=us.oldOrdersLen;
|
||||||
for (UndoOrderData& i: us.ord) {
|
for (UndoOrderData& i: us.ord) {
|
||||||
e->song.orders.ord[i.chan][i.ord]=i.oldVal;
|
e->changeSongP(i.subSong);
|
||||||
|
e->curOrders->ord[i.chan][i.ord]=i.oldVal;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_UNDO_PATTERN_EDIT:
|
case GUI_UNDO_PATTERN_EDIT:
|
||||||
|
@ -942,7 +944,8 @@ void FurnaceGUI::doUndo() {
|
||||||
case GUI_UNDO_PATTERN_COLLAPSE:
|
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||||
case GUI_UNDO_PATTERN_EXPAND:
|
case GUI_UNDO_PATTERN_EXPAND:
|
||||||
for (UndoPatternData& i: us.pat) {
|
for (UndoPatternData& i: us.pat) {
|
||||||
DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true);
|
e->changeSongP(i.subSong);
|
||||||
|
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
|
||||||
p->data[i.row][i.col]=i.oldVal;
|
p->data[i.row][i.col]=i.oldVal;
|
||||||
}
|
}
|
||||||
if (!e->isPlaying() || !followPattern) {
|
if (!e->isPlaying() || !followPattern) {
|
||||||
|
@ -967,9 +970,10 @@ void FurnaceGUI::doRedo() {
|
||||||
|
|
||||||
switch (us.type) {
|
switch (us.type) {
|
||||||
case GUI_UNDO_CHANGE_ORDER:
|
case GUI_UNDO_CHANGE_ORDER:
|
||||||
e->song.ordersLen=us.newOrdersLen;
|
e->curSubSong->ordersLen=us.newOrdersLen;
|
||||||
for (UndoOrderData& i: us.ord) {
|
for (UndoOrderData& i: us.ord) {
|
||||||
e->song.orders.ord[i.chan][i.ord]=i.newVal;
|
e->changeSongP(i.subSong);
|
||||||
|
e->curOrders->ord[i.chan][i.ord]=i.newVal;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_UNDO_PATTERN_EDIT:
|
case GUI_UNDO_PATTERN_EDIT:
|
||||||
|
@ -988,7 +992,8 @@ void FurnaceGUI::doRedo() {
|
||||||
case GUI_UNDO_PATTERN_COLLAPSE:
|
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||||
case GUI_UNDO_PATTERN_EXPAND:
|
case GUI_UNDO_PATTERN_EXPAND:
|
||||||
for (UndoPatternData& i: us.pat) {
|
for (UndoPatternData& i: us.pat) {
|
||||||
DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true);
|
e->changeSongP(i.subSong);
|
||||||
|
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
|
||||||
p->data[i.row][i.col]=i.newVal;
|
p->data[i.row][i.col]=i.newVal;
|
||||||
}
|
}
|
||||||
if (!e->isPlaying()) {
|
if (!e->isPlaying()) {
|
||||||
|
|
|
@ -793,9 +793,9 @@ void FurnaceGUI::prepareLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float FurnaceGUI::calcBPM(int s1, int s2, float hz) {
|
float FurnaceGUI::calcBPM(int s1, int s2, float hz) {
|
||||||
float hl=e->song.hilightA;
|
float hl=e->curSubSong->hilightA;
|
||||||
if (hl<=0.0f) hl=4.0f;
|
if (hl<=0.0f) hl=4.0f;
|
||||||
float timeBase=e->song.timeBase+1;
|
float timeBase=e->curSubSong->timeBase+1;
|
||||||
float speedSum=s1+s2;
|
float speedSum=s1+s2;
|
||||||
if (timeBase<1.0f) timeBase=1.0f;
|
if (timeBase<1.0f) timeBase=1.0f;
|
||||||
if (speedSum<1.0f) speedSum=1.0f;
|
if (speedSum<1.0f) speedSum=1.0f;
|
||||||
|
@ -857,7 +857,7 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::noteInput(int num, int key, int vol) {
|
void FurnaceGUI::noteInput(int num, int key, int vol) {
|
||||||
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
|
||||||
|
|
||||||
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
|
|
||||||
|
@ -901,7 +901,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
||||||
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
|
||||||
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
if (target==-1) target=cursor.xFine+1;
|
if (target==-1) target=cursor.xFine+1;
|
||||||
if (direct) {
|
if (direct) {
|
||||||
|
@ -965,7 +965,7 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
||||||
editAdvance();
|
editAdvance();
|
||||||
} else {
|
} else {
|
||||||
if (settings.effectCursorDir==2) {
|
if (settings.effectCursorDir==2) {
|
||||||
if (++cursor.xFine>=(3+(e->song.pat[cursor.xCoarse].effectCols*2))) {
|
if (++cursor.xFine>=(3+(e->curPat[cursor.xCoarse].effectCols*2))) {
|
||||||
cursor.xFine=3;
|
cursor.xFine=3;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1123,7 +1123,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
int num=valueKeys.at(ev.key.keysym.sym);
|
int num=valueKeys.at(ev.key.keysym.sym);
|
||||||
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
|
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
|
||||||
e->lockSave([this,num]() {
|
e->lockSave([this,num]() {
|
||||||
e->song.orders.ord[orderCursor][curOrder]=((e->song.orders.ord[orderCursor][curOrder]<<4)|num);
|
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
|
||||||
});
|
});
|
||||||
if (orderEditMode==2 || orderEditMode==3) {
|
if (orderEditMode==2 || orderEditMode==3) {
|
||||||
curNibble=!curNibble;
|
curNibble=!curNibble;
|
||||||
|
@ -1132,7 +1132,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
orderCursor++;
|
orderCursor++;
|
||||||
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
|
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
|
||||||
} else if (orderEditMode==3) {
|
} else if (orderEditMode==3) {
|
||||||
if (curOrder<e->song.ordersLen-1) {
|
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||||
setOrder(curOrder+1);
|
setOrder(curOrder+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2031,7 +2031,7 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Set")) {
|
if (ImGui::Button("Set")) {
|
||||||
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true);
|
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
|
||||||
latchIns=pat->data[cursor.y][2];
|
latchIns=pat->data[cursor.y][2];
|
||||||
latchVol=pat->data[cursor.y][3];
|
latchVol=pat->data[cursor.y][3];
|
||||||
latchEffect=pat->data[cursor.y][4];
|
latchEffect=pat->data[cursor.y][4];
|
||||||
|
@ -2817,12 +2817,12 @@ bool FurnaceGUI::loop() {
|
||||||
if (e->isPlaying()) {
|
if (e->isPlaying()) {
|
||||||
int totalTicks=e->getTotalTicks();
|
int totalTicks=e->getTotalTicks();
|
||||||
int totalSeconds=e->getTotalSeconds();
|
int totalSeconds=e->getTotalSeconds();
|
||||||
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz()),e->getOrder(),e->song.ordersLen,e->getRow(),e->song.patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz()),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
||||||
} else {
|
} else {
|
||||||
bool hasInfo=false;
|
bool hasInfo=false;
|
||||||
String info;
|
String info;
|
||||||
if (cursor.xCoarse>=0 && cursor.xCoarse<e->getTotalChannelCount()) {
|
if (cursor.xCoarse>=0 && cursor.xCoarse<e->getTotalChannelCount()) {
|
||||||
DivPattern* p=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],false);
|
DivPattern* p=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
|
||||||
if (cursor.xFine>=0) switch (cursor.xFine) {
|
if (cursor.xFine>=0) switch (cursor.xFine) {
|
||||||
case 0: // note
|
case 0: // note
|
||||||
if (p->data[cursor.y][0]>0) {
|
if (p->data[cursor.y][0]>0) {
|
||||||
|
@ -2883,6 +2883,7 @@ bool FurnaceGUI::loop() {
|
||||||
|
|
||||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||||
|
|
||||||
|
drawSubSongs();
|
||||||
drawPattern();
|
drawPattern();
|
||||||
drawEditControls();
|
drawEditControls();
|
||||||
drawSongInfo();
|
drawSongInfo();
|
||||||
|
@ -3491,7 +3492,7 @@ bool FurnaceGUI::loop() {
|
||||||
stop();
|
stop();
|
||||||
e->lockEngine([this]() {
|
e->lockEngine([this]() {
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],true);
|
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
|
||||||
memset(pat->data,-1,256*32*sizeof(short));
|
memset(pat->data,-1,256*32*sizeof(short));
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<256; j++) {
|
||||||
pat->data[j][0]=0;
|
pat->data[j][0]=0;
|
||||||
|
@ -3650,6 +3651,7 @@ bool FurnaceGUI::init() {
|
||||||
regViewOpen=e->getConfBool("regViewOpen",false);
|
regViewOpen=e->getConfBool("regViewOpen",false);
|
||||||
logOpen=e->getConfBool("logOpen",false);
|
logOpen=e->getConfBool("logOpen",false);
|
||||||
effectListOpen=e->getConfBool("effectListOpen",false);
|
effectListOpen=e->getConfBool("effectListOpen",false);
|
||||||
|
subSongsOpen=e->getConfBool("subSongsOpen",true);
|
||||||
|
|
||||||
tempoView=e->getConfBool("tempoView",true);
|
tempoView=e->getConfBool("tempoView",true);
|
||||||
waveHex=e->getConfBool("waveHex",false);
|
waveHex=e->getConfBool("waveHex",false);
|
||||||
|
@ -3841,6 +3843,7 @@ bool FurnaceGUI::finish() {
|
||||||
e->setConf("regViewOpen",regViewOpen);
|
e->setConf("regViewOpen",regViewOpen);
|
||||||
e->setConf("logOpen",logOpen);
|
e->setConf("logOpen",logOpen);
|
||||||
e->setConf("effectListOpen",effectListOpen);
|
e->setConf("effectListOpen",effectListOpen);
|
||||||
|
e->setConf("subSongsOpen",subSongsOpen);
|
||||||
|
|
||||||
// commit last window size
|
// commit last window size
|
||||||
e->setConf("lastWindowWidth",scrW);
|
e->setConf("lastWindowWidth",scrW);
|
||||||
|
@ -3961,6 +3964,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
logOpen(false),
|
logOpen(false),
|
||||||
effectListOpen(false),
|
effectListOpen(false),
|
||||||
chanOscOpen(false),
|
chanOscOpen(false),
|
||||||
|
subSongsOpen(true),
|
||||||
/*
|
/*
|
||||||
editControlsDocked(false),
|
editControlsDocked(false),
|
||||||
ordersDocked(false),
|
ordersDocked(false),
|
||||||
|
@ -3988,6 +3992,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
logDocked(false),
|
logDocked(false),
|
||||||
effectListDocked(false),
|
effectListDocked(false),
|
||||||
chanOscDocked(false),
|
chanOscDocked(false),
|
||||||
|
subSongsDocked(false),
|
||||||
*/
|
*/
|
||||||
selecting(false),
|
selecting(false),
|
||||||
selectingFull(false),
|
selectingFull(false),
|
||||||
|
|
|
@ -231,7 +231,8 @@ enum FurnaceGUIWindows {
|
||||||
GUI_WINDOW_REGISTER_VIEW,
|
GUI_WINDOW_REGISTER_VIEW,
|
||||||
GUI_WINDOW_LOG,
|
GUI_WINDOW_LOG,
|
||||||
GUI_WINDOW_EFFECT_LIST,
|
GUI_WINDOW_EFFECT_LIST,
|
||||||
GUI_WINDOW_CHAN_OSC
|
GUI_WINDOW_CHAN_OSC,
|
||||||
|
GUI_WINDOW_SUBSONGS
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FurnaceGUIFileDialogs {
|
enum FurnaceGUIFileDialogs {
|
||||||
|
@ -337,6 +338,7 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_WINDOW_LOG,
|
GUI_ACTION_WINDOW_LOG,
|
||||||
GUI_ACTION_WINDOW_EFFECT_LIST,
|
GUI_ACTION_WINDOW_EFFECT_LIST,
|
||||||
GUI_ACTION_WINDOW_CHAN_OSC,
|
GUI_ACTION_WINDOW_CHAN_OSC,
|
||||||
|
GUI_ACTION_WINDOW_SUBSONGS,
|
||||||
|
|
||||||
GUI_ACTION_COLLAPSE_WINDOW,
|
GUI_ACTION_COLLAPSE_WINDOW,
|
||||||
GUI_ACTION_CLOSE_WINDOW,
|
GUI_ACTION_CLOSE_WINDOW,
|
||||||
|
@ -550,9 +552,10 @@ enum ActionType {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UndoPatternData {
|
struct UndoPatternData {
|
||||||
int chan, pat, row, col;
|
int subSong, chan, pat, row, col;
|
||||||
short oldVal, newVal;
|
short oldVal, newVal;
|
||||||
UndoPatternData(int c, int p, int r, int co, short v1, short v2):
|
UndoPatternData(int s, int c, int p, int r, int co, short v1, short v2):
|
||||||
|
subSong(s),
|
||||||
chan(c),
|
chan(c),
|
||||||
pat(p),
|
pat(p),
|
||||||
row(r),
|
row(r),
|
||||||
|
@ -562,9 +565,10 @@ struct UndoPatternData {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UndoOrderData {
|
struct UndoOrderData {
|
||||||
int chan, ord;
|
int subSong, chan, ord;
|
||||||
unsigned char oldVal, newVal;
|
unsigned char oldVal, newVal;
|
||||||
UndoOrderData(int c, int o, unsigned char v1, unsigned char v2):
|
UndoOrderData(int s, int c, int o, unsigned char v1, unsigned char v2):
|
||||||
|
subSong(s),
|
||||||
chan(c),
|
chan(c),
|
||||||
ord(o),
|
ord(o),
|
||||||
oldVal(v1),
|
oldVal(v1),
|
||||||
|
@ -1010,12 +1014,14 @@ class FurnaceGUI {
|
||||||
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
||||||
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
||||||
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
||||||
|
bool subSongsOpen;
|
||||||
|
|
||||||
/* there ought to be a better way...
|
/* there ought to be a better way...
|
||||||
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
|
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
|
||||||
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
|
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
|
||||||
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked;
|
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked;
|
||||||
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked;
|
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked;
|
||||||
|
bool subSongsDocked;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SelectionPoint selStart, selEnd, cursor;
|
SelectionPoint selStart, selEnd, cursor;
|
||||||
|
@ -1246,6 +1252,7 @@ class FurnaceGUI {
|
||||||
void drawNewSong();
|
void drawNewSong();
|
||||||
void drawLog();
|
void drawLog();
|
||||||
void drawEffectList();
|
void drawEffectList();
|
||||||
|
void drawSubSongs();
|
||||||
|
|
||||||
void parseKeybinds();
|
void parseKeybinds();
|
||||||
void promptKey(int which);
|
void promptKey(int which);
|
||||||
|
|
|
@ -485,6 +485,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
||||||
D("WINDOW_CHANNELS", "Channels", 0),
|
D("WINDOW_CHANNELS", "Channels", 0),
|
||||||
D("WINDOW_REGISTER_VIEW", "Register View", 0),
|
D("WINDOW_REGISTER_VIEW", "Register View", 0),
|
||||||
D("WINDOW_LOG", "Log Viewer", 0),
|
D("WINDOW_LOG", "Log Viewer", 0),
|
||||||
|
D("WINDOW_SUBSONGS", "Subsongs", 0),
|
||||||
D("EFFECT_LIST", "Effect List", 0),
|
D("EFFECT_LIST", "Effect List", 0),
|
||||||
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
|
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ void FurnaceGUI::drawOrders() {
|
||||||
ImGui::SetColumnWidth(-1,regionX-24.0f*dpiScale);
|
ImGui::SetColumnWidth(-1,regionX-24.0f*dpiScale);
|
||||||
int displayChans=0;
|
int displayChans=0;
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
if (e->song.chanShow[i]) displayChans++;
|
if (e->curSubSong->chanShow[i]) displayChans++;
|
||||||
}
|
}
|
||||||
ImGui::PushFont(patFont);
|
ImGui::PushFont(patFont);
|
||||||
bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x)));
|
bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x)));
|
||||||
|
@ -56,12 +56,12 @@ void FurnaceGUI::drawOrders() {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]);
|
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]);
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
if (!e->song.chanShow[i]) continue;
|
if (!e->curSubSong->chanShow[i]) continue;
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s",e->getChannelShortName(i));
|
ImGui::Text("%s",e->getChannelShortName(i));
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
for (int i=0; i<e->song.ordersLen; i++) {
|
for (int i=0; i<e->curSubSong->ordersLen; i++) {
|
||||||
ImGui::TableNextRow(0,lineHeight);
|
ImGui::TableNextRow(0,lineHeight);
|
||||||
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
|
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
@ -84,16 +84,16 @@ void FurnaceGUI::drawOrders() {
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
for (int j=0; j<e->getTotalChannelCount(); j++) {
|
for (int j=0; j<e->getTotalChannelCount(); j++) {
|
||||||
if (!e->song.chanShow[j]) continue;
|
if (!e->curSubSong->chanShow[j]) continue;
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
DivPattern* pat=e->song.pat[j].getPattern(e->song.orders.ord[j][i],false);
|
DivPattern* pat=e->curPat[j].getPattern(e->curOrders->ord[j][i],false);
|
||||||
/*if (!pat->name.empty()) {
|
/*if (!pat->name.empty()) {
|
||||||
snprintf(selID,4096,"%s##O_%.2x_%.2x",pat->name.c_str(),j,i);
|
snprintf(selID,4096,"%s##O_%.2x_%.2x",pat->name.c_str(),j,i);
|
||||||
} else {*/
|
} else {*/
|
||||||
snprintf(selID,4096,"%.2X##O_%.2x_%.2x",e->song.orders.ord[j][i],j,i);
|
snprintf(selID,4096,"%.2X##O_%.2x_%.2x",e->curOrders->ord[j][i],j,i);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->song.orders.ord[j][i]==e->song.orders.ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
|
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->curOrders->ord[j][i]==e->curOrders->ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
|
||||||
if (ImGui::Selectable(selID,(orderEditMode!=0 && curOrder==i && orderCursor==j))) {
|
if (ImGui::Selectable(selID,(orderEditMode!=0 && curOrder==i && orderCursor==j))) {
|
||||||
if (curOrder==i) {
|
if (curOrder==i) {
|
||||||
if (orderEditMode==0) {
|
if (orderEditMode==0) {
|
||||||
|
@ -101,10 +101,10 @@ void FurnaceGUI::drawOrders() {
|
||||||
e->lockSave([this,i,j]() {
|
e->lockSave([this,i,j]() {
|
||||||
if (changeAllOrders) {
|
if (changeAllOrders) {
|
||||||
for (int k=0; k<e->getTotalChannelCount(); k++) {
|
for (int k=0; k<e->getTotalChannelCount(); k++) {
|
||||||
if (e->song.orders.ord[k][i]<0xff) e->song.orders.ord[k][i]++;
|
if (e->curOrders->ord[k][i]<0xff) e->curOrders->ord[k][i]++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (e->song.orders.ord[j][i]<0xff) e->song.orders.ord[j][i]++;
|
if (e->curOrders->ord[j][i]<0xff) e->curOrders->ord[j][i]++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
@ -137,10 +137,10 @@ void FurnaceGUI::drawOrders() {
|
||||||
e->lockSave([this,i,j]() {
|
e->lockSave([this,i,j]() {
|
||||||
if (changeAllOrders) {
|
if (changeAllOrders) {
|
||||||
for (int k=0; k<e->getTotalChannelCount(); k++) {
|
for (int k=0; k<e->getTotalChannelCount(); k++) {
|
||||||
if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--;
|
if (e->curOrders->ord[k][i]>0) e->curOrders->ord[k][i]--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--;
|
if (e->curOrders->ord[j][i]>0) e->curOrders->ord[j][i]--;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
|
|
@ -43,21 +43,21 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check if we are in range
|
// check if we are in range
|
||||||
if (ord<0 || ord>=e->song.ordersLen) {
|
if (ord<0 || ord>=e->curSubSong->ordersLen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (i<0 || i>=e->song.patLen) {
|
if (i<0 || i>=e->curSubSong->patLen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool isPushing=false;
|
bool isPushing=false;
|
||||||
ImVec4 activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE];
|
ImVec4 activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE];
|
||||||
ImVec4 inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE];
|
ImVec4 inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE];
|
||||||
ImVec4 rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX];
|
ImVec4 rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX];
|
||||||
if (e->song.hilightB>0 && !(i%e->song.hilightB)) {
|
if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
|
||||||
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI2];
|
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI2];
|
||||||
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI2];
|
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI2];
|
||||||
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI2];
|
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI2];
|
||||||
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) {
|
} else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
|
||||||
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI1];
|
activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE_HI1];
|
||||||
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI1];
|
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_HI1];
|
||||||
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI1];
|
rowIndexColor=uiColors[GUI_COLOR_PATTERN_ROW_INDEX_HI1];
|
||||||
|
@ -68,9 +68,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
|
||||||
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
||||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
|
||||||
} else if (e->song.hilightB>0 && !(i%e->song.hilightB)) {
|
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
|
||||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
|
||||||
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) {
|
} else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
|
||||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,9 +79,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
|
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
|
||||||
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
|
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
|
||||||
} else if (e->song.hilightB>0 && !(i%e->song.hilightB)) {
|
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
|
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
|
||||||
} else if (e->song.hilightA>0 && !(i%e->song.hilightA)) {
|
} else if (e->curSubSong->hilightA>0 && !(i%e->curSubSong->hilightA)) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
|
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]));
|
||||||
} else {
|
} else {
|
||||||
isPushing=false;
|
isPushing=false;
|
||||||
|
@ -106,7 +106,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
// for each column
|
// for each column
|
||||||
for (int j=0; j<chans; j++) {
|
for (int j=0; j<chans; j++) {
|
||||||
// check if channel is not hidden
|
// check if channel is not hidden
|
||||||
if (!e->song.chanShow[j]) {
|
if (!e->curSubSong->chanShow[j]) {
|
||||||
patChanX[j]=ImGui::GetCursorPosX();
|
patChanX[j]=ImGui::GetCursorPosX();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
// the following is only visible when the channel is not collapsed
|
// the following is only visible when the channel is not collapsed
|
||||||
if (e->song.chanCollapse[j]<3) {
|
if (e->curSubSong->chanCollapse[j]<3) {
|
||||||
// instrument
|
// instrument
|
||||||
if (pat->data[i][2]==-1) {
|
if (pat->data[i][2]==-1) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
|
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
|
||||||
|
@ -195,7 +195,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->song.chanCollapse[j]<2) {
|
if (e->curSubSong->chanCollapse[j]<2) {
|
||||||
// volume
|
// volume
|
||||||
if (pat->data[i][3]==-1) {
|
if (pat->data[i][3]==-1) {
|
||||||
sprintf(id,"..##PV_%d_%d",i,j);
|
sprintf(id,"..##PV_%d_%d",i,j);
|
||||||
|
@ -229,9 +229,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->song.chanCollapse[j]<1) {
|
if (e->curSubSong->chanCollapse[j]<1) {
|
||||||
// effects
|
// effects
|
||||||
for (int k=0; k<e->song.pat[j].effectCols; k++) {
|
for (int k=0; k<e->curPat[j].effectCols; k++) {
|
||||||
int index=4+(k<<1);
|
int index=4+(k<<1);
|
||||||
bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum);
|
bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum);
|
||||||
bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
|
bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
|
||||||
|
@ -358,7 +358,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
int displayChans=0;
|
int displayChans=0;
|
||||||
const DivPattern* patCache[DIV_MAX_CHANS];
|
const DivPattern* patCache[DIV_MAX_CHANS];
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
if (e->song.chanShow[i]) displayChans++;
|
if (e->curSubSong->chanShow[i]) displayChans++;
|
||||||
}
|
}
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
|
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
|
||||||
|
@ -383,7 +383,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
}
|
}
|
||||||
ImGui::TableSetupScrollFreeze(1,1);
|
ImGui::TableSetupScrollFreeze(1,1);
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
if (!e->song.chanShow[i]) continue;
|
if (!e->curSubSong->chanShow[i]) continue;
|
||||||
ImGui::TableSetupColumn(fmt::sprintf("c%d",i).c_str(),ImGuiTableColumnFlags_WidthFixed);
|
ImGui::TableSetupColumn(fmt::sprintf("c%d",i).c_str(),ImGuiTableColumnFlags_WidthFixed);
|
||||||
}
|
}
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
@ -408,10 +408,10 @@ void FurnaceGUI::drawPattern() {
|
||||||
cmdStream.clear();
|
cmdStream.clear();
|
||||||
}
|
}
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
if (!e->song.chanShow[i]) continue;
|
if (!e->curSubSong->chanShow[i]) continue;
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool displayTooltip=false;
|
bool displayTooltip=false;
|
||||||
if (e->song.chanCollapse[i]) {
|
if (e->curSubSong->chanCollapse[i]) {
|
||||||
const char* chName=e->getChannelShortName(i);
|
const char* chName=e->getChannelShortName(i);
|
||||||
if (strlen(chName)>3) {
|
if (strlen(chName)>3) {
|
||||||
snprintf(chanID,2048,"...##_CH%d",i);
|
snprintf(chanID,2048,"...##_CH%d",i);
|
||||||
|
@ -421,7 +421,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
displayTooltip=true;
|
displayTooltip=true;
|
||||||
} else {
|
} else {
|
||||||
const char* chName=e->getChannelName(i);
|
const char* chName=e->getChannelName(i);
|
||||||
size_t chNameLimit=6+4*e->song.pat[i].effectCols;
|
size_t chNameLimit=6+4*e->curPat[i].effectCols;
|
||||||
if (strlen(chName)>chNameLimit) {
|
if (strlen(chName)>chNameLimit) {
|
||||||
String shortChName=chName;
|
String shortChName=chName;
|
||||||
shortChName.resize(chNameLimit-3);
|
shortChName.resize(chNameLimit-3);
|
||||||
|
@ -483,7 +483,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
e->toggleSolo(i);
|
e->toggleSolo(i);
|
||||||
}
|
}
|
||||||
if (extraChannelButtons==2) {
|
if (extraChannelButtons==2) {
|
||||||
DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][ord],true);
|
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||||
ImGui::PushFont(mainFont);
|
ImGui::PushFont(mainFont);
|
||||||
if (patNameTarget==i) {
|
if (patNameTarget==i) {
|
||||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||||
|
@ -510,30 +510,30 @@ void FurnaceGUI::drawPattern() {
|
||||||
}
|
}
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
} else if (extraChannelButtons==1) {
|
} else if (extraChannelButtons==1) {
|
||||||
snprintf(chanID,2048,"%c##_HCH%d",e->song.chanCollapse[i]?'+':'-',i);
|
snprintf(chanID,2048,"%c##_HCH%d",e->curSubSong->chanCollapse[i]?'+':'-',i);
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
|
||||||
if (ImGui::SmallButton(chanID)) {
|
if (ImGui::SmallButton(chanID)) {
|
||||||
if (e->song.chanCollapse[i]==0) {
|
if (e->curSubSong->chanCollapse[i]==0) {
|
||||||
e->song.chanCollapse[i]=3;
|
e->curSubSong->chanCollapse[i]=3;
|
||||||
} else if (e->song.chanCollapse[i]>0) {
|
} else if (e->curSubSong->chanCollapse[i]>0) {
|
||||||
e->song.chanCollapse[i]--;
|
e->curSubSong->chanCollapse[i]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!e->song.chanCollapse[i]) {
|
if (!e->curSubSong->chanCollapse[i]) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
snprintf(chanID,2048,"<##_LCH%d",i);
|
snprintf(chanID,2048,"<##_LCH%d",i);
|
||||||
ImGui::BeginDisabled(e->song.pat[i].effectCols<=1);
|
ImGui::BeginDisabled(e->curPat[i].effectCols<=1);
|
||||||
if (ImGui::SmallButton(chanID)) {
|
if (ImGui::SmallButton(chanID)) {
|
||||||
e->song.pat[i].effectCols--;
|
e->curPat[i].effectCols--;
|
||||||
if (e->song.pat[i].effectCols<1) e->song.pat[i].effectCols=1;
|
if (e->curPat[i].effectCols<1) e->curPat[i].effectCols=1;
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(e->song.pat[i].effectCols>=8);
|
ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
|
||||||
snprintf(chanID,2048,">##_RCH%d",i);
|
snprintf(chanID,2048,">##_RCH%d",i);
|
||||||
if (ImGui::SmallButton(chanID)) {
|
if (ImGui::SmallButton(chanID)) {
|
||||||
e->song.pat[i].effectCols++;
|
e->curPat[i].effectCols++;
|
||||||
if (e->song.pat[i].effectCols>8) e->song.pat[i].effectCols=8;
|
if (e->curPat[i].effectCols>8) e->curPat[i].effectCols=8;
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
@ -555,10 +555,10 @@ void FurnaceGUI::drawPattern() {
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
if (settings.viewPrevPattern) {
|
if (settings.viewPrevPattern) {
|
||||||
if ((ord-1)>=0) for (int i=0; i<chans; i++) {
|
if ((ord-1)>=0) for (int i=0; i<chans; i++) {
|
||||||
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord-1],true);
|
patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord-1],true);
|
||||||
}
|
}
|
||||||
for (int i=0; i<dummyRows-1; i++) {
|
for (int i=0; i<dummyRows-1; i++) {
|
||||||
patternRow(e->song.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true);
|
patternRow(e->curSubSong->patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<dummyRows-1; i++) {
|
for (int i=0; i<dummyRows-1; i++) {
|
||||||
|
@ -569,16 +569,16 @@ void FurnaceGUI::drawPattern() {
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
// active area
|
// active area
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord],true);
|
patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||||
}
|
}
|
||||||
for (int i=0; i<e->song.patLen; i++) {
|
for (int i=0; i<e->curSubSong->patLen; i++) {
|
||||||
patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false);
|
patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false);
|
||||||
}
|
}
|
||||||
// next pattern
|
// next pattern
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
if (settings.viewPrevPattern) {
|
if (settings.viewPrevPattern) {
|
||||||
if ((ord+1)<e->song.ordersLen) for (int i=0; i<chans; i++) {
|
if ((ord+1)<e->curSubSong->ordersLen) for (int i=0; i<chans; i++) {
|
||||||
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord+1],true);
|
patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord+1],true);
|
||||||
}
|
}
|
||||||
for (int i=0; i<=dummyRows; i++) {
|
for (int i=0; i<=dummyRows; i++) {
|
||||||
patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true);
|
patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true);
|
||||||
|
@ -612,7 +612,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
if (curOrder>0) {
|
if (curOrder>0) {
|
||||||
setOrder(curOrder-1);
|
setOrder(curOrder-1);
|
||||||
ImGui::SetScrollY(ImGui::GetScrollMaxY());
|
ImGui::SetScrollY(ImGui::GetScrollMaxY());
|
||||||
updateScroll(e->song.patLen);
|
updateScroll(e->curSubSong->patLen);
|
||||||
}
|
}
|
||||||
haveHitBounds=false;
|
haveHitBounds=false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -624,7 +624,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
} else {
|
} else {
|
||||||
if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) {
|
if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) {
|
||||||
if (haveHitBounds) {
|
if (haveHitBounds) {
|
||||||
if (curOrder<(e->song.ordersLen-1)) {
|
if (curOrder<(e->curSubSong->ordersLen-1)) {
|
||||||
setOrder(curOrder+1);
|
setOrder(curOrder+1);
|
||||||
ImGui::SetScrollY(0);
|
ImGui::SetScrollY(0);
|
||||||
updateScroll(0);
|
updateScroll(0);
|
||||||
|
@ -776,7 +776,7 @@ void FurnaceGUI::drawPattern() {
|
||||||
// note slides
|
// note slides
|
||||||
ImVec2 arrowPoints[7];
|
ImVec2 arrowPoints[7];
|
||||||
if (e->isPlaying()) for (int i=0; i<chans; i++) {
|
if (e->isPlaying()) for (int i=0; i<chans; i++) {
|
||||||
if (!e->song.chanShow[i]) continue;
|
if (!e->curSubSong->chanShow[i]) continue;
|
||||||
DivChannelState* ch=e->getChanState(i);
|
DivChannelState* ch=e->getChanState(i);
|
||||||
if (ch->portaSpeed>0) {
|
if (ch->portaSpeed>0) {
|
||||||
ImVec4 col=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
|
ImVec4 col=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
|
||||||
|
|
|
@ -1495,6 +1495,7 @@ void FurnaceGUI::drawSettings() {
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO);
|
||||||
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SUBSONGS);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT);
|
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
#include "imgui.h"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
#include "intConst.h"
|
#include "intConst.h"
|
||||||
|
|
||||||
|
@ -74,28 +75,28 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
float avail=ImGui::GetContentRegionAvail().x;
|
float avail=ImGui::GetContentRegionAvail().x;
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
unsigned char realTB=e->song.timeBase+1;
|
unsigned char realTB=e->curSubSong->timeBase+1;
|
||||||
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (realTB<1) realTB=1;
|
if (realTB<1) realTB=1;
|
||||||
if (realTB>16) realTB=16;
|
if (realTB>16) realTB=16;
|
||||||
e->song.timeBase=realTB-1;
|
e->curSubSong->timeBase=realTB-1;
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz));
|
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz));
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Speed");
|
ImGui::Text("Speed");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (e->song.speed1<1) e->song.speed1=1;
|
if (e->curSubSong->speed1<1) e->curSubSong->speed1=1;
|
||||||
if (e->isPlaying()) play();
|
if (e->isPlaying()) play();
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (e->song.speed2<1) e->song.speed2=1;
|
if (e->curSubSong->speed2<1) e->curSubSong->speed2=1;
|
||||||
if (e->isPlaying()) play();
|
if (e->isPlaying()) play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,12 +105,12 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
ImGui::Text("Highlight");
|
ImGui::Text("Highlight");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) {
|
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) {
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) {
|
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) {
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +119,11 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
ImGui::Text("Pattern Length");
|
ImGui::Text("Pattern Length");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
int patLen=e->song.patLen;
|
int patLen=e->curSubSong->patLen;
|
||||||
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
|
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
|
||||||
if (patLen<1) patLen=1;
|
if (patLen<1) patLen=1;
|
||||||
if (patLen>256) patLen=256;
|
if (patLen>256) patLen=256;
|
||||||
e->song.patLen=patLen;
|
e->curSubSong->patLen=patLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
@ -130,11 +131,11 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
ImGui::Text("Song Length");
|
ImGui::Text("Song Length");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
int ordLen=e->song.ordersLen;
|
int ordLen=e->curSubSong->ordersLen;
|
||||||
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
|
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
|
||||||
if (ordLen<1) ordLen=1;
|
if (ordLen<1) ordLen=1;
|
||||||
if (ordLen>256) ordLen=256;
|
if (ordLen>256) ordLen=256;
|
||||||
e->song.ordersLen=ordLen;
|
e->curSubSong->ordersLen=ordLen;
|
||||||
if (curOrder>=ordLen) {
|
if (curOrder>=ordLen) {
|
||||||
setOrder(ordLen-1);
|
setOrder(ordLen-1);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,7 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(avail);
|
||||||
float setHz=tempoView?e->song.hz*2.5:e->song.hz;
|
float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz;
|
||||||
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
|
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
|
||||||
if (tempoView) setHz/=2.5;
|
if (tempoView) setHz/=2.5;
|
||||||
if (setHz<10) setHz=10;
|
if (setHz<10) setHz=10;
|
||||||
|
@ -156,13 +157,13 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
}
|
}
|
||||||
if (tempoView) {
|
if (tempoView) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("= %gHz",e->song.hz);
|
ImGui::Text("= %gHz",e->curSubSong->hz);
|
||||||
} else {
|
} else {
|
||||||
if (e->song.hz>=49.98 && e->song.hz<=50.02) {
|
if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("PAL");
|
ImGui::Text("PAL");
|
||||||
}
|
}
|
||||||
if (e->song.hz>=59.9 && e->song.hz<=60.11) {
|
if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("NTSC");
|
ImGui::Text("NTSC");
|
||||||
}
|
}
|
||||||
|
|
94
src/gui/subSongs.cpp
Normal file
94
src/gui/subSongs.cpp
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#include "gui.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "IconsFontAwesome4.h"
|
||||||
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
|
||||||
|
void FurnaceGUI::drawSubSongs() {
|
||||||
|
if (nextWindow==GUI_WINDOW_SUBSONGS) {
|
||||||
|
subSongsOpen=true;
|
||||||
|
ImGui::SetNextWindowFocus();
|
||||||
|
nextWindow=GUI_WINDOW_NOTHING;
|
||||||
|
}
|
||||||
|
if (!oscOpen) return;
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
|
||||||
|
if (ImGui::Begin("Subsongs",&subSongsOpen,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
|
||||||
|
char id[1024];
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0f-ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
if (e->curSubSong->name.empty()) {
|
||||||
|
snprintf(id,1023,"%d. <no name>",(int)e->getCurrentSubSong()+1);
|
||||||
|
} else {
|
||||||
|
snprintf(id,1023,"%d. %s",(int)e->getCurrentSubSong()+1,e->curSubSong->name.c_str());
|
||||||
|
}
|
||||||
|
if (ImGui::BeginCombo("##SubSong",id)) {
|
||||||
|
for (size_t i=0; i<e->song.subsong.size(); i++) {
|
||||||
|
if (e->song.subsong[i]->name.empty()) {
|
||||||
|
snprintf(id,1023,"%d. <no name>",(int)i+1);
|
||||||
|
} else {
|
||||||
|
snprintf(id,1023,"%d. %s",(int)i+1,e->song.subsong[i]->name.c_str());
|
||||||
|
}
|
||||||
|
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
|
||||||
|
e->changeSongP(i);
|
||||||
|
updateScroll(0);
|
||||||
|
oldOrder=0;
|
||||||
|
oldOrder1=0;
|
||||||
|
oldRow=0;
|
||||||
|
cursor.xCoarse=0;
|
||||||
|
cursor.xFine=0;
|
||||||
|
cursor.y=0;
|
||||||
|
selStart=cursor;
|
||||||
|
selEnd=cursor;
|
||||||
|
curOrder=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_FA_PLUS "##SubSongAdd")) {
|
||||||
|
if (!e->addSubSong()) {
|
||||||
|
showError("too many subsongs!");
|
||||||
|
} else {
|
||||||
|
e->changeSongP(e->song.subsong.size()-1);
|
||||||
|
updateScroll(0);
|
||||||
|
oldOrder=0;
|
||||||
|
oldOrder1=0;
|
||||||
|
oldRow=0;
|
||||||
|
cursor.xCoarse=0;
|
||||||
|
cursor.xFine=0;
|
||||||
|
cursor.y=0;
|
||||||
|
selStart=cursor;
|
||||||
|
selEnd=cursor;
|
||||||
|
curOrder=0;
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_FA_MINUS "##SubSongDel")) {
|
||||||
|
if (!e->removeSubSong(e->getCurrentSubSong())) {
|
||||||
|
showError("this is the only subsong!");
|
||||||
|
} else {
|
||||||
|
undoHist.clear();
|
||||||
|
redoHist.clear();
|
||||||
|
updateScroll(0);
|
||||||
|
oldOrder=0;
|
||||||
|
oldOrder1=0;
|
||||||
|
oldRow=0;
|
||||||
|
cursor.xCoarse=0;
|
||||||
|
cursor.xFine=0;
|
||||||
|
cursor.y=0;
|
||||||
|
selStart=cursor;
|
||||||
|
selEnd=cursor;
|
||||||
|
curOrder=0;
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Name");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::InputText("##SubSongName",&e->curSubSong->name)) {
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS;
|
||||||
|
ImGui::End();
|
||||||
|
}
|
Loading…
Reference in a new issue