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