mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-15 17:25:06 +00:00
Merge branch 'master' into feature/Moar-patch-bank-support-part3
This commit is contained in:
commit
0e07b745c7
28 changed files with 1206 additions and 611 deletions
|
@ -354,6 +354,7 @@ src/engine/platform/c64.cpp
|
|||
src/engine/platform/arcade.cpp
|
||||
src/engine/platform/tx81z.cpp
|
||||
src/engine/platform/ym2203.cpp
|
||||
src/engine/platform/ym2203ext.cpp
|
||||
src/engine/platform/ym2608.cpp
|
||||
src/engine/platform/ym2610.cpp
|
||||
src/engine/platform/ym2610ext.cpp
|
||||
|
@ -444,6 +445,7 @@ src/gui/settings.cpp
|
|||
src/gui/songInfo.cpp
|
||||
src/gui/songNotes.cpp
|
||||
src/gui/stats.cpp
|
||||
src/gui/subSongs.cpp
|
||||
src/gui/sysConf.cpp
|
||||
src/gui/sysEx.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:
|
||||
|
||||
- 95: Furnace dev95
|
||||
- 94: Furnace dev94
|
||||
- 93: Furnace dev93
|
||||
- 92: Furnace dev92
|
||||
- 91: Furnace dev91
|
||||
- 90: Furnace dev90
|
||||
- 89: Furnace dev89
|
||||
- 88: Furnace dev88
|
||||
|
@ -122,26 +127,26 @@ size | description
|
|||
-----|------------------------------------
|
||||
4 | "INFO" block ID
|
||||
4 | reserved
|
||||
1 | time base
|
||||
1 | speed 1
|
||||
1 | speed 2
|
||||
1 | initial arpeggio time
|
||||
4f | ticks per second
|
||||
1 | time base (of first song)
|
||||
1 | speed 1 (of first song)
|
||||
1 | speed 2 (of first song)
|
||||
1 | initial arpeggio time (of first song)
|
||||
4f | ticks per second (of first song)
|
||||
| - 60 is NTSC
|
||||
| - 50 is PAL
|
||||
2 | pattern length
|
||||
2 | pattern length (of first song)
|
||||
| - the limit is 256.
|
||||
2 | orders length
|
||||
2 | orders length (of first song)
|
||||
| - the limit is 256 (>=80) or 127 (<80).
|
||||
1 | highlight A
|
||||
1 | highlight B
|
||||
1 | highlight A (of first song)
|
||||
1 | highlight B (of first song)
|
||||
2 | instrument count
|
||||
| - the limit is 256.
|
||||
2 | wavetable count
|
||||
| - the limit is 256.
|
||||
2 | sample count
|
||||
| - the limit is 256.
|
||||
4 | pattern count
|
||||
4 | pattern count (global)
|
||||
32 | list of sound chips
|
||||
| - possible soundchips:
|
||||
| - 0x00: end of list
|
||||
|
@ -215,6 +220,7 @@ size | description
|
|||
| - 0xb5: tildearrow Sound Unit - 8 channels
|
||||
| - 0xb6: OPN extended - 9 channels
|
||||
| - 0xb7: PC-98 extended - 19 channels
|
||||
| - 0xb8: YMZ280B - 8 channels
|
||||
| - 0xde: YM2610B extended - 19 channels
|
||||
| - 0xe0: QSound - 19 channels
|
||||
| - 0xfd: Dummy System - 8 channels
|
||||
|
@ -257,20 +263,20 @@ size | description
|
|||
4?? | pointers to wavetables
|
||||
4?? | pointers to samples
|
||||
4?? | pointers to patterns
|
||||
??? | orders
|
||||
??? | orders (of first song)
|
||||
| - a table of bytes
|
||||
| - size=channels*ordLen
|
||||
| - read orders then channels
|
||||
| - the maximum value of a cell is FF (>=80) or 7F (<80).
|
||||
??? | effect columns
|
||||
??? | effect columns (of first song)
|
||||
| - size=channels
|
||||
1?? | channel hide status
|
||||
1?? | channel hide status (of first song)
|
||||
| - size=channels
|
||||
1?? | channel collapse status
|
||||
1?? | channel collapse status (of first song)
|
||||
| - size=channels
|
||||
S?? | channel names
|
||||
S?? | channel names (of first song)
|
||||
| - a list of channelCount C strings
|
||||
S?? | channel short names
|
||||
S?? | channel short names (of first song)
|
||||
| - same as above
|
||||
STR | song comment
|
||||
4f | master volume, 1.0f=100% (>=59)
|
||||
|
@ -291,6 +297,55 @@ size | description
|
|||
1 | pitch macro is linear (>=90) or reserved
|
||||
1 | pitch slide speed in full linear pitch mode (>=94) or 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
|
||||
|
@ -743,6 +798,8 @@ size | description
|
|||
1 | depth
|
||||
| - 0: ZX Spectrum overlay drum (1-bit)
|
||||
| - 1: 1-bit NES DPCM (1-bit)
|
||||
| - 2: AICA ADPCM
|
||||
| - 3: YMZ ADPCM
|
||||
| - 4: QSound ADPCM
|
||||
| - 5: ADPCM-A
|
||||
| - 6: ADPCM-B
|
||||
|
@ -769,7 +826,8 @@ size | description
|
|||
4 | reserved
|
||||
2 | channel
|
||||
2 | pattern index
|
||||
4 | reserved
|
||||
2 | subsong (>=95) or reserved
|
||||
2 | reserved
|
||||
??? | pattern data
|
||||
| - size: rows*(4+effectColumns*2)*2
|
||||
| - read shorts in this order:
|
||||
|
|
|
@ -139,18 +139,18 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
|
|||
int nextRow=0;
|
||||
int effectVal=0;
|
||||
DivPattern* pat[DIV_MAX_CHANS];
|
||||
for (int i=0; i<song.ordersLen; i++) {
|
||||
for (int i=0; i<curSubSong->ordersLen; i++) {
|
||||
for (int j=0; j<chans; j++) {
|
||||
pat[j]=song.pat[j].getPattern(song.orders.ord[j][i],false);
|
||||
pat[j]=curPat[j].getPattern(curOrders->ord[j][i],false);
|
||||
}
|
||||
for (int j=nextRow; j<song.patLen; j++) {
|
||||
for (int j=nextRow; j<curSubSong->patLen; j++) {
|
||||
nextRow=0;
|
||||
for (int k=0; k<chans; k++) {
|
||||
for (int l=0; l<song.pat[k].effectCols; l++) {
|
||||
for (int l=0; l<curPat[k].effectCols; l++) {
|
||||
effectVal=pat[k]->data[j][5+(l<<1)];
|
||||
if (effectVal<0) effectVal=0;
|
||||
if (pat[k]->data[j][4+(l<<1)]==0x0d) {
|
||||
if (nextOrder==-1 && (i<song.ordersLen-1 || !song.ignoreJumpAtEnd)) {
|
||||
if (nextOrder==-1 && (i<curSubSong->ordersLen-1 || !song.ignoreJumpAtEnd)) {
|
||||
nextOrder=i+1;
|
||||
nextRow=effectVal;
|
||||
}
|
||||
|
@ -695,6 +695,7 @@ void DivEngine::createNew(const int* description) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=DivSong();
|
||||
changeSong(0);
|
||||
if (description!=NULL) {
|
||||
initSongWithDesc(description);
|
||||
}
|
||||
|
@ -716,45 +717,55 @@ void DivEngine::swapChannels(int src, int dest) {
|
|||
}
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
||||
song.orders.ord[src][i]^=song.orders.ord[dest][i];
|
||||
song.orders.ord[dest][i]^=song.orders.ord[src][i];
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
|
||||
DivPattern* prev=song.pat[src].data[i];
|
||||
song.pat[src].data[i]=song.pat[dest].data[i];
|
||||
song.pat[dest].data[i]=prev;
|
||||
DivPattern* prev=curPat[src].data[i];
|
||||
curPat[src].data[i]=curPat[dest].data[i];
|
||||
curPat[dest].data[i]=prev;
|
||||
}
|
||||
|
||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
||||
song.pat[dest].effectCols^=song.pat[src].effectCols;
|
||||
song.pat[src].effectCols^=song.pat[dest].effectCols;
|
||||
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||
curPat[dest].effectCols^=curPat[src].effectCols;
|
||||
curPat[src].effectCols^=curPat[dest].effectCols;
|
||||
|
||||
String prevChanName=song.chanName[src];
|
||||
String prevChanShortName=song.chanShortName[src];
|
||||
bool prevChanShow=song.chanShow[src];
|
||||
bool prevChanCollapse=song.chanCollapse[src];
|
||||
String prevChanName=curSubSong->chanName[src];
|
||||
String prevChanShortName=curSubSong->chanShortName[src];
|
||||
bool prevChanShow=curSubSong->chanShow[src];
|
||||
bool prevChanCollapse=curSubSong->chanCollapse[src];
|
||||
|
||||
song.chanName[src]=song.chanName[dest];
|
||||
song.chanShortName[src]=song.chanShortName[dest];
|
||||
song.chanShow[src]=song.chanShow[dest];
|
||||
song.chanCollapse[src]=song.chanCollapse[dest];
|
||||
song.chanName[dest]=prevChanName;
|
||||
song.chanShortName[dest]=prevChanShortName;
|
||||
song.chanShow[dest]=prevChanShow;
|
||||
song.chanCollapse[dest]=prevChanCollapse;
|
||||
curSubSong->chanName[src]=curSubSong->chanName[dest];
|
||||
curSubSong->chanShortName[src]=curSubSong->chanShortName[dest];
|
||||
curSubSong->chanShow[src]=curSubSong->chanShow[dest];
|
||||
curSubSong->chanCollapse[src]=curSubSong->chanCollapse[dest];
|
||||
curSubSong->chanName[dest]=prevChanName;
|
||||
curSubSong->chanShortName[dest]=prevChanShortName;
|
||||
curSubSong->chanShow[dest]=prevChanShow;
|
||||
curSubSong->chanCollapse[dest]=prevChanCollapse;
|
||||
}
|
||||
|
||||
void DivEngine::stompChannel(int ch) {
|
||||
logV("stomping channel %d",ch);
|
||||
for (int i=0; i<256; i++) {
|
||||
song.orders.ord[ch][i]=0;
|
||||
curOrders->ord[ch][i]=0;
|
||||
}
|
||||
song.pat[ch].wipePatterns();
|
||||
song.pat[ch].effectCols=1;
|
||||
song.chanName[ch]="";
|
||||
song.chanShortName[ch]="";
|
||||
song.chanShow[ch]=true;
|
||||
song.chanCollapse[ch]=false;
|
||||
curPat[ch].wipePatterns();
|
||||
curPat[ch].effectCols=1;
|
||||
curSubSong->chanName[ch]="";
|
||||
curSubSong->chanShortName[ch]="";
|
||||
curSubSong->chanShow[ch]=true;
|
||||
curSubSong->chanCollapse[ch]=false;
|
||||
}
|
||||
|
||||
void DivEngine::changeSong(size_t songIndex) {
|
||||
if (songIndex>=song.subsong.size()) return;
|
||||
curSubSong=song.subsong[songIndex];
|
||||
curPat=song.subsong[songIndex]->pat;
|
||||
curOrders=&song.subsong[songIndex]->orders;
|
||||
curSubSongIndex=songIndex;
|
||||
curOrder=0;
|
||||
curRow=0;
|
||||
}
|
||||
|
||||
void DivEngine::swapChannelsP(int src, int dest) {
|
||||
|
@ -767,6 +778,41 @@ void DivEngine::swapChannelsP(int src, int dest) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::changeSongP(size_t index) {
|
||||
if (index>=song.subsong.size()) return;
|
||||
if (index==curSubSongIndex) return;
|
||||
stop();
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
changeSong(index);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
int DivEngine::addSubSong() {
|
||||
if (song.subsong.size()>=127) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong.push_back(new DivSubSong);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.subsong.size()-1;
|
||||
}
|
||||
|
||||
bool DivEngine::removeSubSong(int index) {
|
||||
if (song.subsong.size()<=1) return false;
|
||||
stop();
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong[index]->clearData();
|
||||
delete song.subsong[index];
|
||||
song.subsong.erase(song.subsong.begin()+index);
|
||||
changeSong(0);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||
int chanCount=chans;
|
||||
quitDispatch();
|
||||
|
@ -1321,15 +1367,15 @@ void DivEngine::reset() {
|
|||
}
|
||||
extValue=0;
|
||||
extValuePresent=0;
|
||||
speed1=song.speed1;
|
||||
speed2=song.speed2;
|
||||
speed1=curSubSong->speed1;
|
||||
speed2=curSubSong->speed2;
|
||||
firstTick=false;
|
||||
nextSpeed=speed1;
|
||||
divider=60;
|
||||
if (song.customTempo) {
|
||||
divider=song.hz;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
} else {
|
||||
if (song.pal) {
|
||||
if (curSubSong->pal) {
|
||||
divider=60;
|
||||
} else {
|
||||
divider=50;
|
||||
|
@ -1476,6 +1522,10 @@ int DivEngine::getRow() {
|
|||
return curRow;
|
||||
}
|
||||
|
||||
size_t DivEngine::getCurrentSubSong() {
|
||||
return curSubSongIndex;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed1() {
|
||||
return speed1;
|
||||
}
|
||||
|
@ -1485,9 +1535,9 @@ unsigned char DivEngine::getSpeed2() {
|
|||
}
|
||||
|
||||
float DivEngine::getHz() {
|
||||
if (song.customTempo) {
|
||||
return song.hz;
|
||||
} else if (song.pal) {
|
||||
if (curSubSong->customTempo) {
|
||||
return curSubSong->hz;
|
||||
} else if (curSubSong->pal) {
|
||||
return 60.0;
|
||||
} else {
|
||||
return 50.0;
|
||||
|
@ -1594,6 +1644,7 @@ void DivEngine::unmuteAll() {
|
|||
}
|
||||
|
||||
int DivEngine::addInstrument(int refChan) {
|
||||
if (song.ins.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
int insCount=(int)song.ins.size();
|
||||
|
@ -1624,6 +1675,10 @@ int DivEngine::addInstrument(int refChan) {
|
|||
}
|
||||
|
||||
int DivEngine::addInstrumentPtr(DivInstrument* which) {
|
||||
if (song.ins.size()>=256) {
|
||||
delete which;
|
||||
return -1;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.ins.push_back(which);
|
||||
|
@ -1654,10 +1709,10 @@ void DivEngine::delInstrument(int index) {
|
|||
song.insLen=song.ins.size();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<256; j++) {
|
||||
if (song.pat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
if (song.pat[i].data[j]->data[k][2]>index) {
|
||||
song.pat[i].data[j]->data[k][2]--;
|
||||
if (curPat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<curSubSong->patLen; k++) {
|
||||
if (curPat[i].data[j]->data[k][2]>index) {
|
||||
curPat[i].data[j]->data[k][2]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1668,6 +1723,7 @@ void DivEngine::delInstrument(int index) {
|
|||
}
|
||||
|
||||
int DivEngine::addWave() {
|
||||
if (song.wave.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
DivWavetable* wave=new DivWavetable;
|
||||
|
@ -1680,37 +1736,48 @@ int DivEngine::addWave() {
|
|||
}
|
||||
|
||||
bool DivEngine::addWaveFromFile(const char* path) {
|
||||
if (song.wave.size()>=256) {
|
||||
lastError="too many wavetables!";
|
||||
return false;
|
||||
}
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
lastError=fmt::sprintf("%s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
unsigned char* buf;
|
||||
ssize_t len;
|
||||
if (fseek(f,0,SEEK_END)!=0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not seek to end: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
len=ftell(f);
|
||||
if (len<0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not determine file size: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
lastError="file size is invalid!";
|
||||
return false;
|
||||
}
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
lastError="file is empty";
|
||||
return false;
|
||||
}
|
||||
if (fseek(f,0,SEEK_SET)!=0) {
|
||||
fclose(f);
|
||||
lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
buf=new unsigned char[len];
|
||||
if (fread(buf,1,len,f)!=(size_t)len) {
|
||||
logW("did not read entire wavetable file buffer!");
|
||||
delete[] buf;
|
||||
lastError=fmt::sprintf("could not read entire file: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
|
@ -1790,6 +1857,7 @@ bool DivEngine::addWaveFromFile(const char* path) {
|
|||
} catch (EndOfFileException& e) {
|
||||
delete wave;
|
||||
delete[] buf;
|
||||
lastError="premature end of file";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1816,6 +1884,7 @@ void DivEngine::delWave(int index) {
|
|||
}
|
||||
|
||||
int DivEngine::addSample() {
|
||||
if (song.sample.size()>=256) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
DivSample* sample=new DivSample;
|
||||
|
@ -1830,6 +1899,10 @@ int DivEngine::addSample() {
|
|||
}
|
||||
|
||||
int DivEngine::addSampleFromFile(const char* path) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return -1;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
|
@ -2025,18 +2098,18 @@ void DivEngine::delSample(int index) {
|
|||
|
||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (song.ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
order[i]=song.orders.ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
}
|
||||
} else {
|
||||
bool used[256];
|
||||
for (int i=0; i<chans; i++) {
|
||||
memset(used,0,sizeof(bool)*256);
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
used[song.orders.ord[i][j]]=true;
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
used[curOrders->ord[i][j]]=true;
|
||||
}
|
||||
order[i]=0xff;
|
||||
for (int j=0; j<256; j++) {
|
||||
|
@ -2050,19 +2123,19 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
if (where) { // at the end
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][song.ordersLen]=order[i];
|
||||
curOrders->ord[i][curSubSong->ordersLen]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
song.orders.ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (playing && !freelance) {
|
||||
|
@ -2074,21 +2147,21 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (song.ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
warnings="";
|
||||
BUSY_BEGIN_SOFT;
|
||||
for (int i=0; i<chans; i++) {
|
||||
bool didNotFind=true;
|
||||
logD("channel %d",i);
|
||||
order[i]=song.orders.ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
// find free slot
|
||||
for (int j=0; j<256; j++) {
|
||||
logD("finding free slot in %d...",j);
|
||||
if (song.pat[i].data[j]==NULL) {
|
||||
if (curPat[i].data[j]==NULL) {
|
||||
int origOrd=order[i];
|
||||
order[i]=j;
|
||||
DivPattern* oldPat=song.pat[i].getPattern(origOrd,false);
|
||||
DivPattern* pat=song.pat[i].getPattern(j,true);
|
||||
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||
DivPattern* pat=curPat[i].getPattern(j,true);
|
||||
memcpy(pat->data,oldPat->data,256*32*sizeof(short));
|
||||
logD("found at %d",j);
|
||||
didNotFind=false;
|
||||
|
@ -2102,19 +2175,19 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
if (where) { // at the end
|
||||
saveLock.lock();
|
||||
for (int i=0; i<chans; i++) {
|
||||
song.orders.ord[i][song.ordersLen]=order[i];
|
||||
curOrders->ord[i][curSubSong->ordersLen]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=song.ordersLen; j>curOrder; j--) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j-1];
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
song.orders.ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
}
|
||||
song.ordersLen++;
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (playing && !freelance) {
|
||||
|
@ -2125,17 +2198,17 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
}
|
||||
|
||||
void DivEngine::deleteOrder() {
|
||||
if (song.ordersLen<=1) return;
|
||||
if (curSubSong->ordersLen<=1) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=curOrder; j<song.ordersLen; j++) {
|
||||
song.orders.ord[i][j]=song.orders.ord[i][j+1];
|
||||
for (int j=curOrder; j<curSubSong->ordersLen; j++) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j+1];
|
||||
}
|
||||
}
|
||||
song.ordersLen--;
|
||||
curSubSong->ordersLen--;
|
||||
saveLock.unlock();
|
||||
if (curOrder>=song.ordersLen) curOrder=song.ordersLen-1;
|
||||
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
@ -2150,9 +2223,9 @@ void DivEngine::moveOrderUp() {
|
|||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
||||
song.orders.ord[i][curOrder-1]^=song.orders.ord[i][curOrder];
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder--;
|
||||
|
@ -2164,15 +2237,15 @@ void DivEngine::moveOrderUp() {
|
|||
|
||||
void DivEngine::moveOrderDown() {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder>=song.ordersLen-1) {
|
||||
if (curOrder>=curSubSong->ordersLen-1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
||||
song.orders.ord[i][curOrder+1]^=song.orders.ord[i][curOrder];
|
||||
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
|
@ -2185,12 +2258,12 @@ void DivEngine::moveOrderDown() {
|
|||
void DivEngine::exchangeIns(int one, int two) {
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<256; j++) {
|
||||
if (song.pat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
if (song.pat[i].data[j]->data[k][2]==one) {
|
||||
song.pat[i].data[j]->data[k][2]=two;
|
||||
} else if (song.pat[i].data[j]->data[k][2]==two) {
|
||||
song.pat[i].data[j]->data[k][2]=one;
|
||||
if (curPat[i].data[j]==NULL) continue;
|
||||
for (int k=0; k<curSubSong->patLen; k++) {
|
||||
if (curPat[i].data[j]->data[k][2]==one) {
|
||||
curPat[i].data[j]->data[k][2]=two;
|
||||
} else if (curPat[i].data[j]->data[k][2]==two) {
|
||||
curPat[i].data[j]->data[k][2]=one;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2394,7 +2467,7 @@ void DivEngine::autoNoteOffAll() {
|
|||
void DivEngine::setOrder(unsigned char order) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
curOrder=order;
|
||||
if (order>=song.ordersLen) curOrder=0;
|
||||
if (order>=curSubSong->ordersLen) curOrder=0;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
@ -2417,15 +2490,15 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
|
|||
void DivEngine::setSongRate(float hz, bool pal) {
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.pal=!pal;
|
||||
song.hz=hz;
|
||||
curSubSong->pal=!pal;
|
||||
curSubSong->hz=hz;
|
||||
// what?
|
||||
song.customTempo=true;
|
||||
curSubSong->customTempo=true;
|
||||
divider=60;
|
||||
if (song.customTempo) {
|
||||
divider=song.hz;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
} else {
|
||||
if (song.pal) {
|
||||
if (curSubSong->pal) {
|
||||
divider=60;
|
||||
} else {
|
||||
divider=50;
|
||||
|
@ -2868,5 +2941,6 @@ bool DivEngine::quit() {
|
|||
if (yrw801ROM!=NULL) delete[] yrw801ROM;
|
||||
if (tg100ROM!=NULL) delete[] tg100ROM;
|
||||
if (mu5ROM!=NULL) delete[] mu5ROM;
|
||||
song.unload();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev94"
|
||||
#define DIV_ENGINE_VERSION 94
|
||||
#define DIV_VERSION "dev95"
|
||||
#define DIV_ENGINE_VERSION 95
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
@ -304,6 +304,7 @@ class DivEngine {
|
|||
bool hasLoadedSomething;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
|
||||
size_t curSubSongIndex;
|
||||
double divider;
|
||||
int cycles;
|
||||
double clockDrift;
|
||||
|
@ -414,8 +415,14 @@ class DivEngine {
|
|||
void swapChannels(int src, int dest);
|
||||
void stompChannel(int ch);
|
||||
|
||||
// change song (UNSAFE)
|
||||
void changeSong(size_t songIndex);
|
||||
|
||||
public:
|
||||
DivSong song;
|
||||
DivOrders* curOrders;
|
||||
DivChannelData* curPat;
|
||||
DivSubSong* curSubSong;
|
||||
DivInstrument* tempIns;
|
||||
DivSystem sysOfChan[DIV_MAX_CHANS];
|
||||
int dispatchOfChan[DIV_MAX_CHANS];
|
||||
|
@ -606,6 +613,9 @@ class DivEngine {
|
|||
// get current row
|
||||
int getRow();
|
||||
|
||||
// get current subsong
|
||||
size_t getCurrentSubSong();
|
||||
|
||||
// get speed 1
|
||||
unsigned char getSpeed1();
|
||||
|
||||
|
@ -802,6 +812,15 @@ class DivEngine {
|
|||
// public swap channels
|
||||
void swapChannelsP(int src, int dest);
|
||||
|
||||
// public change song
|
||||
void changeSongP(size_t index);
|
||||
|
||||
// add subsong
|
||||
int addSubSong();
|
||||
|
||||
// remove subsong
|
||||
bool removeSubSong(int index);
|
||||
|
||||
// change system
|
||||
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
||||
|
||||
|
@ -903,6 +922,7 @@ class DivEngine {
|
|||
curOrder(0),
|
||||
remainingLoops(-1),
|
||||
nextSpeed(3),
|
||||
curSubSongIndex(0),
|
||||
divider(60),
|
||||
cycles(0),
|
||||
clockDrift(0),
|
||||
|
@ -938,6 +958,8 @@ class DivEngine {
|
|||
metroAmp(0.0f),
|
||||
metroVol(1.0f),
|
||||
totalProcessed(0),
|
||||
curOrders(NULL),
|
||||
curPat(NULL),
|
||||
tempIns(NULL),
|
||||
oscBuf{NULL,NULL},
|
||||
oscSize(1),
|
||||
|
@ -962,6 +984,8 @@ class DivEngine {
|
|||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||
}
|
||||
|
||||
changeSong(0);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -184,57 +184,57 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
logI("reading module data...");
|
||||
if (ds.version>0x0c) {
|
||||
ds.hilightA=reader.readC();
|
||||
ds.hilightB=reader.readC();
|
||||
ds.subsong[0]->hilightA=reader.readC();
|
||||
ds.subsong[0]->hilightB=reader.readC();
|
||||
}
|
||||
|
||||
ds.timeBase=reader.readC();
|
||||
ds.speed1=reader.readC();
|
||||
if (ds.version>0x05) {
|
||||
ds.speed2=reader.readC();
|
||||
ds.pal=reader.readC();
|
||||
ds.hz=(ds.pal)?60:50;
|
||||
ds.customTempo=reader.readC();
|
||||
ds.subsong[0]->timeBase=reader.readC();
|
||||
ds.subsong[0]->speed1=reader.readC();
|
||||
if (ds.version>0x07) {
|
||||
ds.subsong[0]->speed2=reader.readC();
|
||||
ds.subsong[0]->pal=reader.readC();
|
||||
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||
ds.subsong[0]->customTempo=reader.readC();
|
||||
} else {
|
||||
ds.speed2=ds.speed1;
|
||||
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
||||
}
|
||||
if (ds.version>0x0a) {
|
||||
String hz=reader.readString(3);
|
||||
if (ds.customTempo) {
|
||||
if (ds.subsong[0]->customTempo) {
|
||||
try {
|
||||
ds.hz=std::stoi(hz);
|
||||
ds.subsong[0]->hz=std::stoi(hz);
|
||||
} catch (std::exception& e) {
|
||||
logW("invalid custom Hz!");
|
||||
ds.hz=60;
|
||||
ds.subsong[0]->hz=60;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ds.version>0x17) {
|
||||
ds.patLen=reader.readI();
|
||||
ds.subsong[0]->patLen=reader.readI();
|
||||
} else {
|
||||
ds.patLen=(unsigned char)reader.readC();
|
||||
ds.subsong[0]->patLen=(unsigned char)reader.readC();
|
||||
}
|
||||
ds.ordersLen=(unsigned char)reader.readC();
|
||||
ds.subsong[0]->ordersLen=(unsigned char)reader.readC();
|
||||
|
||||
if (ds.patLen<0) {
|
||||
if (ds.subsong[0]->patLen<0) {
|
||||
logE("pattern length is negative!");
|
||||
lastError="pattern lengrh is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.patLen>256) {
|
||||
if (ds.subsong[0]->patLen>256) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen<0) {
|
||||
if (ds.subsong[0]->ordersLen<0) {
|
||||
logE("song length is negative!");
|
||||
lastError="song length is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen>127) {
|
||||
if (ds.subsong[0]->ordersLen>127) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
@ -242,54 +242,54 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
if (ds.version<20 && ds.version>3) {
|
||||
ds.arpLen=reader.readC();
|
||||
ds.subsong[0]->arpLen=reader.readC();
|
||||
} else {
|
||||
ds.arpLen=1;
|
||||
ds.subsong[0]->arpLen=1;
|
||||
}
|
||||
|
||||
if (ds.system[0]==DIV_SYSTEM_YMU759) {
|
||||
switch (ds.timeBase) {
|
||||
switch (ds.subsong[0]->timeBase) {
|
||||
case 0:
|
||||
ds.hz=248;
|
||||
ds.subsong[0]->hz=248;
|
||||
break;
|
||||
case 1:
|
||||
ds.hz=200;
|
||||
ds.subsong[0]->hz=200;
|
||||
break;
|
||||
case 2:
|
||||
ds.hz=100;
|
||||
ds.subsong[0]->hz=100;
|
||||
break;
|
||||
case 3:
|
||||
ds.hz=50;
|
||||
ds.subsong[0]->hz=50;
|
||||
break;
|
||||
case 4:
|
||||
ds.hz=25;
|
||||
ds.subsong[0]->hz=25;
|
||||
break;
|
||||
case 5:
|
||||
ds.hz=20;
|
||||
ds.subsong[0]->hz=20;
|
||||
break;
|
||||
default:
|
||||
ds.hz=248;
|
||||
ds.subsong[0]->hz=248;
|
||||
break;
|
||||
}
|
||||
ds.customTempo=true;
|
||||
ds.timeBase=0;
|
||||
ds.subsong[0]->customTempo=true;
|
||||
ds.subsong[0]->timeBase=0;
|
||||
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
|
||||
}
|
||||
|
||||
logV("%x",reader.tell());
|
||||
|
||||
logI("reading pattern matrix (%d * %d = %d)...",ds.ordersLen,getChannelCount(ds.system[0]),ds.ordersLen*getChannelCount(ds.system[0]));
|
||||
logI("reading pattern matrix (%d * %d = %d)...",ds.subsong[0]->ordersLen,getChannelCount(ds.system[0]),ds.subsong[0]->ordersLen*getChannelCount(ds.system[0]));
|
||||
for (int i=0; i<getChannelCount(ds.system[0]); i++) {
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
ds.orders.ord[i][j]=reader.readC();
|
||||
if (ds.orders.ord[i][j]>0x7f) {
|
||||
logE("order at %d, %d out of range! (%d)",i,j,ds.orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order at %d, %d out of range! (%d)",i,j,ds.orders.ord[i][j]);
|
||||
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||
ds.subsong[0]->orders.ord[i][j]=reader.readC();
|
||||
if (ds.subsong[0]->orders.ord[i][j]>0x7f) {
|
||||
logE("order at %d, %d out of range! (%d)",i,j,ds.subsong[0]->orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order at %d, %d out of range! (%d)",i,j,ds.subsong[0]->orders.ord[i][j]);
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.version>0x18) { // 1.1 pattern names
|
||||
ds.pat[i].getPattern(j,true)->name=reader.readString((unsigned char)reader.readC());
|
||||
ds.subsong[0]->pat[i].getPattern(j,true)->name=reader.readString((unsigned char)reader.readC());
|
||||
}
|
||||
}
|
||||
if (ds.version>0x03 && ds.version<0x06 && i<16) {
|
||||
|
@ -637,9 +637,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
logV("%x",reader.tell());
|
||||
|
||||
logI("reading patterns (%d channels, %d orders)...",getChannelCount(ds.system[0]),ds.ordersLen);
|
||||
logI("reading patterns (%d channels, %d orders)...",getChannelCount(ds.system[0]),ds.subsong[0]->ordersLen);
|
||||
for (int i=0; i<getChannelCount(ds.system[0]); i++) {
|
||||
DivChannelData& chan=ds.pat[i];
|
||||
DivChannelData& chan=ds.subsong[0]->pat[i];
|
||||
if (ds.version<0x0a) {
|
||||
chan.effectCols=1;
|
||||
} else {
|
||||
|
@ -652,10 +652,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
DivPattern* pat=chan.getPattern(ds.orders.ord[i][j],true);
|
||||
for (int j=0; j<ds.subsong[0]->ordersLen; j++) {
|
||||
DivPattern* pat=chan.getPattern(ds.subsong[0]->orders.ord[i][j],true);
|
||||
if (ds.version>0x08) { // current pattern format
|
||||
for (int k=0; k<ds.patLen; k++) {
|
||||
for (int k=0; k<ds.subsong[0]->patLen; k++) {
|
||||
// note
|
||||
pat->data[k][0]=reader.readS();
|
||||
// octave
|
||||
|
@ -723,7 +723,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
} else { // historic pattern format
|
||||
if (i<16) pat->data[0][2]=historicColIns[i];
|
||||
for (int k=0; k<ds.patLen; k++) {
|
||||
for (int k=0; k<ds.subsong[0]->patLen; k++) {
|
||||
// note
|
||||
pat->data[k][0]=reader.readC();
|
||||
// octave
|
||||
|
@ -788,7 +788,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
pitch=reader.readC();
|
||||
vol=reader.readC();
|
||||
}
|
||||
if (ds.version<=0x05) {
|
||||
if (ds.version<=0x08) {
|
||||
sample->rate=ymuSampleRate*400;
|
||||
}
|
||||
if (ds.version>0x15) {
|
||||
|
@ -798,15 +798,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
sample->depth=16;
|
||||
}
|
||||
} else {
|
||||
if (ds.version>0x05) {
|
||||
if (ds.version>0x08) {
|
||||
sample->depth=16;
|
||||
} else {
|
||||
// it appears samples were stored as ADPCM back then
|
||||
sample->depth=6;
|
||||
sample->depth=3;
|
||||
}
|
||||
}
|
||||
if (length>0) {
|
||||
if (ds.version>0x05) {
|
||||
if (ds.version>0x08) {
|
||||
if (ds.version<0x0b) {
|
||||
data=new short[1+(length/2)];
|
||||
reader.read(data,length);
|
||||
|
@ -842,8 +842,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
|
||||
delete[] data;
|
||||
} else {
|
||||
// ADPCM?
|
||||
// it appears to be a slightly modified version of ADPCM-B!
|
||||
// YMZ ADPCM
|
||||
adpcmData=new unsigned char[length];
|
||||
logV("%x",reader.tell());
|
||||
reader.read(adpcmData,length);
|
||||
|
@ -854,7 +853,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
logE("%d: error while initializing sample!",i);
|
||||
}
|
||||
|
||||
memcpy(sample->dataB,adpcmData,length);
|
||||
memcpy(sample->dataZ,adpcmData,length);
|
||||
delete[] adpcmData;
|
||||
}
|
||||
}
|
||||
|
@ -906,6 +905,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -930,13 +930,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
unsigned int insPtr[256];
|
||||
unsigned int wavePtr[256];
|
||||
unsigned int samplePtr[256];
|
||||
unsigned int subSongPtr[256];
|
||||
std::vector<int> patPtr;
|
||||
int numberOfSubSongs=0;
|
||||
char magic[5];
|
||||
memset(magic,0,5);
|
||||
SafeReader reader=SafeReader(file,len);
|
||||
warnings="";
|
||||
try {
|
||||
DivSong ds;
|
||||
DivSubSong* subSong=ds.subsong[0];
|
||||
|
||||
if (!reader.seek(16,SEEK_SET)) {
|
||||
logE("premature end of file!");
|
||||
|
@ -1046,44 +1049,44 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
reader.readI();
|
||||
|
||||
ds.timeBase=reader.readC();
|
||||
ds.speed1=reader.readC();
|
||||
ds.speed2=reader.readC();
|
||||
ds.arpLen=reader.readC();
|
||||
ds.hz=reader.readF();
|
||||
ds.pal=(ds.hz>=53);
|
||||
ds.customTempo=true;
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
subSong->customTempo=true;
|
||||
|
||||
ds.patLen=reader.readS();
|
||||
ds.ordersLen=reader.readS();
|
||||
subSong->patLen=reader.readS();
|
||||
subSong->ordersLen=reader.readS();
|
||||
|
||||
ds.hilightA=reader.readC();
|
||||
ds.hilightB=reader.readC();
|
||||
subSong->hilightA=reader.readC();
|
||||
subSong->hilightB=reader.readC();
|
||||
|
||||
ds.insLen=reader.readS();
|
||||
ds.waveLen=reader.readS();
|
||||
ds.sampleLen=reader.readS();
|
||||
int numberOfPats=reader.readI();
|
||||
|
||||
if (ds.patLen<0) {
|
||||
if (subSong->patLen<0) {
|
||||
logE("pattern length is negative!");
|
||||
lastError="pattern lengrh is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.patLen>256) {
|
||||
if (subSong->patLen>256) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen<0) {
|
||||
if (subSong->ordersLen<0) {
|
||||
logE("song length is negative!");
|
||||
lastError="song length is negative!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (ds.ordersLen>256) {
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
@ -1296,18 +1299,18 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.read(samplePtr,ds.sampleLen*4);
|
||||
for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
|
||||
|
||||
logD("reading orders (%d)...",ds.ordersLen);
|
||||
logD("reading orders (%d)...",subSong->ordersLen);
|
||||
for (int i=0; i<tchans; i++) {
|
||||
for (int j=0; j<ds.ordersLen; j++) {
|
||||
ds.orders.ord[i][j]=reader.readC();
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
subSong->orders.ord[i][j]=reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.pat[i].effectCols=reader.readC();
|
||||
if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) {
|
||||
logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectCols);
|
||||
subSong->pat[i].effectCols=reader.readC();
|
||||
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>8) {
|
||||
logE("channel %d has zero or too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
@ -1315,25 +1318,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=39) {
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanShow[i]=reader.readC();
|
||||
subSong->chanShow[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanCollapse[i]=reader.readC();
|
||||
subSong->chanCollapse[i]=reader.readC();
|
||||
}
|
||||
|
||||
if (ds.version<92) {
|
||||
for (int i=0; i<tchans; i++) {
|
||||
if (ds.chanCollapse[i]>0) ds.chanCollapse[i]=3;
|
||||
if (subSong->chanCollapse[i]>0) subSong->chanCollapse[i]=3;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanName[i]=reader.readString();
|
||||
subSong->chanName[i]=reader.readString();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
ds.chanShortName[i]=reader.readString();
|
||||
subSong->chanShortName[i]=reader.readString();
|
||||
}
|
||||
|
||||
ds.notes=reader.readString();
|
||||
|
@ -1406,6 +1409,88 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// subsongs
|
||||
if (ds.version>=95) {
|
||||
subSong->name=reader.readString();
|
||||
subSong->notes=reader.readString();
|
||||
numberOfSubSongs=(unsigned char)reader.readC();
|
||||
reader.readC(); // reserved
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
// pointers
|
||||
for (int i=0; i<numberOfSubSongs; i++) {
|
||||
subSongPtr[i]=reader.readI();
|
||||
}
|
||||
|
||||
for (int i=0; i<numberOfSubSongs; i++) {
|
||||
ds.subsong.push_back(new DivSubSong);
|
||||
if (!reader.seek(subSongPtr[i],SEEK_SET)) {
|
||||
logE("couldn't seek to subsong %d!",i+1);
|
||||
lastError=fmt::sprintf("couldn't seek to subsong %d!",i+1);
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
reader.read(magic,4);
|
||||
if (strcmp(magic,"SONG")!=0) {
|
||||
logE("%d: invalid subsong header!",i);
|
||||
lastError="invalid subsong header!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
reader.readI();
|
||||
|
||||
subSong=ds.subsong[i+1];
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
subSong->customTempo=true;
|
||||
|
||||
subSong->patLen=reader.readS();
|
||||
subSong->ordersLen=reader.readS();
|
||||
|
||||
subSong->hilightA=reader.readC();
|
||||
subSong->hilightB=reader.readC();
|
||||
|
||||
reader.readI(); // reserved
|
||||
|
||||
subSong->name=reader.readString();
|
||||
subSong->notes=reader.readString();
|
||||
|
||||
logD("reading orders of subsong %d (%d)...",i+1,subSong->ordersLen);
|
||||
for (int j=0; j<tchans; j++) {
|
||||
for (int k=0; k<subSong->ordersLen; k++) {
|
||||
subSong->orders.ord[j][k]=reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->pat[i].effectCols=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanShow[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanCollapse[i]=reader.readC();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanName[i]=reader.readString();
|
||||
}
|
||||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanShortName[i]=reader.readString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read instruments
|
||||
for (int i=0; i<ds.insLen; i++) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
|
@ -1569,9 +1654,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
int chan=reader.readS();
|
||||
int index=reader.readS();
|
||||
reader.readI();
|
||||
int subs=0;
|
||||
if (ds.version>=95) {
|
||||
subs=reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
reader.readS();
|
||||
|
||||
logD("- %d, %d",chan,index);
|
||||
logD("- %d, %d, %d",subs,chan,index);
|
||||
|
||||
if (chan<0 || chan>=tchans) {
|
||||
logE("pattern channel out of range!",i);
|
||||
|
@ -1587,14 +1678,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subs<0 || subs>=(int)ds.subsong.size()) {
|
||||
logE("pattern subsong out of range!",i);
|
||||
lastError="pattern subsong out of range!";
|
||||
ds.unload();
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
DivPattern* pat=ds.pat[chan].getPattern(index,true);
|
||||
for (int j=0; j<ds.patLen; j++) {
|
||||
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
|
||||
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
|
||||
pat->data[j][0]=reader.readS();
|
||||
pat->data[j][1]=reader.readS();
|
||||
pat->data[j][2]=reader.readS();
|
||||
pat->data[j][3]=reader.readS();
|
||||
for (int k=0; k<ds.pat[chan].effectCols; k++) {
|
||||
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
|
||||
pat->data[j][4+(k<<1)]=reader.readS();
|
||||
pat->data[j][5+(k<<1)]=reader.readS();
|
||||
}
|
||||
|
@ -1616,6 +1714,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -1741,8 +1840,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
ds.sampleLen=ds.sample.size();
|
||||
|
||||
// orders
|
||||
ds.ordersLen=ordCount=reader.readC();
|
||||
if (ds.ordersLen<1 || ds.ordersLen>127) {
|
||||
ds.subsong[0]->ordersLen=ordCount=reader.readC();
|
||||
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) {
|
||||
logD("invalid order count!");
|
||||
throw EndOfFileException(&reader,reader.tell());
|
||||
}
|
||||
|
@ -1762,7 +1861,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
unsigned char pat=reader.readC();
|
||||
if (pat>patMax) patMax=pat;
|
||||
for (int j=0; j<chCount; j++) {
|
||||
ds.orders.ord[j][i]=pat;
|
||||
ds.subsong[0]->orders.ord[j][i]=pat;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1779,7 +1878,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// patterns
|
||||
ds.patLen=64;
|
||||
ds.subsong[0]->patLen=64;
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
for (int i=0; i<5; i++) {
|
||||
fxUsage[ch][i]=false;
|
||||
|
@ -1788,7 +1887,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
for (int pat=0; pat<=patMax; pat++) {
|
||||
DivPattern* chpats[DIV_MAX_CHANS];
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
chpats[ch]=ds.pat[ch].getPattern(pat,true);
|
||||
chpats[ch]=ds.subsong[0]->pat[ch].getPattern(pat,true);
|
||||
}
|
||||
for (int row=0; row<64; row++) {
|
||||
for (int ch=0; ch<chCount; ch++) {
|
||||
|
@ -1861,7 +1960,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
for (int ch=0; ch<=chCount; ch++) {
|
||||
unsigned char fxCols=1;
|
||||
for (int pat=0; pat<=patMax; pat++) {
|
||||
auto* data=ds.pat[ch].getPattern(pat,true)->data;
|
||||
auto* data=ds.subsong[0]->pat[ch].getPattern(pat,true)->data;
|
||||
short lastPitchEffect=-1;
|
||||
short lastEffectState[5]={-1,-1,-1,-1,-1};
|
||||
short setEffectState[5]={-1,-1,-1,-1,-1};
|
||||
|
@ -1989,25 +2088,25 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
}
|
||||
ds.pat[ch].effectCols=fxCols;
|
||||
ds.subsong[0]->pat[ch].effectCols=fxCols;
|
||||
}
|
||||
|
||||
ds.pal=false;
|
||||
ds.hz=50;
|
||||
ds.customTempo=false;
|
||||
ds.subsong[0]->pal=false;
|
||||
ds.subsong[0]->hz=50;
|
||||
ds.subsong[0]->customTempo=false;
|
||||
ds.systemLen=(chCount+3)/4;
|
||||
for(int i=0; i<ds.systemLen; i++) {
|
||||
ds.system[i]=DIV_SYSTEM_AMIGA;
|
||||
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
||||
}
|
||||
for(int i=0; i<chCount; i++) {
|
||||
ds.chanShow[i]=true;
|
||||
ds.chanName[i]=fmt::sprintf("Channel %d",i+1);
|
||||
ds.chanShortName[i]=fmt::sprintf("C%d",i+1);
|
||||
ds.subsong[0]->chanShow[i]=true;
|
||||
ds.subsong[0]->chanName[i]=fmt::sprintf("Channel %d",i+1);
|
||||
ds.subsong[0]->chanShortName[i]=fmt::sprintf("C%d",i+1);
|
||||
}
|
||||
for(int i=chCount; i<ds.systemLen*4; i++) {
|
||||
ds.pat[i].effectCols=1;
|
||||
ds.chanShow[i]=false;
|
||||
ds.subsong[0]->pat[i].effectCols=1;
|
||||
ds.subsong[0]->chanShow[i]=false;
|
||||
}
|
||||
|
||||
// instrument creation
|
||||
|
@ -2025,6 +2124,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
saveLock.lock();
|
||||
song.unload();
|
||||
song=ds;
|
||||
changeSong(0);
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
@ -2119,8 +2219,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
newVibrato=reader.readI();
|
||||
}
|
||||
if (blockVersion>=4) {
|
||||
ds.hilightA=reader.readI();
|
||||
ds.hilightB=reader.readI();
|
||||
ds.subsong[0]->hilightA=reader.readI();
|
||||
ds.subsong[0]->hilightB=reader.readI();
|
||||
}
|
||||
if (expansions&8) if (blockVersion>=5) { // N163 channels
|
||||
n163Chans=reader.readI();
|
||||
|
@ -2136,12 +2236,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
logV("custom Hz: %d",customHz);
|
||||
logV("new vibrato: %d",newVibrato);
|
||||
logV("N163 channels: %d",n163Chans);
|
||||
logV("highlight 1: %d",ds.hilightA);
|
||||
logV("highlight 2: %d",ds.hilightB);
|
||||
logV("highlight 1: %d",ds.subsong[0]->hilightA);
|
||||
logV("highlight 2: %d",ds.subsong[0]->hilightB);
|
||||
logV("split point: %d",speedSplitPoint);
|
||||
|
||||
if (customHz!=0) {
|
||||
ds.hz=customHz;
|
||||
ds.subsong[0]->hz=customHz;
|
||||
}
|
||||
|
||||
// initialize channels
|
||||
|
@ -2202,7 +2302,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
for (int j=0; j<=totalSongs; j++) {
|
||||
unsigned char effectCols=reader.readC();
|
||||
if (j==0) {
|
||||
ds.pat[i].effectCols=effectCols+1;
|
||||
ds.subsong[0]->pat[i].effectCols=effectCols+1;
|
||||
}
|
||||
logV("- song %d has %d effect columns",j,effectCols);
|
||||
}
|
||||
|
@ -2533,15 +2633,55 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
|||
return false;
|
||||
}
|
||||
|
||||
struct PatToWrite {
|
||||
unsigned short subsong, chan, pat;
|
||||
PatToWrite(unsigned short s, unsigned short c, unsigned short p):
|
||||
subsong(s),
|
||||
chan(c),
|
||||
pat(p) {}
|
||||
};
|
||||
|
||||
SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||
saveLock.lock();
|
||||
int insPtr[256];
|
||||
int wavePtr[256];
|
||||
int samplePtr[256];
|
||||
std::vector<int> subSongPtr;
|
||||
std::vector<int> insPtr;
|
||||
std::vector<int> wavePtr;
|
||||
std::vector<int> samplePtr;
|
||||
std::vector<int> patPtr;
|
||||
size_t ptrSeek;
|
||||
size_t ptrSeek, subSongPtrSeek;
|
||||
size_t subSongIndex=0;
|
||||
DivSubSong* subSong=song.subsong[subSongIndex];
|
||||
warnings="";
|
||||
|
||||
// fail if values are out of range
|
||||
/*
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("maximum song length is 256!");
|
||||
lastError="maximum song length is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
logE("maximum pattern length is 256!");
|
||||
lastError="maximum pattern length is 256";
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
if (song.ins.size()>256) {
|
||||
logE("maximum number of instruments is 256!");
|
||||
lastError="maximum number of instruments is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (song.wave.size()>256) {
|
||||
logE("maximum number of wavetables is 256!");
|
||||
lastError="maximum number of wavetables is 256";
|
||||
return NULL;
|
||||
}
|
||||
if (song.sample.size()>256) {
|
||||
logE("maximum number of samples is 256!");
|
||||
lastError="maximum number of samples is 256";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!notPrimary) {
|
||||
song.isDMF=false;
|
||||
song.version=DIV_ENGINE_VERSION;
|
||||
|
@ -2568,14 +2708,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
// high short is channel
|
||||
// low short is pattern number
|
||||
std::vector<int> patsToWrite;
|
||||
std::vector<PatToWrite> patsToWrite;
|
||||
bool alreadyAdded[256];
|
||||
for (int i=0; i<chans; i++) {
|
||||
memset(alreadyAdded,0,256*sizeof(bool));
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
if (alreadyAdded[song.orders.ord[i][j]]) continue;
|
||||
patsToWrite.push_back((i<<16)|song.orders.ord[i][j]);
|
||||
alreadyAdded[song.orders.ord[i][j]]=true;
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
DivSubSong* subs=song.subsong[j];
|
||||
memset(alreadyAdded,0,256*sizeof(bool));
|
||||
for (int k=0; k<subs->ordersLen; k++) {
|
||||
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
||||
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
|
||||
alreadyAdded[subs->orders.ord[i][k]]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2583,15 +2726,15 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->write("INFO",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(song.timeBase);
|
||||
w->writeC(song.speed1);
|
||||
w->writeC(song.speed2);
|
||||
w->writeC(song.arpLen);
|
||||
w->writeF(song.hz);
|
||||
w->writeS(song.patLen);
|
||||
w->writeS(song.ordersLen);
|
||||
w->writeC(song.hilightA);
|
||||
w->writeC(song.hilightB);
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
w->writeS(subSong->ordersLen);
|
||||
w->writeC(subSong->hilightA);
|
||||
w->writeC(subSong->hilightB);
|
||||
w->writeS(song.insLen);
|
||||
w->writeS(song.waveLen);
|
||||
w->writeS(song.sampleLen);
|
||||
|
@ -2668,29 +2811,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
w->writeC(song.orders.ord[i][j]);
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
w->writeC(subSong->orders.ord[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.pat[i].effectCols);
|
||||
w->writeC(subSong->pat[i].effectCols);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.chanShow[i]);
|
||||
w->writeC(subSong->chanShow[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(song.chanCollapse[i]);
|
||||
w->writeC(subSong->chanCollapse[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(song.chanName[i],false);
|
||||
w->writeString(subSong->chanName[i],false);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(song.chanShortName[i],false);
|
||||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
|
||||
w->writeString(song.notes,false);
|
||||
|
@ -2716,24 +2859,85 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeC(0);
|
||||
}
|
||||
|
||||
// subsong list
|
||||
w->writeString(subSong->name,false);
|
||||
w->writeString(subSong->notes,false);
|
||||
w->writeC((unsigned char)(song.subsong.size()-1));
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(0);
|
||||
w->writeC(0);
|
||||
subSongPtrSeek=w->tell();
|
||||
// subsong pointers (we'll seek here later)
|
||||
for (size_t i=0; i<(song.subsong.size()-1); i++) {
|
||||
w->writeI(0);
|
||||
}
|
||||
|
||||
/// SUBSONGS
|
||||
for (subSongIndex=1; subSongIndex<song.subsong.size(); subSongIndex++) {
|
||||
subSong=song.subsong[subSongIndex];
|
||||
subSongPtr.push_back(w->tell());
|
||||
w->write("SONG",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
w->writeS(subSong->ordersLen);
|
||||
w->writeC(subSong->hilightA);
|
||||
w->writeC(subSong->hilightB);
|
||||
w->writeI(0); // reserved
|
||||
|
||||
w->writeString(subSong->name,false);
|
||||
w->writeString(subSong->notes,false);
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<subSong->ordersLen; j++) {
|
||||
w->writeC(subSong->orders.ord[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->pat[i].effectCols);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->chanShow[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeC(subSong->chanCollapse[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(subSong->chanName[i],false);
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
}
|
||||
|
||||
/// INSTRUMENT
|
||||
for (int i=0; i<song.insLen; i++) {
|
||||
DivInstrument* ins=song.ins[i];
|
||||
insPtr[i]=w->tell();
|
||||
insPtr.push_back(w->tell());
|
||||
ins->putInsData(w);
|
||||
}
|
||||
|
||||
/// WAVETABLE
|
||||
for (int i=0; i<song.waveLen; i++) {
|
||||
DivWavetable* wave=song.wave[i];
|
||||
wavePtr[i]=w->tell();
|
||||
wavePtr.push_back(w->tell());
|
||||
wave->putWaveData(w);
|
||||
}
|
||||
|
||||
/// SAMPLE
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
samplePtr[i]=w->tell();
|
||||
samplePtr.push_back(w->tell());
|
||||
w->write("SMPL",4);
|
||||
w->writeI(0);
|
||||
|
||||
|
@ -2750,23 +2954,24 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
/// PATTERN
|
||||
for (int i: patsToWrite) {
|
||||
DivPattern* pat=song.pat[i>>16].getPattern(i&0xffff,false);
|
||||
for (PatToWrite& i: patsToWrite) {
|
||||
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
|
||||
patPtr.push_back(w->tell());
|
||||
w->write("PATR",4);
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(i>>16);
|
||||
w->writeS(i&0xffff);
|
||||
w->writeS(i.chan);
|
||||
w->writeS(i.pat);
|
||||
w->writeS(i.subsong);
|
||||
|
||||
w->writeI(0); // reserved
|
||||
w->writeS(0); // reserved
|
||||
|
||||
for (int j=0; j<song.patLen; j++) {
|
||||
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
|
||||
w->writeS(pat->data[j][0]); // note
|
||||
w->writeS(pat->data[j][1]); // octave
|
||||
w->writeS(pat->data[j][2]); // instrument
|
||||
w->writeS(pat->data[j][3]); // volume
|
||||
w->write(&pat->data[j][4],2*song.pat[i>>16].effectCols*2); // effects
|
||||
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
|
||||
}
|
||||
|
||||
w->writeString(pat->name,false);
|
||||
|
@ -2779,21 +2984,29 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(insPtr[i]);
|
||||
}
|
||||
|
||||
// wavetable pointers (we'll seek here later)
|
||||
// wavetable pointers
|
||||
for (int i=0; i<song.waveLen; i++) {
|
||||
w->writeI(wavePtr[i]);
|
||||
}
|
||||
|
||||
// sample pointers (we'll seek here later)
|
||||
// sample pointers
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
w->writeI(samplePtr[i]);
|
||||
}
|
||||
|
||||
// pattern pointers (we'll seek here later)
|
||||
// pattern pointers
|
||||
for (int i: patPtr) {
|
||||
w->writeI(i);
|
||||
}
|
||||
|
||||
/// SUBSONG POINTERS
|
||||
w->seek(subSongPtrSeek,SEEK_SET);
|
||||
|
||||
// subsong pointers
|
||||
for (size_t i=0; i<(song.subsong.size()-1); i++) {
|
||||
w->writeI(subSongPtr[i]);
|
||||
}
|
||||
|
||||
saveLock.unlock();
|
||||
return w;
|
||||
}
|
||||
|
@ -2864,7 +3077,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
return NULL;
|
||||
}
|
||||
// fail if values are out of range
|
||||
if (song.ordersLen>127) {
|
||||
if (curSubSong->ordersLen>127) {
|
||||
logE("maximum .dmf song length is 127!");
|
||||
lastError="maximum .dmf song length is 127";
|
||||
return NULL;
|
||||
|
@ -2880,10 +3093,10 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
return NULL;
|
||||
}
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
if (song.orders.ord[i][j]>0x7f) {
|
||||
logE("order %d, %d is out of range (0-127)!",song.orders.ord[i][j]);
|
||||
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",song.orders.ord[i][j]);
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
if (curOrders->ord[i][j]>0x7f) {
|
||||
logE("order %d, %d is out of range (0-127)!",curOrders->ord[i][j]);
|
||||
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",curOrders->ord[i][j]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -2926,31 +3139,35 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
// song info
|
||||
w->writeString(song.name,true);
|
||||
w->writeString(song.author,true);
|
||||
w->writeC(song.hilightA);
|
||||
w->writeC(song.hilightB);
|
||||
w->writeC(curSubSong->hilightA);
|
||||
w->writeC(curSubSong->hilightB);
|
||||
|
||||
w->writeC(song.timeBase);
|
||||
w->writeC(song.speed1);
|
||||
w->writeC(song.speed2);
|
||||
w->writeC(song.pal);
|
||||
w->writeC(song.customTempo);
|
||||
w->writeC(curSubSong->timeBase);
|
||||
w->writeC(curSubSong->speed1);
|
||||
w->writeC(curSubSong->speed2);
|
||||
w->writeC(curSubSong->pal);
|
||||
w->writeC(curSubSong->customTempo);
|
||||
char customHz[4];
|
||||
memset(customHz,0,4);
|
||||
snprintf(customHz,4,"%d",(int)song.hz);
|
||||
snprintf(customHz,4,"%d",(int)curSubSong->hz);
|
||||
w->write(customHz,3);
|
||||
w->writeI(song.patLen);
|
||||
w->writeC(song.ordersLen);
|
||||
w->writeI(curSubSong->patLen);
|
||||
w->writeC(curSubSong->ordersLen);
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
w->writeC(song.orders.ord[i][j]);
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
w->writeC(curOrders->ord[i][j]);
|
||||
if (version>=25) {
|
||||
DivPattern* pat=song.pat[i].getPattern(j,false);
|
||||
DivPattern* pat=curPat[i].getPattern(j,false);
|
||||
w->writeString(pat->name,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (song.subsong.size()>1) {
|
||||
addWarning("only the currently selected subsong will be saved");
|
||||
}
|
||||
|
||||
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
||||
addWarning("duty precision will be lost");
|
||||
|
@ -3112,15 +3329,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
}
|
||||
|
||||
for (int i=0; i<getChannelCount(sys); i++) {
|
||||
w->writeC(song.pat[i].effectCols);
|
||||
w->writeC(curPat[i].effectCols);
|
||||
|
||||
for (int j=0; j<song.ordersLen; j++) {
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false);
|
||||
for (int k=0; k<song.patLen; k++) {
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
|
||||
for (int k=0; k<curSubSong->patLen; k++) {
|
||||
w->writeS(pat->data[k][0]); // note
|
||||
w->writeS(pat->data[k][1]); // octave
|
||||
w->writeS(pat->data[k][3]); // volume
|
||||
w->write(&pat->data[k][4],2*song.pat[i].effectCols*2); // effects
|
||||
w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects
|
||||
w->writeS(pat->data[k][2]); // instrument
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ void DivChannelData::wipePatterns() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPattern::copyOn(DivPattern *dest) {
|
||||
void DivPattern::copyOn(DivPattern* dest) {
|
||||
dest->name=name;
|
||||
memcpy(dest->data,data,sizeof(data));
|
||||
}
|
||||
|
|
|
@ -17,20 +17,20 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ym2610bext.h"
|
||||
#include "ym2203ext.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
||||
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
return DivPlatformYM2203::dispatch(c);
|
||||
}
|
||||
if (c.chan>5) {
|
||||
c.chan-=3;
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
return DivPlatformYM2203::dispatch(c);
|
||||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
|
@ -384,7 +384,7 @@ static int opChanOffsH[4]={
|
|||
0xad, 0xae, 0xac, 0xa6
|
||||
};
|
||||
|
||||
void DivPlatformYM2610BExt::tick(bool sysTick) {
|
||||
void DivPlatformYM2203Ext::tick(bool sysTick) {
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
@ -401,7 +401,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2610B::tick(sysTick);
|
||||
DivPlatformYM2203::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
|
@ -438,13 +438,13 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
||||
void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
||||
if (ch<2) {
|
||||
DivPlatformYM2610B::muteChannel(ch,mute);
|
||||
DivPlatformYM2203::muteChannel(ch,mute);
|
||||
return;
|
||||
}
|
||||
if (ch>5) {
|
||||
DivPlatformYM2610B::muteChannel(ch-3,mute);
|
||||
DivPlatformYM2203::muteChannel(ch-3,mute);
|
||||
return;
|
||||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
@ -462,7 +462,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::forceIns() {
|
||||
void DivPlatformYM2203Ext::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
|
@ -494,7 +494,6 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -518,23 +517,23 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
}
|
||||
}
|
||||
|
||||
void* DivPlatformYM2610BExt::getChanState(int ch) {
|
||||
void* DivPlatformYM2203Ext::getChanState(int ch) {
|
||||
if (ch>=6) return &chan[ch-3];
|
||||
if (ch>=2) return &opChan[ch-2];
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
|
||||
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
|
||||
if (ch>=6) return oscBuf[ch-3];
|
||||
if (ch<3) return oscBuf[ch];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::reset() {
|
||||
DivPlatformYM2610B::reset();
|
||||
void DivPlatformYM2203Ext::reset() {
|
||||
DivPlatformYM2203::reset();
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformYM2610BExt::OpChannel();
|
||||
opChan[i]=DivPlatformYM2203Ext::OpChannel();
|
||||
opChan[i].vol=127;
|
||||
}
|
||||
|
||||
|
@ -543,12 +542,12 @@ void DivPlatformYM2610BExt::reset() {
|
|||
extMode=true;
|
||||
}
|
||||
|
||||
bool DivPlatformYM2610BExt::keyOffAffectsArp(int ch) {
|
||||
bool DivPlatformYM2203Ext::keyOffAffectsArp(int ch) {
|
||||
return (ch>8);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::notifyInsChange(int ins) {
|
||||
DivPlatformYM2610B::notifyInsChange(ins);
|
||||
void DivPlatformYM2203Ext::notifyInsChange(int ins) {
|
||||
DivPlatformYM2203::notifyInsChange(ins);
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].ins==ins) {
|
||||
opChan[i].insChanged=true;
|
||||
|
@ -556,8 +555,8 @@ void DivPlatformYM2610BExt::notifyInsChange(int ins) {
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610B::init(parent,channels,sugRate,flags);
|
||||
int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2203::init(parent,channels,sugRate,flags);
|
||||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
|
@ -566,9 +565,9 @@ int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, un
|
|||
return 19;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::quit() {
|
||||
DivPlatformYM2610B::quit();
|
||||
void DivPlatformYM2203Ext::quit() {
|
||||
DivPlatformYM2203::quit();
|
||||
}
|
||||
|
||||
DivPlatformYM2610BExt::~DivPlatformYM2610BExt() {
|
||||
DivPlatformYM2203Ext::~DivPlatformYM2203Ext() {
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
|
||||
#include "ym2610b.h"
|
||||
#include "ym2203.h"
|
||||
|
||||
class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
||||
class DivPlatformYM2203Ext: public DivPlatformYM2203 {
|
||||
struct OpChannel {
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
|
@ -47,5 +47,5 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
|||
void notifyInsChange(int ins);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2610BExt();
|
||||
~DivPlatformYM2203Ext();
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
|||
void DivEngine::nextOrder() {
|
||||
curRow=0;
|
||||
if (repeatPattern) return;
|
||||
if (++curOrder>=song.ordersLen) {
|
||||
if (++curOrder>=curSubSong->ordersLen) {
|
||||
endOfSong=true;
|
||||
curOrder=0;
|
||||
}
|
||||
|
@ -266,11 +266,11 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
|||
void DivEngine::processRow(int i, bool afterDelay) {
|
||||
int whatOrder=afterDelay?chan[i].delayOrder:curOrder;
|
||||
int whatRow=afterDelay?chan[i].delayRow:curRow;
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false);
|
||||
// pre effects
|
||||
if (!afterDelay) {
|
||||
bool returnAfterPre=false;
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
@ -290,7 +290,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
}
|
||||
break;
|
||||
case 0x0d: // next order
|
||||
if (changeOrd<0 && (curOrder<(song.ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
||||
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
|
||||
changeOrd=-2;
|
||||
changePos=effectVal;
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
bool panChanged=false;
|
||||
|
||||
// effects
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
@ -548,7 +548,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
break;
|
||||
case 0xe0: // arp speed
|
||||
if (effectVal>0) {
|
||||
song.arpLen=effectVal;
|
||||
curSubSong->arpLen=effectVal;
|
||||
}
|
||||
break;
|
||||
case 0xe1: // portamento up
|
||||
|
@ -727,7 +727,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].noteOnInhibit=false;
|
||||
|
||||
// post effects
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
short effect=pat->data[whatRow][4+(j<<1)];
|
||||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
|
@ -745,10 +745,10 @@ void DivEngine::nextRow() {
|
|||
strcpy(pb1,"");
|
||||
strcpy(pb3,"");
|
||||
for (int i=0; i<chans; i++) {
|
||||
snprintf(pb,4095," %.2x",song.orders.ord[i][curOrder]);
|
||||
snprintf(pb,4095," %.2x",curOrders->ord[i][curOrder]);
|
||||
strcat(pb1,pb);
|
||||
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
|
||||
snprintf(pb2,4095,"\x1b[37m %s",
|
||||
formatNote(pat->data[curRow][0],pat->data[curRow][1]));
|
||||
strcat(pb3,pb2);
|
||||
|
@ -764,7 +764,7 @@ void DivEngine::nextRow() {
|
|||
snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]);
|
||||
strcat(pb3,pb2);
|
||||
}
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==-1) {
|
||||
strcat(pb3,"\x1b[m--");
|
||||
} else {
|
||||
|
@ -796,32 +796,32 @@ void DivEngine::nextRow() {
|
|||
if (changeOrd==-2) changeOrd=curOrder+1;
|
||||
if (changeOrd<=curOrder) endOfSong=true;
|
||||
curOrder=changeOrd;
|
||||
if (curOrder>=song.ordersLen) {
|
||||
if (curOrder>=curSubSong->ordersLen) {
|
||||
curOrder=0;
|
||||
endOfSong=true;
|
||||
}
|
||||
changeOrd=-1;
|
||||
}
|
||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||
} else if (playing) if (++curRow>=song.patLen) {
|
||||
} else if (playing) if (++curRow>=curSubSong->patLen) {
|
||||
nextOrder();
|
||||
if (haltOn==DIV_HALT_PATTERN) halted=true;
|
||||
}
|
||||
|
||||
if (song.brokenSpeedSel) {
|
||||
if ((song.patLen&1) && curOrder&1) {
|
||||
ticks=((curRow&1)?speed2:speed1)*(song.timeBase+1);
|
||||
if ((curSubSong->patLen&1) && curOrder&1) {
|
||||
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
|
||||
nextSpeed=(curRow&1)?speed1:speed2;
|
||||
} else {
|
||||
ticks=((curRow&1)?speed1:speed2)*(song.timeBase+1);
|
||||
ticks=((curRow&1)?speed1:speed2)*(curSubSong->timeBase+1);
|
||||
nextSpeed=(curRow&1)?speed2:speed1;
|
||||
}
|
||||
} else {
|
||||
if (speedAB) {
|
||||
ticks=speed2*(song.timeBase+1);
|
||||
ticks=speed2*(curSubSong->timeBase+1);
|
||||
nextSpeed=speed1;
|
||||
} else {
|
||||
ticks=speed1*(song.timeBase+1);
|
||||
ticks=speed1*(curSubSong->timeBase+1);
|
||||
nextSpeed=speed2;
|
||||
}
|
||||
speedAB=!speedAB;
|
||||
|
@ -829,7 +829,7 @@ void DivEngine::nextRow() {
|
|||
|
||||
// post row details
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][curOrder],false);
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
|
||||
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
||||
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
|
||||
if (!chan[i].legato) {
|
||||
|
@ -838,7 +838,7 @@ void DivEngine::nextRow() {
|
|||
if (song.oneTickCut) {
|
||||
bool doPrepareCut=true;
|
||||
|
||||
for (int j=0; j<song.pat[i].effectCols; j++) {
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||
doPrepareCut=false;
|
||||
break;
|
||||
|
@ -1007,7 +1007,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
|
||||
if (--chan[i].arpTicks<1) {
|
||||
chan[i].arpTicks=song.arpLen;
|
||||
chan[i].arpTicks=curSubSong->arpLen;
|
||||
chan[i].arpStage++;
|
||||
if (chan[i].arpStage>2) chan[i].arpStage=0;
|
||||
switch (chan[i].arpStage) {
|
||||
|
@ -1048,7 +1048,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
}
|
||||
|
||||
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond);
|
||||
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
|
||||
}
|
||||
|
||||
if (haltOn==DIV_HALT_TICK) halted=true;
|
||||
|
@ -1240,11 +1240,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
if (!freelance && stepPlay!=-1 && subticks==1) {
|
||||
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
||||
if (realPos>=size) realPos=size-1;
|
||||
if (song.hilightA>0) {
|
||||
if ((curRow%song.hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
||||
if (curSubSong->hilightA>0) {
|
||||
if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
|
||||
}
|
||||
if (song.hilightB>0) {
|
||||
if ((curRow%song.hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
||||
if (curSubSong->hilightB>0) {
|
||||
if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
|
||||
}
|
||||
}
|
||||
if (nextTick()) {
|
||||
|
|
|
@ -93,6 +93,18 @@ bool DivSample::initInternal(unsigned char d, int count) {
|
|||
dataDPCM=new unsigned char[lengthDPCM];
|
||||
memset(dataDPCM,0,lengthDPCM);
|
||||
break;
|
||||
case 2: // AICA ADPCM
|
||||
if (dataAICA!=NULL) delete[] dataAICA;
|
||||
lengthAICA=(count+1)/2;
|
||||
dataAICA=new unsigned char[(lengthAICA+255)&(~0xff)];
|
||||
memset(dataAICA,0,(lengthAICA+255)&(~0xff));
|
||||
break;
|
||||
case 3: // YMZ ADPCM
|
||||
if (dataZ!=NULL) delete[] dataZ;
|
||||
lengthZ=(count+1)/2;
|
||||
dataZ=new unsigned char[(lengthZ+255)&(~0xff)];
|
||||
memset(dataZ,0,(lengthZ+255)&(~0xff));
|
||||
break;
|
||||
case 4: // QSound ADPCM
|
||||
if (dataQSoundA!=NULL) delete[] dataQSoundA;
|
||||
lengthQSoundA=(count+1)/2;
|
||||
|
@ -657,6 +669,12 @@ void DivSample::render() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 2: // AICA ADPCM
|
||||
aica_decode(dataAICA,data16,samples);
|
||||
break;
|
||||
case 3: // YMZ ADPCM
|
||||
ymz_decode(dataZ,data16,samples);
|
||||
break;
|
||||
case 4: // QSound ADPCM
|
||||
bs_decode(dataQSoundA,data16,samples);
|
||||
break;
|
||||
|
@ -709,6 +727,14 @@ void DivSample::render() {
|
|||
if (accum>127) accum=127;
|
||||
}
|
||||
}
|
||||
if (depth!=2) { // AICA ADPCM
|
||||
if (!initInternal(2,samples)) return;
|
||||
aica_encode(data16,dataAICA,(samples+511)&(~0x1ff));
|
||||
}
|
||||
if (depth!=3) { // YMZ ADPCM
|
||||
if (!initInternal(3,samples)) return;
|
||||
ymz_encode(data16,dataZ,(samples+511)&(~0x1ff));
|
||||
}
|
||||
if (depth!=4) { // QSound ADPCM
|
||||
if (!initInternal(4,samples)) return;
|
||||
bs_encode(data16,dataQSoundA,samples);
|
||||
|
@ -745,6 +771,10 @@ void* DivSample::getCurBuf() {
|
|||
return data1;
|
||||
case 1:
|
||||
return dataDPCM;
|
||||
case 2:
|
||||
return dataAICA;
|
||||
case 3:
|
||||
return dataZ;
|
||||
case 4:
|
||||
return dataQSoundA;
|
||||
case 5:
|
||||
|
@ -771,6 +801,10 @@ unsigned int DivSample::getCurBufLen() {
|
|||
return length1;
|
||||
case 1:
|
||||
return lengthDPCM;
|
||||
case 2:
|
||||
return lengthAICA;
|
||||
case 3:
|
||||
return lengthZ;
|
||||
case 4:
|
||||
return lengthQSoundA;
|
||||
case 5:
|
||||
|
@ -881,6 +915,8 @@ DivSample::~DivSample() {
|
|||
if (data16) delete[] data16;
|
||||
if (data1) delete[] data1;
|
||||
if (dataDPCM) delete[] dataDPCM;
|
||||
if (dataAICA) delete[] dataAICA;
|
||||
if (dataZ) delete[] dataZ;
|
||||
if (dataQSoundA) delete[] dataQSoundA;
|
||||
if (dataA) delete[] dataA;
|
||||
if (dataB) delete[] dataB;
|
||||
|
|
|
@ -62,6 +62,8 @@ struct DivSample {
|
|||
// valid values are:
|
||||
// - 0: ZX Spectrum overlay drum (1-bit)
|
||||
// - 1: 1-bit NES DPCM (1-bit)
|
||||
// - 2: AICA ADPCM
|
||||
// - 3: YMZ ADPCM
|
||||
// - 4: QSound ADPCM
|
||||
// - 5: ADPCM-A
|
||||
// - 6: ADPCM-B
|
||||
|
@ -77,6 +79,8 @@ struct DivSample {
|
|||
short* data16; // 16
|
||||
unsigned char* data1; // 0
|
||||
unsigned char* dataDPCM; // 1
|
||||
unsigned char* dataAICA; // 2
|
||||
unsigned char* dataZ; // 3
|
||||
unsigned char* dataQSoundA; // 4
|
||||
unsigned char* dataA; // 5
|
||||
unsigned char* dataB; // 6
|
||||
|
@ -84,8 +88,8 @@ struct DivSample {
|
|||
unsigned char* dataBRR; // 9
|
||||
unsigned char* dataVOX; // 10
|
||||
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthAICA, lengthZ, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||
unsigned int off8, off16, off1, offDPCM, offAICA, offZ, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||
unsigned int offSegaPCM, offQSound, offX1_010, offSU;
|
||||
|
||||
unsigned int samples;
|
||||
|
@ -218,6 +222,8 @@ struct DivSample {
|
|||
data16(NULL),
|
||||
data1(NULL),
|
||||
dataDPCM(NULL),
|
||||
dataAICA(NULL),
|
||||
dataZ(NULL),
|
||||
dataQSoundA(NULL),
|
||||
dataA(NULL),
|
||||
dataB(NULL),
|
||||
|
@ -228,6 +234,8 @@ struct DivSample {
|
|||
length16(0),
|
||||
length1(0),
|
||||
lengthDPCM(0),
|
||||
lengthAICA(0),
|
||||
lengthZ(0),
|
||||
lengthQSoundA(0),
|
||||
lengthA(0),
|
||||
lengthB(0),
|
||||
|
@ -238,6 +246,8 @@ struct DivSample {
|
|||
off16(0),
|
||||
off1(0),
|
||||
offDPCM(0),
|
||||
offAICA(0),
|
||||
offZ(0),
|
||||
offQSoundA(0),
|
||||
offA(0),
|
||||
offB(0),
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "song.h"
|
||||
|
||||
void DivSong::clearSongData() {
|
||||
void DivSubSong::clearData() {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
pat[i].wipePatterns();
|
||||
}
|
||||
|
@ -28,6 +28,15 @@ void DivSong::clearSongData() {
|
|||
ordersLen=1;
|
||||
}
|
||||
|
||||
void DivSong::clearSongData() {
|
||||
for (DivSubSong* i: subsong) {
|
||||
i->clearData();
|
||||
delete i;
|
||||
}
|
||||
subsong.clear();
|
||||
subsong.push_back(new DivSubSong);
|
||||
}
|
||||
|
||||
void DivSong::clearInstruments() {
|
||||
for (DivInstrument* i: ins) {
|
||||
delete i;
|
||||
|
@ -71,7 +80,9 @@ void DivSong::unload() {
|
|||
sample.clear();
|
||||
sampleLen=0;
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
pat[i].wipePatterns();
|
||||
for (DivSubSong* i: subsong) {
|
||||
i->clearData();
|
||||
delete i;
|
||||
}
|
||||
subsong.clear();
|
||||
}
|
||||
|
|
|
@ -110,6 +110,44 @@ enum DivSystem {
|
|||
DIV_SYSTEM_DUMMY
|
||||
};
|
||||
|
||||
struct DivSubSong {
|
||||
String name, notes;
|
||||
unsigned char hilightA, hilightB;
|
||||
unsigned char timeBase, speed1, speed2, arpLen;
|
||||
bool pal;
|
||||
bool customTempo;
|
||||
float hz;
|
||||
int patLen, ordersLen;
|
||||
|
||||
DivOrders orders;
|
||||
DivChannelData pat[DIV_MAX_CHANS];
|
||||
|
||||
bool chanShow[DIV_MAX_CHANS];
|
||||
unsigned char chanCollapse[DIV_MAX_CHANS];
|
||||
String chanName[DIV_MAX_CHANS];
|
||||
String chanShortName[DIV_MAX_CHANS];
|
||||
|
||||
void clearData();
|
||||
|
||||
DivSubSong():
|
||||
hilightA(4),
|
||||
hilightB(16),
|
||||
timeBase(0),
|
||||
speed1(6),
|
||||
speed2(6),
|
||||
arpLen(1),
|
||||
pal(true),
|
||||
customTempo(false),
|
||||
hz(60.0),
|
||||
patLen(64),
|
||||
ordersLen(1) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
chanShow[i]=true;
|
||||
chanCollapse[i]=0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DivSong {
|
||||
// version number used for saving the song.
|
||||
// Furnace will save using the latest possible version,
|
||||
|
@ -156,14 +194,21 @@ struct DivSong {
|
|||
// - introduces Genesis system
|
||||
// - introduces system number
|
||||
// - patterns now stored in current known format
|
||||
// - 8: ???
|
||||
// - only used in the Medivo YMU cover
|
||||
// - 7: ???
|
||||
// - only present in a later version of First.dmf
|
||||
// - pattern format changes: empty field is 0xFF instead of 0x80
|
||||
// - instrument now stored in pattern
|
||||
// - 5: BETA 3
|
||||
// - adds arpeggio tick
|
||||
// - 4: BETA 2
|
||||
// - possibly adds instrument number (stored in channel)?
|
||||
// - cannot confirm as I don't have any version 4 modules
|
||||
// - 3: BETA 1
|
||||
// - possibly the first version that could save
|
||||
// - basic format, no system number, 16 instruments, one speed, YMU759-only
|
||||
// - patterns were stored in a different format (chars instead of shorts)
|
||||
// - patterns were stored in a different format (chars instead of shorts) and no instrument
|
||||
// - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle
|
||||
unsigned short version;
|
||||
bool isDMF;
|
||||
|
@ -285,19 +330,10 @@ struct DivSong {
|
|||
String nameJ, authorJ, categoryJ;
|
||||
|
||||
// other things
|
||||
String chanName[DIV_MAX_CHANS];
|
||||
String chanShortName[DIV_MAX_CHANS];
|
||||
String notes;
|
||||
|
||||
// highlight
|
||||
unsigned char hilightA, hilightB;
|
||||
|
||||
// module details
|
||||
unsigned char timeBase, speed1, speed2, arpLen;
|
||||
bool pal;
|
||||
bool customTempo;
|
||||
float hz;
|
||||
int patLen, ordersLen, insLen, waveLen, sampleLen;
|
||||
int insLen, waveLen, sampleLen;
|
||||
float masterVol;
|
||||
float tuning;
|
||||
|
||||
|
@ -345,14 +381,11 @@ struct DivSong {
|
|||
bool snDutyReset;
|
||||
bool pitchMacroIsLinear;
|
||||
|
||||
DivOrders orders;
|
||||
std::vector<DivInstrument*> ins;
|
||||
DivChannelData pat[DIV_MAX_CHANS];
|
||||
std::vector<DivWavetable*> wave;
|
||||
std::vector<DivSample*> sample;
|
||||
|
||||
bool chanShow[DIV_MAX_CHANS];
|
||||
unsigned char chanCollapse[DIV_MAX_CHANS];
|
||||
std::vector<DivSubSong*> subsong;
|
||||
|
||||
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound;
|
||||
DivWavetable nullWave;
|
||||
|
@ -401,17 +434,6 @@ struct DivSong {
|
|||
manInfo(""),
|
||||
createdDate(""),
|
||||
revisionDate(""),
|
||||
hilightA(4),
|
||||
hilightB(16),
|
||||
timeBase(0),
|
||||
speed1(6),
|
||||
speed2(6),
|
||||
arpLen(1),
|
||||
pal(true),
|
||||
customTempo(false),
|
||||
hz(60.0),
|
||||
patLen(64),
|
||||
ordersLen(1),
|
||||
insLen(0),
|
||||
waveLen(0),
|
||||
sampleLen(0),
|
||||
|
@ -457,10 +479,7 @@ struct DivSong {
|
|||
systemPan[i]=0;
|
||||
systemFlags[i]=0;
|
||||
}
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
chanShow[i]=true;
|
||||
chanCollapse[i]=0;
|
||||
}
|
||||
subsong.push_back(new DivSubSong);
|
||||
system[0]=DIV_SYSTEM_YM2612;
|
||||
system[1]=DIV_SYSTEM_SMS;
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ bool DivEngine::isSTDSystem(DivSystem sys) {
|
|||
|
||||
const char* DivEngine::getChannelName(int chan) {
|
||||
if (chan<0 || chan>chans) return "??";
|
||||
if (!song.chanName[chan].empty()) return song.chanName[chan].c_str();
|
||||
if (!curSubSong->chanName[chan].empty()) return curSubSong->chanName[chan].c_str();
|
||||
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||
|
||||
const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]];
|
||||
|
@ -293,7 +293,7 @@ const char* DivEngine::getChannelName(int chan) {
|
|||
|
||||
const char* DivEngine::getChannelShortName(int chan) {
|
||||
if (chan<0 || chan>chans) return "??";
|
||||
if (!song.chanShortName[chan].empty()) return song.chanShortName[chan].c_str();
|
||||
if (!curSubSong->chanShortName[chan].empty()) return curSubSong->chanShortName[chan].c_str();
|
||||
if (sysDefs[sysOfChan[chan]]==NULL) return "??";
|
||||
|
||||
const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]];
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#define DETERMINE_FIRST \
|
||||
int firstChannel=0; \
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) { \
|
||||
if (e->song.chanShow[i]) { \
|
||||
if (e->curSubSong->chanShow[i]) { \
|
||||
firstChannel=i; \
|
||||
break; \
|
||||
} \
|
||||
|
@ -29,7 +29,7 @@
|
|||
#define DETERMINE_LAST \
|
||||
int lastChannel=0; \
|
||||
for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \
|
||||
if (e->song.chanShow[i]) { \
|
||||
if (e->curSubSong->chanShow[i]) { \
|
||||
lastChannel=i+1; \
|
||||
break; \
|
||||
} \
|
||||
|
|
|
@ -38,7 +38,7 @@ void FurnaceGUI::drawChannels() {
|
|||
ImGui::PushID(i);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox("##Visible",&e->song.chanShow[i]);
|
||||
ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]);
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(i==0);
|
||||
if (ImGui::Button(ICON_FA_CHEVRON_UP)) {
|
||||
|
@ -53,10 +53,10 @@ void FurnaceGUI::drawChannels() {
|
|||
ImGui::EndDisabled();
|
||||
ImGui::TableNextColumn();
|
||||
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::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::EndTable();
|
||||
|
|
|
@ -32,7 +32,7 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
|||
selStart.xCoarse=firstChannel;
|
||||
selStart.xFine=0;
|
||||
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;
|
||||
selEnd.y=y;
|
||||
} else {
|
||||
|
@ -56,7 +56,7 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
|||
if (selectingFull) {
|
||||
DETERMINE_LAST;
|
||||
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;
|
||||
} else {
|
||||
selEnd.xCoarse=xCoarse;
|
||||
|
@ -94,21 +94,21 @@ void FurnaceGUI::finishSelection() {
|
|||
if (selStart.xCoarse<0) selStart.xCoarse=0;
|
||||
if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1;
|
||||
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>=chanCount) selEnd.xCoarse=chanCount-1;
|
||||
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>=chanCount) cursor.xCoarse=chanCount-1;
|
||||
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;
|
||||
}
|
||||
if (e->song.chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->song.chanCollapse[selEnd.xCoarse])) {
|
||||
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2;
|
||||
if (e->curSubSong->chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->curSubSong->chanCollapse[selEnd.xCoarse])) {
|
||||
selEnd.xFine=2+e->curPat[cursor.xCoarse].effectCols*2;
|
||||
}
|
||||
|
||||
e->setMidiBaseChan(cursor.xCoarse);
|
||||
|
@ -126,7 +126,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
|||
demandScrollX=true;
|
||||
if (x>0) {
|
||||
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;
|
||||
if (++cursor.xCoarse>=lastChannel) {
|
||||
if (settings.wrapHorizontal!=0 && !select) {
|
||||
|
@ -134,10 +134,10 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
|||
if (settings.wrapHorizontal==2) y++;
|
||||
} else {
|
||||
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 {
|
||||
while (!e->song.chanShow[cursor.xCoarse]) {
|
||||
while (!e->curSubSong->chanShow[cursor.xCoarse]) {
|
||||
cursor.xCoarse++;
|
||||
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
}
|
||||
|
@ -150,21 +150,21 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
|||
if (--cursor.xCoarse<firstChannel) {
|
||||
if (settings.wrapHorizontal!=0 && !select) {
|
||||
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--;
|
||||
} else {
|
||||
cursor.xCoarse=firstChannel;
|
||||
cursor.xFine=0;
|
||||
}
|
||||
} else {
|
||||
while (!e->song.chanShow[cursor.xCoarse]) {
|
||||
while (!e->curSubSong->chanShow[cursor.xCoarse]) {
|
||||
cursor.xCoarse--;
|
||||
if (cursor.xCoarse<0) break;
|
||||
}
|
||||
if (e->song.chanCollapse[cursor.xCoarse]) {
|
||||
cursor.xFine=3-e->song.chanCollapse[cursor.xCoarse];
|
||||
if (e->curSubSong->chanCollapse[cursor.xCoarse]) {
|
||||
cursor.xFine=3-e->curSubSong->chanCollapse[cursor.xCoarse];
|
||||
} 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) {
|
||||
for (int i=0; i<y; i++) {
|
||||
cursor.y++;
|
||||
if (cursor.y>=e->song.patLen) {
|
||||
if (cursor.y>=e->curSubSong->patLen) {
|
||||
if (settings.wrapVertical!=0 && !select) {
|
||||
cursor.y=0;
|
||||
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);
|
||||
} else {
|
||||
cursor.y=e->song.patLen-1;
|
||||
cursor.y=e->curSubSong->patLen-1;
|
||||
}
|
||||
}
|
||||
} 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--;
|
||||
if (cursor.y<0) {
|
||||
if (settings.wrapVertical!=0 && !select) {
|
||||
cursor.y=e->song.patLen-1;
|
||||
cursor.y=e->curSubSong->patLen-1;
|
||||
if (settings.wrapVertical==2) {
|
||||
if ((!e->isPlaying() || !followPattern) && curOrder>0) {
|
||||
setOrder(curOrder-1);
|
||||
|
@ -229,7 +229,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
|||
do {
|
||||
cursor.xCoarse--;
|
||||
if (cursor.xCoarse<0) break;
|
||||
} while (!e->song.chanShow[cursor.xCoarse]);
|
||||
} while (!e->curSubSong->chanShow[cursor.xCoarse]);
|
||||
if (cursor.xCoarse<firstChannel) {
|
||||
if (overflow) {
|
||||
cursor.xCoarse=lastChannel-1;
|
||||
|
@ -253,7 +253,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
|||
do {
|
||||
cursor.xCoarse++;
|
||||
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
} while (!e->song.chanShow[cursor.xCoarse]);
|
||||
} while (!e->curSubSong->chanShow[cursor.xCoarse]);
|
||||
if (cursor.xCoarse>=lastChannel) {
|
||||
if (overflow) {
|
||||
cursor.xCoarse=firstChannel;
|
||||
|
@ -290,14 +290,14 @@ void FurnaceGUI::moveCursorTop(bool select) {
|
|||
void FurnaceGUI::moveCursorBottom(bool select) {
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
if (cursor.y==e->song.patLen-1) {
|
||||
if (cursor.y==e->curSubSong->patLen-1) {
|
||||
DETERMINE_LAST;
|
||||
cursor.xCoarse=lastChannel-1;
|
||||
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;
|
||||
} else {
|
||||
cursor.y=e->song.patLen-1;
|
||||
cursor.y=e->curSubSong->patLen-1;
|
||||
}
|
||||
if (!select) {
|
||||
selStart=cursor;
|
||||
|
@ -310,7 +310,7 @@ void FurnaceGUI::moveCursorBottom(bool select) {
|
|||
void FurnaceGUI::editAdvance() {
|
||||
finishSelection();
|
||||
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;
|
||||
selEnd=cursor;
|
||||
updateScroll(cursor.y);
|
||||
|
|
|
@ -470,7 +470,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
e->unmuteAll();
|
||||
break;
|
||||
case GUI_ACTION_PAT_NEXT_ORDER:
|
||||
if (curOrder<e->song.ordersLen-1) {
|
||||
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||
setOrder(curOrder+1);
|
||||
}
|
||||
break;
|
||||
|
@ -481,21 +481,21 @@ void FurnaceGUI::doAction(int what) {
|
|||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
if (e->song.chanCollapse[cursor.xCoarse]==0) {
|
||||
e->song.chanCollapse[cursor.xCoarse]=3;
|
||||
} else if (e->song.chanCollapse[cursor.xCoarse]>0) {
|
||||
e->song.chanCollapse[cursor.xCoarse]--;
|
||||
if (e->curSubSong->chanCollapse[cursor.xCoarse]==0) {
|
||||
e->curSubSong->chanCollapse[cursor.xCoarse]=3;
|
||||
} else if (e->curSubSong->chanCollapse[cursor.xCoarse]>0) {
|
||||
e->curSubSong->chanCollapse[cursor.xCoarse]--;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_PAT_INCREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
e->song.pat[cursor.xCoarse].effectCols++;
|
||||
if (e->song.pat[cursor.xCoarse].effectCols>8) e->song.pat[cursor.xCoarse].effectCols=8;
|
||||
e->curPat[cursor.xCoarse].effectCols++;
|
||||
if (e->curPat[cursor.xCoarse].effectCols>8) e->curPat[cursor.xCoarse].effectCols=8;
|
||||
break;
|
||||
case GUI_ACTION_PAT_DECREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
e->song.pat[cursor.xCoarse].effectCols--;
|
||||
if (e->song.pat[cursor.xCoarse].effectCols<1) e->song.pat[cursor.xCoarse].effectCols=1;
|
||||
e->curPat[cursor.xCoarse].effectCols--;
|
||||
if (e->curPat[cursor.xCoarse].effectCols<1) e->curPat[cursor.xCoarse].effectCols=1;
|
||||
break;
|
||||
case GUI_ACTION_PAT_INTERPOLATE:
|
||||
doInterpolate();
|
||||
|
@ -525,14 +525,22 @@ void FurnaceGUI::doAction(int what) {
|
|||
|
||||
case GUI_ACTION_INS_LIST_ADD:
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
MARK_MODIFIED;
|
||||
if (curIns==-1) {
|
||||
showError("too many instruments!");
|
||||
} else {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_DUPLICATE:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
|
||||
int prevIns=curIns;
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
(*e->song.ins[curIns])=(*e->song.ins[prevIns]);
|
||||
MARK_MODIFIED;
|
||||
if (curIns==-1) {
|
||||
showError("too many instruments!");
|
||||
} else {
|
||||
(*e->song.ins[curIns])=(*e->song.ins[prevIns]);
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_OPEN:
|
||||
|
@ -571,14 +579,22 @@ void FurnaceGUI::doAction(int what) {
|
|||
|
||||
case GUI_ACTION_WAVE_LIST_ADD:
|
||||
curWave=e->addWave();
|
||||
MARK_MODIFIED;
|
||||
if (curWave==-1) {
|
||||
showError("too many wavetables!");
|
||||
} else {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_WAVE_LIST_DUPLICATE:
|
||||
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
|
||||
int prevWave=curWave;
|
||||
curWave=e->addWave();
|
||||
(*e->song.wave[curWave])=(*e->song.wave[prevWave]);
|
||||
MARK_MODIFIED;
|
||||
if (curWave==-1) {
|
||||
showError("too many wavetables!");
|
||||
} else {
|
||||
(*e->song.wave[curWave])=(*e->song.wave[prevWave]);
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_WAVE_LIST_OPEN:
|
||||
|
@ -614,31 +630,39 @@ void FurnaceGUI::doAction(int what) {
|
|||
|
||||
case GUI_ACTION_SAMPLE_LIST_ADD:
|
||||
curSample=e->addSample();
|
||||
if (curSample==-1) {
|
||||
showError("too many samples!");
|
||||
} else {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
updateSampleTex=true;
|
||||
MARK_MODIFIED;
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_DUPLICATE:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
DivSample* prevSample=e->getSample(curSample);
|
||||
curSample=e->addSample();
|
||||
updateSampleTex=true;
|
||||
e->lockEngine([this,prevSample]() {
|
||||
DivSample* sample=e->getSample(curSample);
|
||||
if (sample!=NULL) {
|
||||
sample->rate=prevSample->rate;
|
||||
sample->centerRate=prevSample->centerRate;
|
||||
sample->name=prevSample->name;
|
||||
sample->loopStart=prevSample->loopStart;
|
||||
sample->depth=prevSample->depth;
|
||||
if (sample->init(prevSample->samples)) {
|
||||
if (prevSample->getCurBuf()!=NULL) {
|
||||
memcpy(sample->getCurBuf(),prevSample->getCurBuf(),prevSample->getCurBufLen());
|
||||
if (curSample==-1) {
|
||||
showError("too many samples!");
|
||||
} else {
|
||||
e->lockEngine([this,prevSample]() {
|
||||
DivSample* sample=e->getSample(curSample);
|
||||
if (sample!=NULL) {
|
||||
sample->rate=prevSample->rate;
|
||||
sample->centerRate=prevSample->centerRate;
|
||||
sample->name=prevSample->name;
|
||||
sample->loopStart=prevSample->loopStart;
|
||||
sample->depth=prevSample->depth;
|
||||
if (sample->init(prevSample->samples)) {
|
||||
if (prevSample->getCurBuf()!=NULL) {
|
||||
memcpy(sample->getCurBuf(),prevSample->getCurBuf(),prevSample->getCurBufLen());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
e->renderSamples();
|
||||
});
|
||||
MARK_MODIFIED;
|
||||
e->renderSamples();
|
||||
});
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
updateSampleTex=true;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_OPEN:
|
||||
|
@ -1154,11 +1178,15 @@ void FurnaceGUI::doAction(int what) {
|
|||
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
|
||||
DivSample* sample=e->song.sample[curSample];
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
e->song.ins[curIns]->type=DIV_INS_AMIGA;
|
||||
e->song.ins[curIns]->name=sample->name;
|
||||
e->song.ins[curIns]->amiga.initSample=curSample;
|
||||
nextWindow=GUI_WINDOW_INS_EDIT;
|
||||
MARK_MODIFIED;
|
||||
if (curIns==-1) {
|
||||
showError("too many instruments!");
|
||||
} else {
|
||||
e->song.ins[curIns]->type=DIV_INS_AMIGA;
|
||||
e->song.ins[curIns]->name=sample->name;
|
||||
e->song.ins[curIns]->amiga.initSample=curSample;
|
||||
nextWindow=GUI_WINDOW_INS_EDIT;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1168,7 +1196,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
}
|
||||
break;
|
||||
case GUI_ACTION_ORDERS_DOWN:
|
||||
if (curOrder<e->song.ordersLen-1) {
|
||||
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||
setOrder(curOrder+1);
|
||||
}
|
||||
break;
|
||||
|
@ -1181,7 +1209,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
orderCursor=firstChannel;
|
||||
break;
|
||||
}
|
||||
} while (!e->song.chanShow[orderCursor]);
|
||||
} while (!e->curSubSong->chanShow[orderCursor]);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_ORDERS_RIGHT: {
|
||||
|
@ -1193,20 +1221,20 @@ void FurnaceGUI::doAction(int what) {
|
|||
orderCursor=lastChannel-1;
|
||||
break;
|
||||
}
|
||||
} while (!e->song.chanShow[orderCursor]);
|
||||
} while (!e->curSubSong->chanShow[orderCursor]);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_ORDERS_INCREASE: {
|
||||
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
||||
if (e->song.orders.ord[orderCursor][curOrder]<0x7f) {
|
||||
e->song.orders.ord[orderCursor][curOrder]++;
|
||||
if (e->curOrders->ord[orderCursor][curOrder]<0x7f) {
|
||||
e->curOrders->ord[orderCursor][curOrder]++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_ORDERS_DECREASE: {
|
||||
if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break;
|
||||
if (e->song.orders.ord[orderCursor][curOrder]>0) {
|
||||
e->song.orders.ord[orderCursor][curOrder]--;
|
||||
if (e->curOrders->ord[orderCursor][curOrder]>0) {
|
||||
e->curOrders->ord[orderCursor][curOrder]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::Text("Edit Step");
|
||||
ImGui::SameLine();
|
||||
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 (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||
|
@ -148,7 +148,7 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(96.0f*dpiScale);
|
||||
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 (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||
|
@ -217,7 +217,7 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::Text("Step");
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
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 (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||
|
@ -317,7 +317,7 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::SetCursorPosX(cursor);
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
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 (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
|
||||
|
|
|
@ -44,8 +44,8 @@ const char* noteNameNormal(short note, short octave) {
|
|||
void FurnaceGUI::prepareUndo(ActionType action) {
|
||||
switch (action) {
|
||||
case GUI_UNDO_CHANGE_ORDER:
|
||||
oldOrders=e->song.orders;
|
||||
oldOrdersLen=e->song.ordersLen;
|
||||
memcpy(&oldOrders,e->curOrders,sizeof(DivOrders));
|
||||
oldOrdersLen=e->curSubSong->ordersLen;
|
||||
break;
|
||||
case GUI_UNDO_PATTERN_EDIT:
|
||||
case GUI_UNDO_PATTERN_DELETE:
|
||||
|
@ -63,7 +63,7 @@ void FurnaceGUI::prepareUndo(ActionType action) {
|
|||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
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;
|
||||
}
|
||||
|
@ -78,18 +78,19 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
|||
s.selEnd=selEnd;
|
||||
s.order=curOrder;
|
||||
s.nibble=curNibble;
|
||||
size_t subSong=e->getCurrentSubSong();
|
||||
switch (action) {
|
||||
case GUI_UNDO_CHANGE_ORDER:
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=0; j<128; j++) {
|
||||
if (oldOrders.ord[i][j]!=e->song.orders.ord[i][j]) {
|
||||
s.ord.push_back(UndoOrderData(i,j,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(subSong,i,j,oldOrders.ord[i][j],e->curOrders->ord[i][j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
s.oldOrdersLen=oldOrdersLen;
|
||||
s.newOrdersLen=e->song.ordersLen;
|
||||
if (oldOrdersLen!=e->song.ordersLen) {
|
||||
s.newOrdersLen=e->curSubSong->ordersLen;
|
||||
if (oldOrdersLen!=e->curSubSong->ordersLen) {
|
||||
doPush=true;
|
||||
}
|
||||
if (!s.ord.empty()) {
|
||||
|
@ -112,11 +113,11 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
|||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false);
|
||||
for (int j=0; j<e->song.patLen; j++) {
|
||||
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
||||
for (int j=0; j<e->curSubSong->patLen; j++) {
|
||||
for (int k=0; k<32; 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() {
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectCols*2) {
|
||||
if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern
|
||||
if (selStart.xFine==0 && selEnd.xFine==2+e->curPat[selEnd.xCoarse].effectCols*2) {
|
||||
if (selStart.y==0 && selEnd.y==e->curSubSong->patLen-1) { // select entire pattern
|
||||
selStart.xCoarse=0;
|
||||
selStart.xFine=0;
|
||||
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
|
||||
selStart.y=0;
|
||||
selEnd.y=e->song.patLen-1;
|
||||
selEnd.y=e->curSubSong->patLen-1;
|
||||
}
|
||||
} else {
|
||||
int selStartX=0;
|
||||
|
@ -153,26 +154,26 @@ void FurnaceGUI::doSelectAll() {
|
|||
// find row position
|
||||
for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) {
|
||||
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.xCoarse++;
|
||||
}
|
||||
}
|
||||
for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) {
|
||||
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.xCoarse++;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
selEnd.y=e->song.patLen-1;
|
||||
selEnd.y=e->curSubSong->patLen-1;
|
||||
} else { // left-right
|
||||
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 iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskDelete,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
|
@ -235,12 +236,12 @@ void FurnaceGUI::doPullDelete() {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskPullDelete,iFine);
|
||||
for (int j=selStart.y; j<e->song.patLen; j++) {
|
||||
if (j<e->song.patLen-1) {
|
||||
for (int j=selStart.y; j<e->curSubSong->patLen; j++) {
|
||||
if (j<e->curSubSong->patLen-1) {
|
||||
if (iFine==0) {
|
||||
pat->data[j][iFine]=pat->data[j+1][iFine];
|
||||
}
|
||||
|
@ -267,11 +268,11 @@ void FurnaceGUI::doInsert() {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); 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 (iFine==0) {
|
||||
pat->data[j][iFine]=0;
|
||||
|
@ -299,9 +300,9 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(mask,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
|
@ -367,9 +368,9 @@ void FurnaceGUI::doCopy(bool cut) {
|
|||
}
|
||||
clipboard+='\n';
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (iFine==0) {
|
||||
clipboard+=noteNameNormal(pat->data[j][0],pat->data[j][1]);
|
||||
if (cut) {
|
||||
|
@ -432,7 +433,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
|
||||
int j=cursor.y;
|
||||
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;
|
||||
int iCoarse=cursor.xCoarse;
|
||||
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];
|
||||
|
||||
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]=='|') {
|
||||
iCoarse++;
|
||||
if (iCoarse<lastChannel) while (!e->song.chanShow[iCoarse]) {
|
||||
if (iCoarse<lastChannel) while (!e->curSubSong->chanShow[iCoarse]) {
|
||||
iCoarse++;
|
||||
if (iCoarse>=lastChannel) break;
|
||||
}
|
||||
|
@ -530,7 +531,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
curOrder++;
|
||||
}
|
||||
|
@ -554,7 +555,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
}
|
||||
if (settings.cursorPastePos) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -567,8 +568,8 @@ void FurnaceGUI::doChangeIns(int ins) {
|
|||
|
||||
int iCoarse=selStart.xCoarse;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
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)) {
|
||||
pat->data[j][2]=ins;
|
||||
|
@ -587,9 +588,9 @@ void FurnaceGUI::doInterpolate() {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskInterpolate,iFine);
|
||||
points.clear();
|
||||
if (iFine!=0) {
|
||||
|
@ -646,9 +647,9 @@ void FurnaceGUI::doFade(int p0, int p1, bool mode) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskFade,iFine);
|
||||
if (iFine!=0) {
|
||||
int absoluteTop=255;
|
||||
|
@ -684,9 +685,9 @@ void FurnaceGUI::doInvertValues() {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskInvertVal,iFine);
|
||||
if (iFine!=0) {
|
||||
int top=255;
|
||||
|
@ -715,9 +716,9 @@ void FurnaceGUI::doScale(float top) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskScale,iFine);
|
||||
if (iFine!=0) {
|
||||
int absoluteTop=255;
|
||||
|
@ -746,9 +747,9 @@ void FurnaceGUI::doRandomize(int bottom, int top, bool mode) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskRandomize,iFine);
|
||||
if (iFine!=0) {
|
||||
int absoluteTop=255;
|
||||
|
@ -792,9 +793,9 @@ void FurnaceGUI::doFlip() {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskFlip,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
|
@ -823,9 +824,9 @@ void FurnaceGUI::doCollapse(int divider) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskCollapseExpand,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
|
@ -880,9 +881,9 @@ void FurnaceGUI::doExpand(int multiplier) {
|
|||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
if (!e->song.chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->song.pat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
maskOut(opMaskCollapseExpand,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
|
@ -891,7 +892,7 @@ void FurnaceGUI::doExpand(int multiplier) {
|
|||
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
|
||||
}
|
||||
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 (iFine==0) {
|
||||
pat->data[j+selStart.y][0]=0;
|
||||
|
@ -921,9 +922,10 @@ void FurnaceGUI::doUndo() {
|
|||
|
||||
switch (us.type) {
|
||||
case GUI_UNDO_CHANGE_ORDER:
|
||||
e->song.ordersLen=us.oldOrdersLen;
|
||||
e->curSubSong->ordersLen=us.oldOrdersLen;
|
||||
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;
|
||||
case GUI_UNDO_PATTERN_EDIT:
|
||||
|
@ -942,7 +944,8 @@ void FurnaceGUI::doUndo() {
|
|||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
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;
|
||||
}
|
||||
if (!e->isPlaying() || !followPattern) {
|
||||
|
@ -967,9 +970,10 @@ void FurnaceGUI::doRedo() {
|
|||
|
||||
switch (us.type) {
|
||||
case GUI_UNDO_CHANGE_ORDER:
|
||||
e->song.ordersLen=us.newOrdersLen;
|
||||
e->curSubSong->ordersLen=us.newOrdersLen;
|
||||
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;
|
||||
case GUI_UNDO_PATTERN_EDIT:
|
||||
|
@ -988,7 +992,8 @@ void FurnaceGUI::doRedo() {
|
|||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
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;
|
||||
}
|
||||
if (!e->isPlaying()) {
|
||||
|
|
|
@ -793,9 +793,9 @@ void FurnaceGUI::prepareLayout() {
|
|||
}
|
||||
|
||||
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;
|
||||
float timeBase=e->song.timeBase+1;
|
||||
float timeBase=e->curSubSong->timeBase+1;
|
||||
float speedSum=s1+s2;
|
||||
if (timeBase<1.0f) timeBase=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) {
|
||||
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);
|
||||
|
||||
|
@ -901,7 +901,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
|||
}
|
||||
|
||||
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);
|
||||
if (target==-1) target=cursor.xFine+1;
|
||||
if (direct) {
|
||||
|
@ -965,7 +965,7 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
|||
editAdvance();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
|
@ -1123,7 +1123,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
|||
int num=valueKeys.at(ev.key.keysym.sym);
|
||||
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
|
||||
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) {
|
||||
curNibble=!curNibble;
|
||||
|
@ -1132,7 +1132,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
|||
orderCursor++;
|
||||
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
|
||||
} else if (orderEditMode==3) {
|
||||
if (curOrder<e->song.ordersLen-1) {
|
||||
if (curOrder<e->curSubSong->ordersLen-1) {
|
||||
setOrder(curOrder+1);
|
||||
}
|
||||
}
|
||||
|
@ -2047,7 +2047,7 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
ImGui::PopFont();
|
||||
ImGui::SameLine();
|
||||
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];
|
||||
latchVol=pat->data[cursor.y][3];
|
||||
latchEffect=pat->data[cursor.y][4];
|
||||
|
@ -2833,12 +2833,12 @@ bool FurnaceGUI::loop() {
|
|||
if (e->isPlaying()) {
|
||||
int totalTicks=e->getTotalTicks();
|
||||
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 {
|
||||
bool hasInfo=false;
|
||||
String info;
|
||||
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) {
|
||||
case 0: // note
|
||||
if (p->data[cursor.y][0]>0) {
|
||||
|
@ -2899,6 +2899,7 @@ bool FurnaceGUI::loop() {
|
|||
|
||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||
|
||||
drawSubSongs();
|
||||
drawPattern();
|
||||
drawEditControls();
|
||||
drawSongInfo();
|
||||
|
@ -3177,8 +3178,11 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
}
|
||||
case GUI_FILE_WAVE_OPEN:
|
||||
e->addWaveFromFile(copyOfName.c_str());
|
||||
MARK_MODIFIED;
|
||||
if (!e->addWaveFromFile(copyOfName.c_str())) {
|
||||
showError("cannot load wavetable! ("+e->getLastError()+")");
|
||||
} else {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_EXPORT_VGM: {
|
||||
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion);
|
||||
|
@ -3504,7 +3508,7 @@ bool FurnaceGUI::loop() {
|
|||
stop();
|
||||
e->lockEngine([this]() {
|
||||
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));
|
||||
for (int j=0; j<256; j++) {
|
||||
pat->data[j][0]=0;
|
||||
|
@ -3663,6 +3667,7 @@ bool FurnaceGUI::init() {
|
|||
regViewOpen=e->getConfBool("regViewOpen",false);
|
||||
logOpen=e->getConfBool("logOpen",false);
|
||||
effectListOpen=e->getConfBool("effectListOpen",false);
|
||||
subSongsOpen=e->getConfBool("subSongsOpen",true);
|
||||
|
||||
tempoView=e->getConfBool("tempoView",true);
|
||||
waveHex=e->getConfBool("waveHex",false);
|
||||
|
@ -3854,6 +3859,7 @@ bool FurnaceGUI::finish() {
|
|||
e->setConf("regViewOpen",regViewOpen);
|
||||
e->setConf("logOpen",logOpen);
|
||||
e->setConf("effectListOpen",effectListOpen);
|
||||
e->setConf("subSongsOpen",subSongsOpen);
|
||||
|
||||
// commit last window size
|
||||
e->setConf("lastWindowWidth",scrW);
|
||||
|
@ -3974,6 +3980,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
logOpen(false),
|
||||
effectListOpen(false),
|
||||
chanOscOpen(false),
|
||||
subSongsOpen(true),
|
||||
/*
|
||||
editControlsDocked(false),
|
||||
ordersDocked(false),
|
||||
|
@ -4001,6 +4008,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
logDocked(false),
|
||||
effectListDocked(false),
|
||||
chanOscDocked(false),
|
||||
subSongsDocked(false),
|
||||
*/
|
||||
selecting(false),
|
||||
selectingFull(false),
|
||||
|
|
|
@ -231,7 +231,8 @@ enum FurnaceGUIWindows {
|
|||
GUI_WINDOW_REGISTER_VIEW,
|
||||
GUI_WINDOW_LOG,
|
||||
GUI_WINDOW_EFFECT_LIST,
|
||||
GUI_WINDOW_CHAN_OSC
|
||||
GUI_WINDOW_CHAN_OSC,
|
||||
GUI_WINDOW_SUBSONGS
|
||||
};
|
||||
|
||||
enum FurnaceGUIFileDialogs {
|
||||
|
@ -337,6 +338,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_WINDOW_LOG,
|
||||
GUI_ACTION_WINDOW_EFFECT_LIST,
|
||||
GUI_ACTION_WINDOW_CHAN_OSC,
|
||||
GUI_ACTION_WINDOW_SUBSONGS,
|
||||
|
||||
GUI_ACTION_COLLAPSE_WINDOW,
|
||||
GUI_ACTION_CLOSE_WINDOW,
|
||||
|
@ -550,9 +552,10 @@ enum ActionType {
|
|||
};
|
||||
|
||||
struct UndoPatternData {
|
||||
int chan, pat, row, col;
|
||||
int subSong, chan, pat, row, col;
|
||||
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),
|
||||
pat(p),
|
||||
row(r),
|
||||
|
@ -562,9 +565,10 @@ struct UndoPatternData {
|
|||
};
|
||||
|
||||
struct UndoOrderData {
|
||||
int chan, ord;
|
||||
int subSong, chan, ord;
|
||||
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),
|
||||
ord(o),
|
||||
oldVal(v1),
|
||||
|
@ -1010,12 +1014,14 @@ class FurnaceGUI {
|
|||
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
||||
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
||||
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
||||
bool subSongsOpen;
|
||||
|
||||
/* there ought to be a better way...
|
||||
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
|
||||
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
|
||||
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked;
|
||||
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked;
|
||||
bool subSongsDocked;
|
||||
*/
|
||||
|
||||
SelectionPoint selStart, selEnd, cursor;
|
||||
|
@ -1246,6 +1252,7 @@ class FurnaceGUI {
|
|||
void drawNewSong();
|
||||
void drawLog();
|
||||
void drawEffectList();
|
||||
void drawSubSongs();
|
||||
|
||||
void parseKeybinds();
|
||||
void promptKey(int which);
|
||||
|
|
|
@ -116,8 +116,8 @@ const char* insTypes[DIV_INS_MAX]={
|
|||
const char* sampleDepths[17]={
|
||||
"1-bit PCM",
|
||||
"1-bit DPCM",
|
||||
NULL,
|
||||
NULL,
|
||||
"Yamaha AICA",
|
||||
"YMZ/YMU ADPCM",
|
||||
"QSound ADPCM",
|
||||
"ADPCM-A",
|
||||
"ADPCM-B",
|
||||
|
@ -485,6 +485,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WINDOW_CHANNELS", "Channels", 0),
|
||||
D("WINDOW_REGISTER_VIEW", "Register View", 0),
|
||||
D("WINDOW_LOG", "Log Viewer", 0),
|
||||
D("WINDOW_SUBSONGS", "Subsongs", 0),
|
||||
D("EFFECT_LIST", "Effect List", 0),
|
||||
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ void FurnaceGUI::drawOrders() {
|
|||
ImGui::SetColumnWidth(-1,regionX-24.0f*dpiScale);
|
||||
int displayChans=0;
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
if (e->song.chanShow[i]) displayChans++;
|
||||
if (e->curSubSong->chanShow[i]) displayChans++;
|
||||
}
|
||||
ImGui::PushFont(patFont);
|
||||
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::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]);
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
if (!e->song.chanShow[i]) continue;
|
||||
if (!e->curSubSong->chanShow[i]) continue;
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",e->getChannelShortName(i));
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
for (int i=0; i<e->song.ordersLen; i++) {
|
||||
for (int i=0; i<e->curSubSong->ordersLen; i++) {
|
||||
ImGui::TableNextRow(0,lineHeight);
|
||||
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -84,16 +84,16 @@ void FurnaceGUI::drawOrders() {
|
|||
}
|
||||
ImGui::PopStyleColor();
|
||||
for (int j=0; j<e->getTotalChannelCount(); j++) {
|
||||
if (!e->song.chanShow[j]) continue;
|
||||
if (!e->curSubSong->chanShow[j]) continue;
|
||||
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()) {
|
||||
snprintf(selID,4096,"%s##O_%.2x_%.2x",pat->name.c_str(),j,i);
|
||||
} 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 (curOrder==i) {
|
||||
if (orderEditMode==0) {
|
||||
|
@ -101,10 +101,10 @@ void FurnaceGUI::drawOrders() {
|
|||
e->lockSave([this,i,j]() {
|
||||
if (changeAllOrders) {
|
||||
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 {
|
||||
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);
|
||||
|
@ -137,10 +137,10 @@ void FurnaceGUI::drawOrders() {
|
|||
e->lockSave([this,i,j]() {
|
||||
if (changeAllOrders) {
|
||||
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 {
|
||||
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);
|
||||
|
|
|
@ -43,21 +43,21 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
return;
|
||||
}
|
||||
// check if we are in range
|
||||
if (ord<0 || ord>=e->song.ordersLen) {
|
||||
if (ord<0 || ord>=e->curSubSong->ordersLen) {
|
||||
return;
|
||||
}
|
||||
if (i<0 || i>=e->song.patLen) {
|
||||
if (i<0 || i>=e->curSubSong->patLen) {
|
||||
return;
|
||||
}
|
||||
bool isPushing=false;
|
||||
ImVec4 activeColor=uiColors[GUI_COLOR_PATTERN_ACTIVE];
|
||||
ImVec4 inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE];
|
||||
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];
|
||||
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_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];
|
||||
inactiveColor=uiColors[GUI_COLOR_PATTERN_INACTIVE_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]));
|
||||
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
||||
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]));
|
||||
} 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]));
|
||||
}
|
||||
} 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]));
|
||||
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
|
||||
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]));
|
||||
} 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]));
|
||||
} else {
|
||||
isPushing=false;
|
||||
|
@ -106,7 +106,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
// for each column
|
||||
for (int j=0; j<chans; j++) {
|
||||
// check if channel is not hidden
|
||||
if (!e->song.chanShow[j]) {
|
||||
if (!e->curSubSong->chanShow[j]) {
|
||||
patChanX[j]=ImGui::GetCursorPosX();
|
||||
continue;
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
ImGui::PopStyleColor();
|
||||
|
||||
// the following is only visible when the channel is not collapsed
|
||||
if (e->song.chanCollapse[j]<3) {
|
||||
if (e->curSubSong->chanCollapse[j]<3) {
|
||||
// instrument
|
||||
if (pat->data[i][2]==-1) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
|
||||
|
@ -195,7 +195,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (e->song.chanCollapse[j]<2) {
|
||||
if (e->curSubSong->chanCollapse[j]<2) {
|
||||
// volume
|
||||
if (pat->data[i][3]==-1) {
|
||||
sprintf(id,"..##PV_%d_%d",i,j);
|
||||
|
@ -229,9 +229,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (e->song.chanCollapse[j]<1) {
|
||||
if (e->curSubSong->chanCollapse[j]<1) {
|
||||
// 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);
|
||||
bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum);
|
||||
bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
|
||||
|
@ -358,7 +358,7 @@ void FurnaceGUI::drawPattern() {
|
|||
int displayChans=0;
|
||||
const DivPattern* patCache[DIV_MAX_CHANS];
|
||||
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::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
|
||||
|
@ -383,7 +383,7 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::TableSetupScrollFreeze(1,1);
|
||||
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::TableNextRow();
|
||||
|
@ -408,10 +408,10 @@ void FurnaceGUI::drawPattern() {
|
|||
cmdStream.clear();
|
||||
}
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (!e->song.chanShow[i]) continue;
|
||||
if (!e->curSubSong->chanShow[i]) continue;
|
||||
ImGui::TableNextColumn();
|
||||
bool displayTooltip=false;
|
||||
if (e->song.chanCollapse[i]) {
|
||||
if (e->curSubSong->chanCollapse[i]) {
|
||||
const char* chName=e->getChannelShortName(i);
|
||||
if (strlen(chName)>3) {
|
||||
snprintf(chanID,2048,"...##_CH%d",i);
|
||||
|
@ -421,7 +421,7 @@ void FurnaceGUI::drawPattern() {
|
|||
displayTooltip=true;
|
||||
} else {
|
||||
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) {
|
||||
String shortChName=chName;
|
||||
shortChName.resize(chNameLimit-3);
|
||||
|
@ -483,7 +483,7 @@ void FurnaceGUI::drawPattern() {
|
|||
e->toggleSolo(i);
|
||||
}
|
||||
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);
|
||||
if (patNameTarget==i) {
|
||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||
|
@ -510,30 +510,30 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::PopFont();
|
||||
} 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);
|
||||
if (ImGui::SmallButton(chanID)) {
|
||||
if (e->song.chanCollapse[i]==0) {
|
||||
e->song.chanCollapse[i]=3;
|
||||
} else if (e->song.chanCollapse[i]>0) {
|
||||
e->song.chanCollapse[i]--;
|
||||
if (e->curSubSong->chanCollapse[i]==0) {
|
||||
e->curSubSong->chanCollapse[i]=3;
|
||||
} else if (e->curSubSong->chanCollapse[i]>0) {
|
||||
e->curSubSong->chanCollapse[i]--;
|
||||
}
|
||||
}
|
||||
if (!e->song.chanCollapse[i]) {
|
||||
if (!e->curSubSong->chanCollapse[i]) {
|
||||
ImGui::SameLine();
|
||||
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)) {
|
||||
e->song.pat[i].effectCols--;
|
||||
if (e->song.pat[i].effectCols<1) e->song.pat[i].effectCols=1;
|
||||
e->curPat[i].effectCols--;
|
||||
if (e->curPat[i].effectCols<1) e->curPat[i].effectCols=1;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(e->song.pat[i].effectCols>=8);
|
||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
|
||||
snprintf(chanID,2048,">##_RCH%d",i);
|
||||
if (ImGui::SmallButton(chanID)) {
|
||||
e->song.pat[i].effectCols++;
|
||||
if (e->song.pat[i].effectCols>8) e->song.pat[i].effectCols=8;
|
||||
e->curPat[i].effectCols++;
|
||||
if (e->curPat[i].effectCols>8) e->curPat[i].effectCols=8;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
@ -555,10 +555,10 @@ void FurnaceGUI::drawPattern() {
|
|||
ImGui::BeginDisabled();
|
||||
if (settings.viewPrevPattern) {
|
||||
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++) {
|
||||
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 {
|
||||
for (int i=0; i<dummyRows-1; i++) {
|
||||
|
@ -569,16 +569,16 @@ void FurnaceGUI::drawPattern() {
|
|||
ImGui::EndDisabled();
|
||||
// active area
|
||||
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);
|
||||
}
|
||||
// next pattern
|
||||
ImGui::BeginDisabled();
|
||||
if (settings.viewPrevPattern) {
|
||||
if ((ord+1)<e->song.ordersLen) for (int i=0; i<chans; i++) {
|
||||
patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord+1],true);
|
||||
if ((ord+1)<e->curSubSong->ordersLen) for (int i=0; i<chans; i++) {
|
||||
patCache[i]=e->curPat[i].getPattern(e->curOrders->ord[i][ord+1],true);
|
||||
}
|
||||
for (int i=0; i<=dummyRows; i++) {
|
||||
patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true);
|
||||
|
@ -612,7 +612,7 @@ void FurnaceGUI::drawPattern() {
|
|||
if (curOrder>0) {
|
||||
setOrder(curOrder-1);
|
||||
ImGui::SetScrollY(ImGui::GetScrollMaxY());
|
||||
updateScroll(e->song.patLen);
|
||||
updateScroll(e->curSubSong->patLen);
|
||||
}
|
||||
haveHitBounds=false;
|
||||
} else {
|
||||
|
@ -624,7 +624,7 @@ void FurnaceGUI::drawPattern() {
|
|||
} else {
|
||||
if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) {
|
||||
if (haveHitBounds) {
|
||||
if (curOrder<(e->song.ordersLen-1)) {
|
||||
if (curOrder<(e->curSubSong->ordersLen-1)) {
|
||||
setOrder(curOrder+1);
|
||||
ImGui::SetScrollY(0);
|
||||
updateScroll(0);
|
||||
|
@ -776,7 +776,7 @@ void FurnaceGUI::drawPattern() {
|
|||
// note slides
|
||||
ImVec2 arrowPoints[7];
|
||||
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);
|
||||
if (ch->portaSpeed>0) {
|
||||
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_EDIT);
|
||||
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_WAVE_LIST);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "gui.h"
|
||||
#include "imgui.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "intConst.h"
|
||||
|
||||
|
@ -74,28 +75,28 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::TableNextColumn();
|
||||
float avail=ImGui::GetContentRegionAvail().x;
|
||||
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 (realTB<1) realTB=1;
|
||||
if (realTB>16) realTB=16;
|
||||
e->song.timeBase=realTB-1;
|
||||
e->curSubSong->timeBase=realTB-1;
|
||||
}
|
||||
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::TableNextColumn();
|
||||
ImGui::Text("Speed");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (e->song.speed1<1) e->song.speed1=1;
|
||||
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (e->curSubSong->speed1<1) e->curSubSong->speed1=1;
|
||||
if (e->isPlaying()) play();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (e->song.speed2<1) e->song.speed2=1;
|
||||
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (e->curSubSong->speed2<1) e->curSubSong->speed2=1;
|
||||
if (e->isPlaying()) play();
|
||||
}
|
||||
|
||||
|
@ -104,12 +105,12 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::Text("Highlight");
|
||||
ImGui::TableNextColumn();
|
||||
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;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -118,11 +119,11 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::Text("Pattern Length");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
int patLen=e->song.patLen;
|
||||
int patLen=e->curSubSong->patLen;
|
||||
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
|
||||
if (patLen<1) patLen=1;
|
||||
if (patLen>256) patLen=256;
|
||||
e->song.patLen=patLen;
|
||||
e->curSubSong->patLen=patLen;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
@ -130,11 +131,11 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::Text("Song Length");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
int ordLen=e->song.ordersLen;
|
||||
int ordLen=e->curSubSong->ordersLen;
|
||||
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
|
||||
if (ordLen<1) ordLen=1;
|
||||
if (ordLen>256) ordLen=256;
|
||||
e->song.ordersLen=ordLen;
|
||||
e->curSubSong->ordersLen=ordLen;
|
||||
if (curOrder>=ordLen) {
|
||||
setOrder(ordLen-1);
|
||||
}
|
||||
|
@ -147,7 +148,7 @@ void FurnaceGUI::drawSongInfo() {
|
|||
}
|
||||
ImGui::TableNextColumn();
|
||||
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 (tempoView) setHz/=2.5;
|
||||
if (setHz<10) setHz=10;
|
||||
|
@ -156,13 +157,13 @@ void FurnaceGUI::drawSongInfo() {
|
|||
}
|
||||
if (tempoView) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("= %gHz",e->song.hz);
|
||||
ImGui::Text("= %gHz",e->curSubSong->hz);
|
||||
} 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::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::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