Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
commit
37e83c0905
|
@ -11,7 +11,7 @@ defaults:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Debug
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -275,6 +275,7 @@ jobs:
|
||||||
cp -v ../LICENSE LICENSE.txt
|
cp -v ../LICENSE LICENSE.txt
|
||||||
cp -v ../README.md README.txt
|
cp -v ../README.md README.txt
|
||||||
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
|
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
|
||||||
|
sha256sum ../${binPath}/furnace.exe > checksum.txt
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ the coding style is described here:
|
||||||
- no spaces in operations except for `||` and `&&`
|
- no spaces in operations except for `||` and `&&`
|
||||||
- no space between variable name and assignment
|
- no space between variable name and assignment
|
||||||
- space between macro in string literals
|
- space between macro in string literals
|
||||||
|
- space after comment delimiter
|
||||||
- C++ pointer style: `void* variable` rather than `void *variable`
|
- C++ pointer style: `void* variable` rather than `void *variable`
|
||||||
- indent switch cases
|
- indent switch cases
|
||||||
- preprocessor directives not intended
|
- preprocessor directives not intended
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DEFINES_H
|
||||||
|
#define _DEFINES_H
|
||||||
|
|
||||||
|
// global
|
||||||
|
#define DIV_MAX_CHIPS 32
|
||||||
|
#define DIV_MAX_CHANS 128
|
||||||
|
#define DIV_MAX_PATTERNS 256
|
||||||
|
|
||||||
|
// in-pattern
|
||||||
|
#define DIV_MAX_ROWS 256
|
||||||
|
#define DIV_MAX_COLS 32
|
||||||
|
#define DIV_MAX_EFFECTS 8
|
||||||
|
|
||||||
|
// sample related
|
||||||
|
#define DIV_MAX_SAMPLE_TYPE 4
|
||||||
|
|
||||||
|
#endif
|
|
@ -884,10 +884,10 @@ void DivEngine::runExportThread() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_EXPORT_MODE_MANY_SYS: {
|
case DIV_EXPORT_MODE_MANY_SYS: {
|
||||||
SNDFILE* sf[32];
|
SNDFILE* sf[DIV_MAX_CHIPS];
|
||||||
SF_INFO si[32];
|
SF_INFO si[DIV_MAX_CHIPS];
|
||||||
String fname[32];
|
String fname[DIV_MAX_CHIPS];
|
||||||
SFWrapper sfWrap[32];
|
SFWrapper sfWrap[DIV_MAX_CHIPS];
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
sf[i]=NULL;
|
sf[i]=NULL;
|
||||||
si[i].samplerate=got.rate;
|
si[i].samplerate=got.rate;
|
||||||
|
@ -915,7 +915,7 @@ void DivEngine::runExportThread() {
|
||||||
float* outBuf[2];
|
float* outBuf[2];
|
||||||
outBuf[0]=new float[EXPORT_BUFSIZE];
|
outBuf[0]=new float[EXPORT_BUFSIZE];
|
||||||
outBuf[1]=new float[EXPORT_BUFSIZE];
|
outBuf[1]=new float[EXPORT_BUFSIZE];
|
||||||
short* sysBuf[32];
|
short* sysBuf[DIV_MAX_CHIPS];
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
|
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
|
||||||
}
|
}
|
||||||
|
@ -1399,7 +1399,7 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64) {
|
||||||
c.loadFromMemory(description);
|
c.loadFromMemory(description);
|
||||||
}
|
}
|
||||||
int index=0;
|
int index=0;
|
||||||
for (; index<32; index++) {
|
for (; index<DIV_MAX_CHIPS; index++) {
|
||||||
song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
|
song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
|
||||||
if (song.system[index]==DIV_SYSTEM_NULL) {
|
if (song.system[index]==DIV_SYSTEM_NULL) {
|
||||||
break;
|
break;
|
||||||
|
@ -1456,7 +1456,7 @@ void DivEngine::swapChannels(int src, int dest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||||
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
||||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||||
|
@ -1487,7 +1487,7 @@ void DivEngine::swapChannels(int src, int dest) {
|
||||||
|
|
||||||
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<DIV_MAX_PATTERNS; i++) {
|
||||||
curOrders->ord[ch][i]=0;
|
curOrders->ord[ch][i]=0;
|
||||||
}
|
}
|
||||||
curPat[ch].wipePatterns();
|
curPat[ch].wipePatterns();
|
||||||
|
@ -1649,8 +1649,8 @@ void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivEngine::addSystem(DivSystem which) {
|
bool DivEngine::addSystem(DivSystem which) {
|
||||||
if (song.systemLen>32) {
|
if (song.systemLen>DIV_MAX_CHIPS) {
|
||||||
lastError="max number of systems is 32";
|
lastError=fmt::sprintf("max number of systems is %d",DIV_MAX_CHIPS);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (chans+getChannelCount(which)>DIV_MAX_CHANS) {
|
if (chans+getChannelCount(which)>DIV_MAX_CHANS) {
|
||||||
|
@ -1786,7 +1786,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
||||||
|
|
||||||
for (size_t i=0; i<song.subsong.size(); i++) {
|
for (size_t i=0; i<song.subsong.size(); i++) {
|
||||||
DivOrders prevOrders=song.subsong[i]->orders;
|
DivOrders prevOrders=song.subsong[i]->orders;
|
||||||
DivPattern* prevPat[DIV_MAX_CHANS][256];
|
DivPattern* prevPat[DIV_MAX_CHANS][DIV_MAX_PATTERNS];
|
||||||
unsigned char prevEffectCols[DIV_MAX_CHANS];
|
unsigned char prevEffectCols[DIV_MAX_CHANS];
|
||||||
String prevChanName[DIV_MAX_CHANS];
|
String prevChanName[DIV_MAX_CHANS];
|
||||||
String prevChanShortName[DIV_MAX_CHANS];
|
String prevChanShortName[DIV_MAX_CHANS];
|
||||||
|
@ -1794,7 +1794,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
||||||
unsigned char prevChanCollapse[DIV_MAX_CHANS];
|
unsigned char prevChanCollapse[DIV_MAX_CHANS];
|
||||||
|
|
||||||
for (int j=0; j<tchans; j++) {
|
for (int j=0; j<tchans; j++) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
prevPat[j][k]=song.subsong[i]->pat[j].data[k];
|
prevPat[j][k]=song.subsong[i]->pat[j].data[k];
|
||||||
}
|
}
|
||||||
prevEffectCols[j]=song.subsong[i]->pat[j].effectCols;
|
prevEffectCols[j]=song.subsong[i]->pat[j].effectCols;
|
||||||
|
@ -1806,7 +1806,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j=0; j<tchans; j++) {
|
for (int j=0; j<tchans; j++) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
song.subsong[i]->orders.ord[j][k]=prevOrders.ord[swappedChannels[j]][k];
|
song.subsong[i]->orders.ord[j][k]=prevOrders.ord[swappedChannels[j]][k];
|
||||||
song.subsong[i]->pat[j].data[k]=prevPat[swappedChannels[j]][k];
|
song.subsong[i]->pat[j].data[k]=prevPat[swappedChannels[j]][k];
|
||||||
}
|
}
|
||||||
|
@ -2770,7 +2770,7 @@ 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 (size_t j=0; j<song.subsong.size(); j++) {
|
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
||||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||||
if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) {
|
if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) {
|
||||||
|
@ -3470,7 +3470,7 @@ 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 (curSubSong->ordersLen>=0xff) return;
|
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||||
memset(order,0,DIV_MAX_CHANS);
|
memset(order,0,DIV_MAX_CHANS);
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
|
@ -3478,14 +3478,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
||||||
order[i]=curOrders->ord[i][curOrder];
|
order[i]=curOrders->ord[i][curOrder];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool used[256];
|
bool used[DIV_MAX_PATTERNS];
|
||||||
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)*DIV_MAX_PATTERNS);
|
||||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||||
used[curOrders->ord[i][j]]=true;
|
used[curOrders->ord[i][j]]=true;
|
||||||
}
|
}
|
||||||
order[i]=0xff;
|
order[i]=(DIV_MAX_PATTERNS-1);
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||||
if (!used[j]) {
|
if (!used[j]) {
|
||||||
order[i]=j;
|
order[i]=j;
|
||||||
break;
|
break;
|
||||||
|
@ -3520,7 +3520,7 @@ 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 (curSubSong->ordersLen>=0xff) return;
|
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||||
warnings="";
|
warnings="";
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
|
@ -3528,14 +3528,14 @@ void DivEngine::deepCloneOrder(bool where) {
|
||||||
logD("channel %d",i);
|
logD("channel %d",i);
|
||||||
order[i]=curOrders->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<DIV_MAX_PATTERNS; j++) {
|
||||||
logD("finding free slot in %d...",j);
|
logD("finding free slot in %d...",j);
|
||||||
if (curPat[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=curPat[i].getPattern(origOrd,false);
|
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||||
DivPattern* pat=curPat[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,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||||
logD("found at %d",j);
|
logD("found at %d",j);
|
||||||
didNotFind=false;
|
didNotFind=false;
|
||||||
break;
|
break;
|
||||||
|
@ -3631,7 +3631,7 @@ 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 (size_t j=0; j<song.subsong.size(); j++) {
|
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
||||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||||
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
|
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
|
||||||
|
|
|
@ -318,7 +318,7 @@ enum DivChanTypes {
|
||||||
extern const char* cmdName[];
|
extern const char* cmdName[];
|
||||||
|
|
||||||
class DivEngine {
|
class DivEngine {
|
||||||
DivDispatchContainer disCont[32];
|
DivDispatchContainer disCont[DIV_MAX_CHIPS];
|
||||||
TAAudio* output;
|
TAAudio* output;
|
||||||
TAAudioDesc want, got;
|
TAAudioDesc want, got;
|
||||||
String exportPath;
|
String exportPath;
|
||||||
|
|
|
@ -1581,7 +1581,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
unsigned int wavePtr[256];
|
unsigned int wavePtr[256];
|
||||||
unsigned int samplePtr[256];
|
unsigned int samplePtr[256];
|
||||||
unsigned int subSongPtr[256];
|
unsigned int subSongPtr[256];
|
||||||
unsigned int sysFlagsPtr[32];
|
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
|
||||||
std::vector<int> patPtr;
|
std::vector<int> patPtr;
|
||||||
int numberOfSubSongs=0;
|
int numberOfSubSongs=0;
|
||||||
char magic[5];
|
char magic[5];
|
||||||
|
@ -1760,7 +1760,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (subSong->patLen>256) {
|
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||||
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;
|
||||||
|
@ -1772,7 +1772,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (subSong->ordersLen>256) {
|
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||||
logE("song is too long!");
|
logE("song is too long!");
|
||||||
lastError="song is too long!";
|
lastError="song is too long!";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
@ -1804,7 +1804,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
logD("systems:");
|
logD("systems:");
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
unsigned char sysID=reader.readC();
|
unsigned char sysID=reader.readC();
|
||||||
ds.system[i]=systemFromFileFur(sysID);
|
ds.system[i]=systemFromFileFur(sysID);
|
||||||
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
|
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
|
||||||
|
@ -1826,7 +1826,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// system volume
|
// system volume
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
ds.systemVol[i]=reader.readC();
|
ds.systemVol[i]=reader.readC();
|
||||||
if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) {
|
if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) {
|
||||||
ds.systemVol[i]/=4;
|
ds.systemVol[i]/=4;
|
||||||
|
@ -1834,15 +1834,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// system panning
|
// system panning
|
||||||
for (int i=0; i<32; i++) ds.systemPan[i]=reader.readC();
|
for (int i=0; i<DIV_MAX_CHIPS; i++) ds.systemPan[i]=reader.readC();
|
||||||
|
|
||||||
// system props
|
// system props
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
sysFlagsPtr[i]=reader.readI();
|
sysFlagsPtr[i]=reader.readI();
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle compound systems
|
// handle compound systems
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
if (ds.system[i]==DIV_SYSTEM_GENESIS ||
|
if (ds.system[i]==DIV_SYSTEM_GENESIS ||
|
||||||
ds.system[i]==DIV_SYSTEM_GENESIS_EXT ||
|
ds.system[i]==DIV_SYSTEM_GENESIS_EXT ||
|
||||||
ds.system[i]==DIV_SYSTEM_ARCADE) {
|
ds.system[i]==DIV_SYSTEM_ARCADE) {
|
||||||
|
@ -1851,7 +1851,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
ds.systemVol[j]=ds.systemVol[j-1];
|
ds.systemVol[j]=ds.systemVol[j-1];
|
||||||
ds.systemPan[j]=ds.systemPan[j-1];
|
ds.systemPan[j]=ds.systemPan[j-1];
|
||||||
}
|
}
|
||||||
if (++ds.systemLen>32) ds.systemLen=32;
|
if (++ds.systemLen>DIV_MAX_CHIPS) ds.systemLen=DIV_MAX_CHIPS;
|
||||||
|
|
||||||
if (ds.system[i]==DIV_SYSTEM_GENESIS) {
|
if (ds.system[i]==DIV_SYSTEM_GENESIS) {
|
||||||
ds.system[i]=DIV_SYSTEM_YM2612;
|
ds.system[i]=DIV_SYSTEM_YM2612;
|
||||||
|
@ -2000,7 +2000,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
subSong->pat[i].effectCols=reader.readC();
|
subSong->pat[i].effectCols=reader.readC();
|
||||||
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>8) {
|
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>DIV_MAX_EFFECTS) {
|
||||||
logE("channel %d has zero or too many effect columns! (%d)",i,subSong->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,subSong->pat[i].effectCols);
|
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
@ -2198,7 +2198,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
// read system flags
|
// read system flags
|
||||||
if (ds.version>=119) {
|
if (ds.version>=119) {
|
||||||
logD("reading chip flags...");
|
logD("reading chip flags...");
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
if (sysFlagsPtr[i]==0) continue;
|
if (sysFlagsPtr[i]==0) continue;
|
||||||
|
|
||||||
if (!reader.seek(sysFlagsPtr[i],SEEK_SET)) {
|
if (!reader.seek(sysFlagsPtr[i],SEEK_SET)) {
|
||||||
|
@ -2416,7 +2416,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (index<0 || index>255) {
|
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||||
logE("pattern index out of range!",i);
|
logE("pattern index out of range!",i);
|
||||||
lastError="pattern index out of range!";
|
lastError="pattern index out of range!";
|
||||||
ds.unload();
|
ds.unload();
|
||||||
|
@ -4175,14 +4175,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
|
|
||||||
// fail if values are out of range
|
// fail if values are out of range
|
||||||
/*
|
/*
|
||||||
if (subSong->ordersLen>256) {
|
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||||
logE("maximum song length is 256!");
|
logE("maximum song length is %d!",DIV_MAX_PATTERNS);
|
||||||
lastError="maximum song length is 256";
|
lastError=fmt::sprintf("maximum song length is %d",DIV_MAX_PATTERNS);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (subSong->patLen>256) {
|
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||||
logE("maximum pattern length is 256!");
|
logE("maximum pattern length is %d!",DIV_MAX_ROWS);
|
||||||
lastError="maximum pattern length is 256";
|
lastError=fmt::sprintf("maximum pattern length is %d",DIV_MAX_ROWS);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -4236,18 +4236,18 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||||
DivSubSong* subs=song.subsong[j];
|
DivSubSong* subs=song.subsong[j];
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
if (subs->pat[i].data[k]==NULL) continue;
|
if (subs->pat[i].data[k]==NULL) continue;
|
||||||
patsToWrite.push_back(PatToWrite(j,i,k));
|
patsToWrite.push_back(PatToWrite(j,i,k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool alreadyAdded[256];
|
bool alreadyAdded[DIV_MAX_PATTERNS];
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||||
DivSubSong* subs=song.subsong[j];
|
DivSubSong* subs=song.subsong[j];
|
||||||
memset(alreadyAdded,0,256*sizeof(bool));
|
memset(alreadyAdded,0,DIV_MAX_PATTERNS*sizeof(bool));
|
||||||
for (int k=0; k<subs->ordersLen; k++) {
|
for (int k=0; k<subs->ordersLen; k++) {
|
||||||
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
||||||
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
|
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
|
||||||
|
@ -4276,7 +4276,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeS(song.sampleLen);
|
w->writeS(song.sampleLen);
|
||||||
w->writeI(patsToWrite.size());
|
w->writeI(patsToWrite.size());
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
if (i>=song.systemLen) {
|
if (i>=song.systemLen) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4284,17 +4284,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
w->writeC(song.systemVol[i]);
|
w->writeC(song.systemVol[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
w->writeC(song.systemPan[i]);
|
w->writeC(song.systemPan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// chip flags (we'll seek here later)
|
// chip flags (we'll seek here later)
|
||||||
sysFlagsPtrSeek=w->tell();
|
sysFlagsPtrSeek=w->tell();
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
#define _ORDERS_H
|
#define _ORDERS_H
|
||||||
|
|
||||||
struct DivOrders {
|
struct DivOrders {
|
||||||
unsigned char ord[DIV_MAX_CHANS][256];
|
unsigned char ord[DIV_MAX_CHANS][DIV_MAX_PATTERNS];
|
||||||
|
|
||||||
DivOrders() {
|
DivOrders() {
|
||||||
memset(ord,0,DIV_MAX_CHANS*256);
|
memset(ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
static DivPattern emptyPat;
|
static DivPattern emptyPat;
|
||||||
|
|
||||||
DivPattern::DivPattern() {
|
DivPattern::DivPattern() {
|
||||||
memset(data,-1,256*32*sizeof(short));
|
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||||
data[i][0]=0;
|
data[i][0]=0;
|
||||||
data[i][1]=0;
|
data[i][1]=0;
|
||||||
}
|
}
|
||||||
|
@ -43,13 +43,13 @@ DivPattern* DivChannelData::getPattern(int index, bool create) {
|
||||||
|
|
||||||
std::vector<std::pair<int,int>> DivChannelData::optimize() {
|
std::vector<std::pair<int,int>> DivChannelData::optimize() {
|
||||||
std::vector<std::pair<int,int>> ret;
|
std::vector<std::pair<int,int>> ret;
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||||
if (data[i]!=NULL) {
|
if (data[i]!=NULL) {
|
||||||
// compare
|
// compare
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||||
if (j==i) continue;
|
if (j==i) continue;
|
||||||
if (data[j]==NULL) continue;
|
if (data[j]==NULL) continue;
|
||||||
if (memcmp(data[i]->data,data[j]->data,256*32*sizeof(short))==0) {
|
if (memcmp(data[i]->data,data[j]->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) {
|
||||||
delete data[j];
|
delete data[j];
|
||||||
data[j]=NULL;
|
data[j]=NULL;
|
||||||
logV("%d == %d",i,j);
|
logV("%d == %d",i,j);
|
||||||
|
@ -63,15 +63,15 @@ std::vector<std::pair<int,int>> DivChannelData::optimize() {
|
||||||
|
|
||||||
std::vector<std::pair<int,int>> DivChannelData::rearrange() {
|
std::vector<std::pair<int,int>> DivChannelData::rearrange() {
|
||||||
std::vector<std::pair<int,int>> ret;
|
std::vector<std::pair<int,int>> ret;
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||||
if (data[i]==NULL) {
|
if (data[i]==NULL) {
|
||||||
for (int j=i; j<256; j++) {
|
for (int j=i; j<DIV_MAX_PATTERNS; j++) {
|
||||||
if (data[j]!=NULL) {
|
if (data[j]!=NULL) {
|
||||||
data[i]=data[j];
|
data[i]=data[j];
|
||||||
data[j]=NULL;
|
data[j]=NULL;
|
||||||
logV("%d -> %d",j,i);
|
logV("%d -> %d",j,i);
|
||||||
ret.push_back(std::pair<int,int>(j,i));
|
ret.push_back(std::pair<int,int>(j,i));
|
||||||
if (++i>=256) break;
|
if (++i>=DIV_MAX_PATTERNS) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ std::vector<std::pair<int,int>> DivChannelData::rearrange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivChannelData::wipePatterns() {
|
void DivChannelData::wipePatterns() {
|
||||||
for (int i=0; i<256; i++) {
|
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||||
if (data[i]!=NULL) {
|
if (data[i]!=NULL) {
|
||||||
delete data[i];
|
delete data[i];
|
||||||
data[i]=NULL;
|
data[i]=NULL;
|
||||||
|
@ -95,5 +95,5 @@ void DivPattern::copyOn(DivPattern* dest) {
|
||||||
|
|
||||||
DivChannelData::DivChannelData():
|
DivChannelData::DivChannelData():
|
||||||
effectCols(1) {
|
effectCols(1) {
|
||||||
memset(data,0,256*sizeof(void*));
|
memset(data,0,DIV_MAX_PATTERNS*sizeof(void*));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
struct DivPattern {
|
struct DivPattern {
|
||||||
String name;
|
String name;
|
||||||
short data[256][32];
|
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* copy this pattern to another.
|
* copy this pattern to another.
|
||||||
|
@ -42,7 +42,7 @@ struct DivChannelData {
|
||||||
// 3: volume
|
// 3: volume
|
||||||
// 4-5+: effect/effect value
|
// 4-5+: effect/effect value
|
||||||
// do NOT access directly unless you know what you're doing!
|
// do NOT access directly unless you know what you're doing!
|
||||||
DivPattern* data[256];
|
DivPattern* data[DIV_MAX_PATTERNS];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get a pattern from this channel, or the empty pattern if not initialized.
|
* get a pattern from this channel, or the empty pattern if not initialized.
|
||||||
|
|
|
@ -125,6 +125,8 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
||||||
if (parent->song.linearPitch==2 || !easyNoise) {
|
if (parent->song.linearPitch==2 || !easyNoise) {
|
||||||
return NOTE_PERIODIC(note);
|
return NOTE_PERIODIC(note);
|
||||||
}
|
}
|
||||||
|
int easyStartingPeriod=16;
|
||||||
|
int easyThreshold=round(12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-3;
|
||||||
if (note>easyThreshold) {
|
if (note>easyThreshold) {
|
||||||
return MAX(0,easyStartingPeriod-(note-easyThreshold));
|
return MAX(0,easyStartingPeriod-(note-easyThreshold));
|
||||||
}
|
}
|
||||||
|
@ -132,19 +134,23 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformSMS::snCalcFreq(int ch) {
|
int DivPlatformSMS::snCalcFreq(int ch) {
|
||||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold<<7)) {
|
double CHIP_DIVIDER=toneDivider;
|
||||||
int ret=(((easyStartingPeriod<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold<<7)))>>7;
|
if (ch==3) CHIP_DIVIDER=noiseDivider;
|
||||||
|
int easyStartingPeriod=16;
|
||||||
|
int easyThreshold=round(128.0*12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-384+64;
|
||||||
|
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold)) {
|
||||||
|
int ret=(((easyStartingPeriod<<7))-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold)))>>7;
|
||||||
if (ret<0) ret=0;
|
if (ret<0) ret=0;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?noiseDivider:toneDivider);
|
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,CHIP_DIVIDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformSMS::tick(bool sysTick) {
|
void DivPlatformSMS::tick(bool sysTick) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
if (chan[i].std.vol.had) {
|
if (chan[i].std.vol.had) {
|
||||||
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].std.vol.val,chan[i].vol,15);
|
chan[i].outVol=VOL_SCALE_LOG(chan[i].std.vol.val,chan[i].vol,15);
|
||||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||||
// old formula
|
// old formula
|
||||||
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
|
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
|
||||||
|
@ -152,7 +158,6 @@ void DivPlatformSMS::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (chan[i].std.arp.had) {
|
if (chan[i].std.arp.had) {
|
||||||
if (!chan[i].inPorta) {
|
if (!chan[i].inPorta) {
|
||||||
// TODO: check whether this weird octave boundary thing applies to other systems as well
|
|
||||||
// TODO: add compatibility flag. this is horrible.
|
// TODO: add compatibility flag. this is horrible.
|
||||||
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||||
while (areYouSerious>0x60) areYouSerious-=12;
|
while (areYouSerious>0x60) areYouSerious-=12;
|
||||||
|
@ -422,7 +427,7 @@ void DivPlatformSMS::reset() {
|
||||||
YMPSG_Init(&sn_nuked,isRealSN,12,isRealSN?13:15,isRealSN?16383:32767);
|
YMPSG_Init(&sn_nuked,isRealSN,12,isRealSN?13:15,isRealSN?16383:32767);
|
||||||
snNoiseMode=3;
|
snNoiseMode=3;
|
||||||
rWrite(0,0xe7);
|
rWrite(0,0xe7);
|
||||||
updateSNMode=false;
|
updateSNMode=true;
|
||||||
oldValue=0xff;
|
oldValue=0xff;
|
||||||
lastPan=0xff;
|
lastPan=0xff;
|
||||||
if (stereo) {
|
if (stereo) {
|
||||||
|
@ -464,38 +469,24 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
||||||
switch (flags.getInt("clockSel",0)) {
|
switch (flags.getInt("clockSel",0)) {
|
||||||
case 1:
|
case 1:
|
||||||
chipClock=COLOR_PAL*4.0/5.0;
|
chipClock=COLOR_PAL*4.0/5.0;
|
||||||
easyThreshold=84;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
chipClock=4000000;
|
chipClock=4000000;
|
||||||
easyThreshold=86;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
chipClock=COLOR_NTSC/2.0;
|
chipClock=COLOR_NTSC/2.0;
|
||||||
easyThreshold=72;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
chipClock=3000000;
|
chipClock=3000000;
|
||||||
easyThreshold=81;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
chipClock=2000000;
|
chipClock=2000000;
|
||||||
easyThreshold=74;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
chipClock=COLOR_NTSC/8.0;
|
chipClock=COLOR_NTSC/8.0;
|
||||||
easyThreshold=48;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
chipClock=COLOR_NTSC;
|
chipClock=COLOR_NTSC;
|
||||||
easyThreshold=84;
|
|
||||||
easyStartingPeriod=13;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CHECK_CUSTOM_CLOCK;
|
CHECK_CUSTOM_CLOCK;
|
||||||
|
@ -569,6 +560,7 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
||||||
stereo=false;
|
stereo=false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rate=chipClock/divider;
|
rate=chipClock/divider;
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
oscBuf[i]->rate=rate;
|
oscBuf[i]->rate=rate;
|
||||||
|
|
|
@ -65,8 +65,6 @@ class DivPlatformSMS: public DivDispatch {
|
||||||
int divider=16;
|
int divider=16;
|
||||||
double toneDivider=64.0;
|
double toneDivider=64.0;
|
||||||
double noiseDivider=64.0;
|
double noiseDivider=64.0;
|
||||||
int easyThreshold;
|
|
||||||
int easyStartingPeriod;
|
|
||||||
bool updateSNMode;
|
bool updateSNMode;
|
||||||
bool resetPhase;
|
bool resetPhase;
|
||||||
bool isRealSN;
|
bool isRealSN;
|
||||||
|
|
|
@ -1592,7 +1592,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
if (disCont[i].lastAvail>0) {
|
if (disCont[i].lastAvail>0) {
|
||||||
disCont[i].flush(disCont[i].lastAvail);
|
disCont[i].flush(disCont[i].lastAvail);
|
||||||
}
|
}
|
||||||
|
if (size<disCont[i].lastAvail) {
|
||||||
|
disCont[i].runtotal=0;
|
||||||
|
} else {
|
||||||
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-disCont[i].lastAvail);
|
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-disCont[i].lastAvail);
|
||||||
|
}
|
||||||
if (disCont[i].runtotal>disCont[i].bbInLen) {
|
if (disCont[i].runtotal>disCont[i].bbInLen) {
|
||||||
logV("growing dispatch %d bbIn to %d",i,disCont[i].runtotal+256);
|
logV("growing dispatch %d bbIn to %d",i,disCont[i].runtotal+256);
|
||||||
delete[] disCont[i].bbIn[0];
|
delete[] disCont[i].bbIn[0];
|
||||||
|
|
|
@ -57,9 +57,9 @@ void DivSample::putSampleData(SafeWriter* w) {
|
||||||
w->writeI(loop?loopStart:-1);
|
w->writeI(loop?loopStart:-1);
|
||||||
w->writeI(loop?loopEnd:-1);
|
w->writeI(loop?loopEnd:-1);
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||||
unsigned int out=0;
|
unsigned int out=0;
|
||||||
for (int j=0; j<32; j++) {
|
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||||
if (renderOn[i][j]) out|=1<<j;
|
if (renderOn[i][j]) out|=1<<j;
|
||||||
}
|
}
|
||||||
w->writeI(out);
|
w->writeI(out);
|
||||||
|
@ -137,9 +137,9 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
||||||
loopEnd=reader.readI();
|
loopEnd=reader.readI();
|
||||||
loop=(loopStart>=0)&&(loopEnd>=0);
|
loop=(loopStart>=0)&&(loopEnd>=0);
|
||||||
|
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||||
unsigned int outMask=(unsigned int)reader.readI();
|
unsigned int outMask=(unsigned int)reader.readI();
|
||||||
for (int j=0; j<32; j++) {
|
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||||
renderOn[i][j]=outMask&(1<<j);
|
renderOn[i][j]=outMask&(1<<j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define _SAMPLE_H
|
#define _SAMPLE_H
|
||||||
|
|
||||||
#include "../ta-utils.h"
|
#include "../ta-utils.h"
|
||||||
|
#include "defines.h"
|
||||||
#include "safeWriter.h"
|
#include "safeWriter.h"
|
||||||
#include "dataErrors.h"
|
#include "dataErrors.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -116,7 +117,7 @@ struct DivSample {
|
||||||
// - 2: Pingpong loop
|
// - 2: Pingpong loop
|
||||||
DivSampleLoopMode loopMode;
|
DivSampleLoopMode loopMode;
|
||||||
|
|
||||||
bool renderOn[4][32];
|
bool renderOn[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||||
|
|
||||||
// these are the new data structures.
|
// these are the new data structures.
|
||||||
signed char* data8; // 8
|
signed char* data8; // 8
|
||||||
|
@ -332,11 +333,10 @@ struct DivSample {
|
||||||
lengthBRR(0),
|
lengthBRR(0),
|
||||||
lengthVOX(0),
|
lengthVOX(0),
|
||||||
samples(0) {
|
samples(0) {
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
renderOn[0][i]=true;
|
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||||
renderOn[1][i]=true;
|
renderOn[j][i]=true;
|
||||||
renderOn[2][i]=true;
|
}
|
||||||
renderOn[3][i]=true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~DivSample();
|
~DivSample();
|
||||||
|
|
|
@ -25,7 +25,7 @@ void DivSubSong::clearData() {
|
||||||
pat[i].wipePatterns();
|
pat[i].wipePatterns();
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(orders.ord,0,DIV_MAX_CHANS*256);
|
memset(orders.ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||||
ordersLen=1;
|
ordersLen=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ void DivSubSong::optimizePatterns() {
|
||||||
logD("optimizing channel %d...",i);
|
logD("optimizing channel %d...",i);
|
||||||
std::vector<std::pair<int,int>> clearOuts=pat[i].optimize();
|
std::vector<std::pair<int,int>> clearOuts=pat[i].optimize();
|
||||||
for (auto& j: clearOuts) {
|
for (auto& j: clearOuts) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
if (orders.ord[i][k]==j.first) {
|
if (orders.ord[i][k]==j.first) {
|
||||||
orders.ord[i][k]=j.second;
|
orders.ord[i][k]=j.second;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ void DivSubSong::rearrangePatterns() {
|
||||||
logD("re-arranging channel %d...",i);
|
logD("re-arranging channel %d...",i);
|
||||||
std::vector<std::pair<int,int>> clearOuts=pat[i].rearrange();
|
std::vector<std::pair<int,int>> clearOuts=pat[i].rearrange();
|
||||||
for (auto& j: clearOuts) {
|
for (auto& j: clearOuts) {
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
if (orders.ord[i][k]==j.first) {
|
if (orders.ord[i][k]==j.first) {
|
||||||
orders.ord[i][k]=j.second;
|
orders.ord[i][k]=j.second;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define DIV_MAX_CHANS 128
|
#include "defines.h"
|
||||||
|
|
||||||
#include "../ta-utils.h"
|
#include "../ta-utils.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "orders.h"
|
#include "orders.h"
|
||||||
|
@ -231,11 +230,11 @@ struct DivSong {
|
||||||
bool isDMF;
|
bool isDMF;
|
||||||
|
|
||||||
// system
|
// system
|
||||||
DivSystem system[32];
|
DivSystem system[DIV_MAX_CHIPS];
|
||||||
unsigned char systemLen;
|
unsigned char systemLen;
|
||||||
signed char systemVol[32];
|
signed char systemVol[DIV_MAX_CHIPS];
|
||||||
signed char systemPan[32];
|
signed char systemPan[DIV_MAX_CHIPS];
|
||||||
DivConfig systemFlags[32];
|
DivConfig systemFlags[DIV_MAX_CHIPS];
|
||||||
|
|
||||||
// song information
|
// song information
|
||||||
String name, author, systemName;
|
String name, author, systemName;
|
||||||
|
@ -429,7 +428,7 @@ struct DivSong {
|
||||||
snNoLowPeriods(false),
|
snNoLowPeriods(false),
|
||||||
disableSampleMacro(false),
|
disableSampleMacro(false),
|
||||||
autoSystem(true) {
|
autoSystem(true) {
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
system[i]=DIV_SYSTEM_NULL;
|
system[i]=DIV_SYSTEM_NULL;
|
||||||
systemVol[i]=64;
|
systemVol[i]=64;
|
||||||
systemPan[i]=0;
|
systemPan[i]=0;
|
||||||
|
|
|
@ -984,15 +984,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
||||||
w->writeI(0); // will be written later
|
w->writeI(0); // will be written later
|
||||||
w->writeI(version);
|
w->writeI(version);
|
||||||
|
|
||||||
bool willExport[32];
|
bool willExport[DIV_MAX_CHIPS];
|
||||||
bool isSecond[32];
|
bool isSecond[DIV_MAX_CHIPS];
|
||||||
int streamIDs[32];
|
int streamIDs[DIV_MAX_CHIPS];
|
||||||
double loopTimer[DIV_MAX_CHANS];
|
double loopTimer[DIV_MAX_CHANS];
|
||||||
double loopFreq[DIV_MAX_CHANS];
|
double loopFreq[DIV_MAX_CHANS];
|
||||||
int loopSample[DIV_MAX_CHANS];
|
int loopSample[DIV_MAX_CHANS];
|
||||||
bool sampleDir[DIV_MAX_CHANS];
|
bool sampleDir[DIV_MAX_CHANS];
|
||||||
std::vector<unsigned int> chipVol;
|
std::vector<unsigned int> chipVol;
|
||||||
std::vector<DivDelayedWrite> delayedWrites[32];
|
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
|
||||||
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
||||||
|
|
||||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||||
|
|
|
@ -58,11 +58,21 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
||||||
min=reader.readI();
|
min=reader.readI();
|
||||||
max=reader.readI();
|
max=reader.readI();
|
||||||
|
|
||||||
if (len>256 || min!=0 || max>255) {
|
if (len>256) {
|
||||||
logV("invalid len/min/max: %d %d %d",len,min,max);
|
logE("invalid len: %d",len);
|
||||||
return DIV_DATA_INVALID_DATA;
|
return DIV_DATA_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (min!=0) {
|
||||||
|
logW("invalid min %d",min);
|
||||||
|
min=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max>255) {
|
||||||
|
logW("invalid max %d",max);
|
||||||
|
max=255;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<len; i++) {
|
for (int i=0; i<len; i++) {
|
||||||
data[i]=reader.readI();
|
data[i]=reader.readI();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ const char* aboutLine[]={
|
||||||
"freq-mod",
|
"freq-mod",
|
||||||
"iyatemu",
|
"iyatemu",
|
||||||
"JayBOB18",
|
"JayBOB18",
|
||||||
|
"Jimmy-DS",
|
||||||
"kleeder",
|
"kleeder",
|
||||||
"jaezu",
|
"jaezu",
|
||||||
"Laggy",
|
"Laggy",
|
||||||
|
@ -87,7 +88,7 @@ const char* aboutLine[]={
|
||||||
"Mahbod Karamoozian",
|
"Mahbod Karamoozian",
|
||||||
"Miker",
|
"Miker",
|
||||||
"nicco1690",
|
"nicco1690",
|
||||||
"NikonTeen",
|
"<nk>",
|
||||||
"potatoTeto",
|
"potatoTeto",
|
||||||
"psxdominator",
|
"psxdominator",
|
||||||
"Raijin",
|
"Raijin",
|
||||||
|
|
|
@ -693,7 +693,7 @@ void FurnaceGUI::actualSampleList() {
|
||||||
DivDispatch* dispatch=e->getDispatch(j);
|
DivDispatch* dispatch=e->getDispatch(j);
|
||||||
if (dispatch==NULL) continue;
|
if (dispatch==NULL) continue;
|
||||||
|
|
||||||
for (int k=0; k<4; k++) {
|
for (int k=0; k<DIV_MAX_SAMPLE_TYPE; k++) {
|
||||||
if (dispatch->getSampleMemCapacity(k)==0) continue;
|
if (dispatch->getSampleMemCapacity(k)==0) continue;
|
||||||
if (!dispatch->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
|
if (!dispatch->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
|
||||||
memWarning=true;
|
memWarning=true;
|
||||||
|
|
|
@ -522,7 +522,7 @@ void FurnaceGUI::doAction(int what) {
|
||||||
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->curPat[cursor.xCoarse].effectCols++;
|
e->curPat[cursor.xCoarse].effectCols++;
|
||||||
if (e->curPat[cursor.xCoarse].effectCols>8) e->curPat[cursor.xCoarse].effectCols=8;
|
if (e->curPat[cursor.xCoarse].effectCols>DIV_MAX_EFFECTS) e->curPat[cursor.xCoarse].effectCols=DIV_MAX_EFFECTS;
|
||||||
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;
|
||||||
|
|
|
@ -19,7 +19,22 @@
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "IconsFontAwesome4.h"
|
#include "IconsFontAwesome4.h"
|
||||||
#include <imgui.h>
|
#include <fmt/printf.h>
|
||||||
|
|
||||||
|
// 0: all directions
|
||||||
|
// 1: half
|
||||||
|
// 2: quarter
|
||||||
|
float mobileButtonAngles[3][8]={
|
||||||
|
{0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875},
|
||||||
|
{0.8, 0.933333, 0.066667, 0.2, 0.8, 0.933333, 0.066667, 0.2},
|
||||||
|
{0.75, 0.833333, 0.916667, 0.0, 0.75, 0.833333, 0.916667, 0.0}
|
||||||
|
};
|
||||||
|
|
||||||
|
float mobileButtonDistances[3][8]={
|
||||||
|
{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
|
||||||
|
{1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0},
|
||||||
|
{1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0}
|
||||||
|
};
|
||||||
|
|
||||||
void FurnaceGUI::drawMobileControls() {
|
void FurnaceGUI::drawMobileControls() {
|
||||||
float timeScale=1.0f/(60.0f*ImGui::GetIO().DeltaTime);
|
float timeScale=1.0f/(60.0f*ImGui::GetIO().DeltaTime);
|
||||||
|
@ -48,12 +63,98 @@ void FurnaceGUI::drawMobileControls() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dragMobileEditButton) {
|
||||||
|
if (ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>ImGui::GetIO().ConfigInertialScrollToleranceSqr) {
|
||||||
|
mobileEditButtonPos.x=((ImGui::GetMousePos().x/canvasW)-((portrait?0.16*canvasW:0.16*canvasH)/2)/canvasW);
|
||||||
|
mobileEditButtonPos.y=((ImGui::GetMousePos().y/canvasH)-((portrait?0.16*canvasW:0.16*canvasH)/2)/canvasH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileEditButtonPos.x<0) mobileEditButtonPos.x=0;
|
||||||
|
if (mobileEditButtonPos.x>1) mobileEditButtonPos.x=1;
|
||||||
|
if (mobileEditButtonPos.y<0) mobileEditButtonPos.y=0;
|
||||||
|
if (mobileEditButtonPos.y>1) mobileEditButtonPos.y=1;
|
||||||
|
|
||||||
|
if (mobileEdit) {
|
||||||
|
mobileEditAnim+=ImGui::GetIO().DeltaTime*2.0;
|
||||||
|
if (mobileEditAnim>1.0f) {
|
||||||
|
mobileEditAnim=1.0f;
|
||||||
|
} else {
|
||||||
|
WAKE_UP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mobileEditAnim-=ImGui::GetIO().DeltaTime*2.0;
|
||||||
|
if (mobileEditAnim<0.0f) {
|
||||||
|
mobileEditAnim=0.0f;
|
||||||
|
} else {
|
||||||
|
WAKE_UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileEditAnim>0.0f) {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0.0f,0.0f));
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(canvasW,canvasH));
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(mobileEditButtonPos.x*canvasW, mobileEditButtonPos.y*canvasH));
|
||||||
|
ImGui::SetNextWindowSize(portrait?ImVec2(0.16*canvasW,0.16*canvasW):ImVec2(0.16*canvasH,0.16*canvasH));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0.0f,0.0f));
|
||||||
|
if (ImGui::Begin("MobileEdit",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoDecoration)) {
|
||||||
|
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) && mobileEdit) {
|
||||||
|
mobileEdit=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileEditAnim>0.0f) {
|
||||||
|
int curButtonPos=0;
|
||||||
|
float buttonDir, buttonDist;
|
||||||
|
float buttonMirrorX=1.0f;
|
||||||
|
float buttonMirrorY=1.0f;
|
||||||
|
|
||||||
|
int buttonLayout=0;
|
||||||
|
|
||||||
|
for (int i=0; i<8; i++) {
|
||||||
|
float anim=(mobileEditAnim*5)-(float)i*0.5;
|
||||||
|
if (anim<0.0f) anim=0.0f;
|
||||||
|
if (anim>1.0f) anim=1.0f;
|
||||||
|
|
||||||
|
buttonDir=mobileButtonAngles[buttonLayout][curButtonPos];
|
||||||
|
buttonDist=mobileButtonDistances[buttonLayout][curButtonPos]*mobileEditButtonSize.x*1.6f;
|
||||||
|
|
||||||
|
ImGui::SetCursorPos(ImVec2(
|
||||||
|
(mobileEditButtonPos.x*canvasW)+cos(buttonDir*2.0*M_PI)*buttonDist*buttonMirrorX*anim,
|
||||||
|
(mobileEditButtonPos.y*canvasH)+sin(buttonDir*2.0*M_PI)*buttonDist*buttonMirrorY*anim
|
||||||
|
));
|
||||||
|
ImGui::Button(fmt::sprintf("%d",i+1).c_str(),mobileEditButtonSize);
|
||||||
|
|
||||||
|
curButtonPos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetCursorPos(ImVec2(mobileEditButtonPos.x*canvasW,mobileEditButtonPos.y*canvasH));
|
||||||
|
} else {
|
||||||
|
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
||||||
|
mobileEditButtonSize=ImVec2(avail,avail);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Edit",mobileEditButtonSize)) {
|
||||||
|
// click
|
||||||
|
if (ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]<=ImGui::GetIO().ConfigInertialScrollToleranceSqr) {
|
||||||
|
mobileEdit=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemClicked() && !mobileEdit) {
|
||||||
|
dragMobileEditButton=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(portrait?ImVec2(0.0f,((1.0-mobileMenuPos*0.65)*canvasH)-(0.16*canvasW)):ImVec2(0.5*canvasW*mobileMenuPos,0.0f));
|
ImGui::SetNextWindowPos(portrait?ImVec2(0.0f,((1.0-mobileMenuPos*0.65)*canvasH)-(0.16*canvasW)):ImVec2(0.5*canvasW*mobileMenuPos,0.0f));
|
||||||
ImGui::SetNextWindowSize(portrait?ImVec2(canvasW,0.16*canvasW):ImVec2(0.16*canvasH,canvasH));
|
ImGui::SetNextWindowSize(portrait?ImVec2(canvasW,0.16*canvasW):ImVec2(0.16*canvasH,canvasH));
|
||||||
if (ImGui::Begin("Mobile Controls",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|globalWinFlags)) {
|
if (ImGui::Begin("Mobile Controls",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|globalWinFlags)) {
|
||||||
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
||||||
ImVec2 buttonSize=ImVec2(avail,avail);
|
ImVec2 buttonSize=ImVec2(avail,avail);
|
||||||
|
|
||||||
const char* mobButtonName=ICON_FA_CHEVRON_RIGHT "##MobileMenu";
|
const char* mobButtonName=ICON_FA_CHEVRON_RIGHT "##MobileMenu";
|
||||||
if (portrait) mobButtonName=ICON_FA_CHEVRON_UP "##MobileMenu";
|
if (portrait) mobButtonName=ICON_FA_CHEVRON_UP "##MobileMenu";
|
||||||
if (mobileMenuOpen) {
|
if (mobileMenuOpen) {
|
||||||
|
|
|
@ -119,7 +119,7 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
||||||
for (int j=0; j<e->curSubSong->patLen; j++) {
|
for (int j=0; j<e->curSubSong->patLen; j++) {
|
||||||
for (int k=0; k<32; k++) {
|
for (int k=0; k<DIV_MAX_COLS; 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(subSong,i,e->curOrders->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]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,20 +220,20 @@ void FurnaceGUI::doReplace() {
|
||||||
UndoStep us;
|
UndoStep us;
|
||||||
us.type=GUI_UNDO_REPLACE;
|
us.type=GUI_UNDO_REPLACE;
|
||||||
|
|
||||||
short prevVal[32];
|
short prevVal[DIV_MAX_COLS];
|
||||||
memset(prevVal,0,32*sizeof(short));
|
memset(prevVal,0,DIV_MAX_COLS*sizeof(short));
|
||||||
|
|
||||||
for (FurnaceGUIQueryResult& i: curQueryResults) {
|
for (FurnaceGUIQueryResult& i: curQueryResults) {
|
||||||
int patIndex=e->song.subsong[i.subsong]->orders.ord[i.x][i.order];
|
int patIndex=e->song.subsong[i.subsong]->orders.ord[i.x][i.order];
|
||||||
DivPattern* p=e->song.subsong[i.subsong]->pat[i.x].getPattern(patIndex,true);
|
DivPattern* p=e->song.subsong[i.subsong]->pat[i.x].getPattern(patIndex,true);
|
||||||
if (touched[i.x]==NULL) {
|
if (touched[i.x]==NULL) {
|
||||||
touched[i.x]=new bool[256*256];
|
touched[i.x]=new bool[DIV_MAX_PATTERNS*DIV_MAX_ROWS];
|
||||||
memset(touched[i.x],0,256*256*sizeof(bool));
|
memset(touched[i.x],0,DIV_MAX_PATTERNS*DIV_MAX_ROWS*sizeof(bool));
|
||||||
}
|
}
|
||||||
if (touched[i.x][(patIndex<<8)|i.y]) continue;
|
if (touched[i.x][(patIndex<<8)|i.y]) continue;
|
||||||
touched[i.x][(patIndex<<8)|i.y]=true;
|
touched[i.x][(patIndex<<8)|i.y]=true;
|
||||||
|
|
||||||
memcpy(prevVal,p->data[i.y],32*sizeof(short));
|
memcpy(prevVal,p->data[i.y],DIV_MAX_COLS*sizeof(short));
|
||||||
|
|
||||||
if (queryReplaceNoteDo) {
|
if (queryReplaceNoteDo) {
|
||||||
switch (queryReplaceNoteMode) {
|
switch (queryReplaceNoteMode) {
|
||||||
|
@ -462,7 +462,7 @@ void FurnaceGUI::doReplace() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// issue undo step
|
// issue undo step
|
||||||
for (int j=0; j<32; j++) {
|
for (int j=0; j<DIV_MAX_COLS; j++) {
|
||||||
if (p->data[i.y][j]!=prevVal[j]) {
|
if (p->data[i.y][j]!=prevVal[j]) {
|
||||||
us.pat.push_back(UndoPatternData(i.subsong,i.x,patIndex,i.y,j,prevVal[j],p->data[i.y][j]));
|
us.pat.push_back(UndoPatternData(i.subsong,i.x,patIndex,i.y,j,prevVal[j],p->data[i.y][j]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2939,6 +2939,9 @@ void FurnaceGUI::pointUp(int x, int y, int button) {
|
||||||
mobileMenuOpen=(mobileMenuPos>=0.15f);
|
mobileMenuOpen=(mobileMenuPos>=0.15f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dragMobileEditButton) {
|
||||||
|
dragMobileEditButton=false;
|
||||||
|
}
|
||||||
if (selecting) {
|
if (selecting) {
|
||||||
if (!selectingFull) cursor=selEnd;
|
if (!selectingFull) cursor=selEnd;
|
||||||
finishSelection();
|
finishSelection();
|
||||||
|
@ -4763,7 +4766,7 @@ bool FurnaceGUI::loop() {
|
||||||
if (ImGui::Button("Orders")) {
|
if (ImGui::Button("Orders")) {
|
||||||
stop();
|
stop();
|
||||||
e->lockEngine([this]() {
|
e->lockEngine([this]() {
|
||||||
memset(e->curOrders->ord,0,DIV_MAX_CHANS*256);
|
memset(e->curOrders->ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||||
e->curSubSong->ordersLen=1;
|
e->curSubSong->ordersLen=1;
|
||||||
});
|
});
|
||||||
e->setOrder(0);
|
e->setOrder(0);
|
||||||
|
@ -4779,8 +4782,8 @@ bool FurnaceGUI::loop() {
|
||||||
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->curPat[i].getPattern(e->curOrders->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,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<DIV_MAX_ROWS; j++) {
|
||||||
pat->data[j][0]=0;
|
pat->data[j][0]=0;
|
||||||
pat->data[j][1]=0;
|
pat->data[j][1]=0;
|
||||||
}
|
}
|
||||||
|
@ -5656,6 +5659,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
pendingInsSingle(false),
|
pendingInsSingle(false),
|
||||||
displayPendingRawSample(false),
|
displayPendingRawSample(false),
|
||||||
snesFilterHex(false),
|
snesFilterHex(false),
|
||||||
|
mobileEdit(false),
|
||||||
vgmExportVersion(0x171),
|
vgmExportVersion(0x171),
|
||||||
drawHalt(10),
|
drawHalt(10),
|
||||||
zsmExportTickRate(60),
|
zsmExportTickRate(60),
|
||||||
|
@ -5664,6 +5668,9 @@ FurnaceGUI::FurnaceGUI():
|
||||||
displayInsTypeListMakeInsSample(-1),
|
displayInsTypeListMakeInsSample(-1),
|
||||||
mobileMenuPos(0.0f),
|
mobileMenuPos(0.0f),
|
||||||
autoButtonSize(0.0f),
|
autoButtonSize(0.0f),
|
||||||
|
mobileEditAnim(0.0f),
|
||||||
|
mobileEditButtonPos(0.7f,0.7f),
|
||||||
|
mobileEditButtonSize(60.0f,60.0f),
|
||||||
curSysSection(NULL),
|
curSysSection(NULL),
|
||||||
pendingRawSampleDepth(8),
|
pendingRawSampleDepth(8),
|
||||||
pendingRawSampleChannels(1),
|
pendingRawSampleChannels(1),
|
||||||
|
@ -5794,12 +5801,14 @@ FurnaceGUI::FurnaceGUI():
|
||||||
orderScrollLocked(false),
|
orderScrollLocked(false),
|
||||||
orderScrollTolerance(false),
|
orderScrollTolerance(false),
|
||||||
dragMobileMenu(false),
|
dragMobileMenu(false),
|
||||||
|
dragMobileEditButton(false),
|
||||||
curWindow(GUI_WINDOW_NOTHING),
|
curWindow(GUI_WINDOW_NOTHING),
|
||||||
nextWindow(GUI_WINDOW_NOTHING),
|
nextWindow(GUI_WINDOW_NOTHING),
|
||||||
curWindowLast(GUI_WINDOW_NOTHING),
|
curWindowLast(GUI_WINDOW_NOTHING),
|
||||||
curWindowThreadSafe(GUI_WINDOW_NOTHING),
|
curWindowThreadSafe(GUI_WINDOW_NOTHING),
|
||||||
lastPatternWidth(0.0f),
|
lastPatternWidth(0.0f),
|
||||||
longThreshold(0.48f),
|
longThreshold(0.48f),
|
||||||
|
buttonLongThreshold(0.20f),
|
||||||
latchNote(-1),
|
latchNote(-1),
|
||||||
latchIns(-2),
|
latchIns(-2),
|
||||||
latchVol(-1),
|
latchVol(-1),
|
||||||
|
@ -5923,6 +5932,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
sampleSelStart(-1),
|
sampleSelStart(-1),
|
||||||
sampleSelEnd(-1),
|
sampleSelEnd(-1),
|
||||||
sampleInfo(true),
|
sampleInfo(true),
|
||||||
|
sampleCompatRate(false),
|
||||||
sampleDragActive(false),
|
sampleDragActive(false),
|
||||||
sampleDragMode(false),
|
sampleDragMode(false),
|
||||||
sampleDrag16(false),
|
sampleDrag16(false),
|
||||||
|
@ -6022,7 +6032,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
valueKeys[SDLK_KP_8]=8;
|
valueKeys[SDLK_KP_8]=8;
|
||||||
valueKeys[SDLK_KP_9]=9;
|
valueKeys[SDLK_KP_9]=9;
|
||||||
|
|
||||||
memset(willExport,1,32*sizeof(bool));
|
memset(willExport,1,DIV_MAX_CHIPS*sizeof(bool));
|
||||||
|
|
||||||
peak[0]=0;
|
peak[0]=0;
|
||||||
peak[1]=0;
|
peak[1]=0;
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<longThreshold && ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]<=ImGui::GetIO().ConfigInertialScrollToleranceSqr)
|
#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<longThreshold && ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]<=ImGui::GetIO().ConfigInertialScrollToleranceSqr)
|
||||||
|
#define CHECK_BUTTON_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=buttonLongThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<buttonLongThreshold)
|
||||||
// for now
|
// for now
|
||||||
#define NOTIFY_LONG_HOLD \
|
#define NOTIFY_LONG_HOLD \
|
||||||
if (vibrator && vibratorAvailable) { \
|
if (vibrator && vibratorAvailable) { \
|
||||||
|
@ -1088,14 +1088,16 @@ class FurnaceGUI {
|
||||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
|
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
|
||||||
bool willExport[32];
|
bool mobileEdit;
|
||||||
|
bool willExport[DIV_MAX_CHIPS];
|
||||||
int vgmExportVersion;
|
int vgmExportVersion;
|
||||||
int drawHalt;
|
int drawHalt;
|
||||||
int zsmExportTickRate;
|
int zsmExportTickRate;
|
||||||
int macroPointSize;
|
int macroPointSize;
|
||||||
int waveEditStyle;
|
int waveEditStyle;
|
||||||
int displayInsTypeListMakeInsSample;
|
int displayInsTypeListMakeInsSample;
|
||||||
float mobileMenuPos, autoButtonSize;
|
float mobileMenuPos, autoButtonSize, mobileEditAnim;
|
||||||
|
ImVec2 mobileEditButtonPos, mobileEditButtonSize;
|
||||||
const int* curSysSection;
|
const int* curSysSection;
|
||||||
DivInstrumentFM opllPreview;
|
DivInstrumentFM opllPreview;
|
||||||
|
|
||||||
|
@ -1435,13 +1437,14 @@ class FurnaceGUI {
|
||||||
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
||||||
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
|
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
|
||||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||||
bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu;
|
bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton;
|
||||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||||
float peak[2];
|
float peak[2];
|
||||||
float patChanX[DIV_MAX_CHANS+1];
|
float patChanX[DIV_MAX_CHANS+1];
|
||||||
float patChanSlideY[DIV_MAX_CHANS+1];
|
float patChanSlideY[DIV_MAX_CHANS+1];
|
||||||
float lastPatternWidth, longThreshold;
|
float lastPatternWidth, longThreshold;
|
||||||
|
float buttonLongThreshold;
|
||||||
String nextDesc;
|
String nextDesc;
|
||||||
String nextDescName;
|
String nextDescName;
|
||||||
|
|
||||||
|
@ -1616,7 +1619,7 @@ class FurnaceGUI {
|
||||||
int resampleStrat;
|
int resampleStrat;
|
||||||
float amplifyVol;
|
float amplifyVol;
|
||||||
int sampleSelStart, sampleSelEnd;
|
int sampleSelStart, sampleSelEnd;
|
||||||
bool sampleInfo;
|
bool sampleInfo, sampleCompatRate;
|
||||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||||
void* sampleDragTarget;
|
void* sampleDragTarget;
|
||||||
ImVec2 sampleDragStart;
|
ImVec2 sampleDragStart;
|
||||||
|
|
|
@ -177,10 +177,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->curOrders->ord[k][i]<0xff) e->curOrders->ord[k][i]++;
|
if (e->curOrders->ord[k][i]<(unsigned char)(DIV_MAX_PATTERNS-1)) e->curOrders->ord[k][i]++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (e->curOrders->ord[j][i]<0xff) e->curOrders->ord[j][i]++;
|
if (e->curOrders->ord[j][i]<(unsigned char)(DIV_MAX_PATTERNS-1)) e->curOrders->ord[j][i]++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
|
|
@ -30,8 +30,8 @@ void FurnaceGUI::drawPatManager() {
|
||||||
}
|
}
|
||||||
if (!patManagerOpen) return;
|
if (!patManagerOpen) return;
|
||||||
char id[1024];
|
char id[1024];
|
||||||
unsigned char isUsed[256];
|
unsigned char isUsed[DIV_MAX_PATTERNS];
|
||||||
bool isNull[256];
|
bool isNull[DIV_MAX_PATTERNS];
|
||||||
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
|
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
|
||||||
ImGui::Text("Global Tasks");
|
ImGui::Text("Global Tasks");
|
||||||
|
|
||||||
|
@ -52,19 +52,19 @@ void FurnaceGUI::drawPatManager() {
|
||||||
|
|
||||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
memset(isUsed,0,256);
|
memset(isUsed,0,DIV_MAX_PATTERNS);
|
||||||
memset(isNull,0,256*sizeof(bool));
|
memset(isNull,0,DIV_MAX_PATTERNS*sizeof(bool));
|
||||||
for (int j=0; j<e->curSubSong->ordersLen; j++) {
|
for (int j=0; j<e->curSubSong->ordersLen; j++) {
|
||||||
isUsed[e->curSubSong->orders.ord[i][j]]++;
|
isUsed[e->curSubSong->orders.ord[i][j]]++;
|
||||||
}
|
}
|
||||||
for (int j=0; j<256; j++) {
|
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||||
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
|
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%s",e->getChannelShortName(i));
|
ImGui::Text("%s",e->getChannelShortName(i));
|
||||||
|
|
||||||
ImGui::PushID(1000+i);
|
ImGui::PushID(1000+i);
|
||||||
for (int k=0; k<256; k++) {
|
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
snprintf(id,1023,"%.2X",k);
|
snprintf(id,1023,"%.2X",k);
|
||||||
|
|
|
@ -875,11 +875,11 @@ void FurnaceGUI::drawPattern() {
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
|
ImGui::BeginDisabled(e->curPat[i].effectCols>=DIV_MAX_EFFECTS);
|
||||||
snprintf(chanID,2048,">##_RCH%d",i);
|
snprintf(chanID,2048,">##_RCH%d",i);
|
||||||
if (ImGui::SmallButton(chanID)) {
|
if (ImGui::SmallButton(chanID)) {
|
||||||
e->curPat[i].effectCols++;
|
e->curPat[i].effectCols++;
|
||||||
if (e->curPat[i].effectCols>8) e->curPat[i].effectCols=8;
|
if (e->curPat[i].effectCols>DIV_MAX_EFFECTS) e->curPat[i].effectCols=DIV_MAX_EFFECTS;
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,32 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
bool isChipVisible[DIV_MAX_CHIPS];
|
||||||
|
bool isTypeVisible[DIV_MAX_SAMPLE_TYPE];
|
||||||
|
bool isMemVisible[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||||
|
bool isMemWarning[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||||
|
memset(isChipVisible,0,DIV_MAX_CHIPS*sizeof(bool));
|
||||||
|
memset(isTypeVisible,0,DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||||
|
memset(isMemVisible,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||||
|
memset(isMemWarning,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
DivDispatch* dispatch=e->getDispatch(i);
|
||||||
|
if (dispatch==NULL) continue;
|
||||||
|
|
||||||
|
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||||
|
if (dispatch->getSampleMemCapacity(j)==0) continue;
|
||||||
|
isChipVisible[i]=true;
|
||||||
|
isTypeVisible[j]=true;
|
||||||
|
isMemVisible[j][i]=true;
|
||||||
|
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int selColumns=1;
|
||||||
|
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||||
|
if (isChipVisible[i]) selColumns++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("SampleProps",(selColumns>1)?4:3,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
||||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::Button(sampleInfo?(ICON_FA_CHEVRON_UP "##SECollapse"):(ICON_FA_CHEVRON_DOWN "##SECollapse"))) {
|
if (ImGui::Button(sampleInfo?(ICON_FA_CHEVRON_UP "##SECollapse"):(ICON_FA_CHEVRON_DOWN "##SECollapse"))) {
|
||||||
|
@ -143,7 +168,20 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("Info");
|
ImGui::Text("Info");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Rate");
|
pushToggleColors(!sampleCompatRate);
|
||||||
|
if (ImGui::Button("Rate")) {
|
||||||
|
sampleCompatRate=false;
|
||||||
|
}
|
||||||
|
popToggleColors();
|
||||||
|
ImGui::SameLine();
|
||||||
|
pushToggleColors(sampleCompatRate);
|
||||||
|
if (ImGui::Button("Compat Rate")) {
|
||||||
|
sampleCompatRate=true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("used in DefleMask-compatible sample mode (17xx), in where samples are mapped to an octave.");
|
||||||
|
}
|
||||||
|
popToggleColors();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
bool doLoop=(sample->isLoopable());
|
bool doLoop=(sample->isLoopable());
|
||||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||||
|
@ -164,8 +202,11 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selColumns>1) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Chips");
|
ImGui::Text("Chips");
|
||||||
|
}
|
||||||
|
|
||||||
if (sampleInfo) {
|
if (sampleInfo) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
@ -216,21 +257,110 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||||
ImGui::Text("C-4 (Hz)");
|
int sampleNote=round(64.0+(128.0*12.0*log((double)targetRate/8363.0)/log(2.0)));
|
||||||
ImGui::SameLine();
|
int sampleNoteCoarse=60+(sampleNote>>7);
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
int sampleNoteFine=(sampleNote&127)-64;
|
||||||
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED
|
|
||||||
if (sample->centerRate<100) sample->centerRate=100;
|
if (sampleNoteCoarse<0) {
|
||||||
if (sample->centerRate>65535) sample->centerRate=65535;
|
sampleNoteCoarse=0;
|
||||||
|
sampleNoteFine=-64;
|
||||||
|
}
|
||||||
|
if (sampleNoteCoarse>119) {
|
||||||
|
sampleNoteCoarse=119;
|
||||||
|
sampleNoteFine=63;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("Compat");
|
bool coarseChanged=false;
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("Hz");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
if (ImGui::InputInt("##SampleRate",&targetRate,10,200)) { MARK_MODIFIED
|
||||||
if (sample->rate<100) sample->rate=100;
|
if (targetRate<100) targetRate=100;
|
||||||
if (sample->rate>96000) sample->rate=96000;
|
if (targetRate>384000) targetRate=384000;
|
||||||
|
|
||||||
|
if (sampleCompatRate) {
|
||||||
|
sample->rate=targetRate;
|
||||||
|
} else {
|
||||||
|
sample->centerRate=targetRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Note");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::BeginCombo("##SampleNote",noteNames[sampleNoteCoarse+60])) {
|
||||||
|
char temp[1024];
|
||||||
|
for (int i=0; i<120; i++) {
|
||||||
|
snprintf(temp,1023,"%s##_SRN%d",noteNames[i+60],i);
|
||||||
|
if (ImGui::Selectable(temp,i==sampleNoteCoarse)) {
|
||||||
|
sampleNoteCoarse=i;
|
||||||
|
coarseChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
} else if (ImGui::IsItemHovered()) {
|
||||||
|
if (wheelY!=0) {
|
||||||
|
sampleNoteCoarse-=wheelY;
|
||||||
|
if (sampleNoteCoarse<0) {
|
||||||
|
sampleNoteCoarse=0;
|
||||||
|
sampleNoteFine=-64;
|
||||||
|
}
|
||||||
|
if (sampleNoteCoarse>119) {
|
||||||
|
sampleNoteCoarse=119;
|
||||||
|
sampleNoteFine=63;
|
||||||
|
}
|
||||||
|
coarseChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coarseChanged) { MARK_MODIFIED
|
||||||
|
sampleNote=((sampleNoteCoarse-60)<<7)+sampleNoteFine;
|
||||||
|
|
||||||
|
targetRate=8363.0*pow(2.0,(double)sampleNote/(128.0*12.0));
|
||||||
|
if (targetRate<100) targetRate=100;
|
||||||
|
if (targetRate>384000) targetRate=384000;
|
||||||
|
|
||||||
|
if (sampleCompatRate) {
|
||||||
|
sample->rate=targetRate;
|
||||||
|
} else {
|
||||||
|
sample->centerRate=targetRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Fine");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
int prevFine=sampleNoteFine;
|
||||||
|
int prevSampleRate=targetRate;
|
||||||
|
if (ImGui::InputInt("##SampleFine",&sampleNoteFine,1,10)) { MARK_MODIFIED
|
||||||
|
if (sampleNoteFine>63) sampleNoteFine=63;
|
||||||
|
if (sampleNoteFine<-64) sampleNoteFine=-64;
|
||||||
|
|
||||||
|
sampleNote=((sampleNoteCoarse-60)<<7)+sampleNoteFine;
|
||||||
|
|
||||||
|
targetRate=round(8363.0*pow(2.0,(double)sampleNote/(128.0*12.0)));
|
||||||
|
|
||||||
|
if (targetRate==prevSampleRate) {
|
||||||
|
if (prevFine==sampleNoteFine) {
|
||||||
|
// do nothing
|
||||||
|
} else if (prevFine>sampleNoteFine) { // coarse incr/decr due to precision loss
|
||||||
|
targetRate--;
|
||||||
|
} else {
|
||||||
|
targetRate++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetRate<100) targetRate=100;
|
||||||
|
if (targetRate>384000) targetRate=384000;
|
||||||
|
|
||||||
|
if (sampleCompatRate) {
|
||||||
|
sample->rate=targetRate;
|
||||||
|
} else {
|
||||||
|
sample->centerRate=targetRate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
@ -298,35 +428,8 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
if (selColumns>1) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
bool isChipVisible[32];
|
|
||||||
bool isTypeVisible[4];
|
|
||||||
bool isMemVisible[4][32];
|
|
||||||
bool isMemWarning[4][32];
|
|
||||||
memset(isChipVisible,0,32*sizeof(bool));
|
|
||||||
memset(isTypeVisible,0,4*sizeof(bool));
|
|
||||||
memset(isMemVisible,0,32*4*sizeof(bool));
|
|
||||||
memset(isMemWarning,0,32*4*sizeof(bool));
|
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
|
||||||
DivDispatch* dispatch=e->getDispatch(i);
|
|
||||||
if (dispatch==NULL) continue;
|
|
||||||
|
|
||||||
for (int j=0; j<4; j++) {
|
|
||||||
if (dispatch->getSampleMemCapacity(j)==0) continue;
|
|
||||||
isChipVisible[i]=true;
|
|
||||||
isTypeVisible[j]=true;
|
|
||||||
isMemVisible[j][i]=true;
|
|
||||||
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int selColumns=1;
|
|
||||||
for (int i=0; i<32; i++) {
|
|
||||||
if (isChipVisible[i]) selColumns++;
|
|
||||||
}
|
|
||||||
if (selColumns<=1) {
|
|
||||||
ImGui::Text("NO CHIPS LESS GOOO");
|
|
||||||
} else {
|
|
||||||
if (ImGui::BeginTable("SEChipSel",selColumns,ImGuiTableFlags_SizingFixedSame)) {
|
if (ImGui::BeginTable("SEChipSel",selColumns,ImGuiTableFlags_SizingFixedSame)) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
@ -336,7 +439,7 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::Text("%d",i+1);
|
ImGui::Text("%d",i+1);
|
||||||
}
|
}
|
||||||
char id[1024];
|
char id[1024];
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||||
if (!isTypeVisible[i]) continue;
|
if (!isTypeVisible[i]) continue;
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
@ -873,7 +976,26 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here
|
// time
|
||||||
|
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||||
|
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||||
|
|
||||||
|
ImVec2 size=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetFontSize()+2.0*dpiScale);
|
||||||
|
|
||||||
|
ImVec2 minArea=window->DC.CursorPos;
|
||||||
|
ImVec2 maxArea=ImVec2(
|
||||||
|
minArea.x+size.x,
|
||||||
|
minArea.y+size.y
|
||||||
|
);
|
||||||
|
ImRect rect=ImRect(minArea,maxArea);
|
||||||
|
ImGuiStyle& style=ImGui::GetStyle();
|
||||||
|
|
||||||
|
ImGui::ItemSize(size,style.FramePadding.y);
|
||||||
|
if (ImGui::ItemAdd(rect,ImGui::GetID("SETime"))) {
|
||||||
|
dl->AddText(minArea,0xffffffff,"0");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 avail=ImGui::GetContentRegionAvail(); // sample view size determined here
|
||||||
// don't do this. reason: mobile.
|
// don't do this. reason: mobile.
|
||||||
/*if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) {
|
/*if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) {
|
||||||
avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f);
|
avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f);
|
||||||
|
@ -1101,6 +1223,8 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
String statusBar=sampleDragMode?"Draw":"Select";
|
String statusBar=sampleDragMode?"Draw":"Select";
|
||||||
|
String statusBar2="";
|
||||||
|
String statusBar3=fmt::sprintf("%d samples, %d bytes",sample->samples,sample->getCurBufLen());
|
||||||
bool drawSelection=false;
|
bool drawSelection=false;
|
||||||
|
|
||||||
if (!sampleDragMode) {
|
if (!sampleDragMode) {
|
||||||
|
@ -1181,12 +1305,15 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
}
|
}
|
||||||
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
|
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
|
||||||
if (posX>=0) {
|
if (posX>=0) {
|
||||||
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
|
statusBar2=fmt::sprintf("(%d, %d)",posX,posY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->isPreviewingSample()) {
|
if (e->isPreviewingSample()) {
|
||||||
statusBar+=fmt::sprintf(" | %.2fHz",e->getSamplePreviewRate());
|
if (!statusBar2.empty()) {
|
||||||
|
statusBar2+=" | ";
|
||||||
|
}
|
||||||
|
statusBar2+=fmt::sprintf("%.2fHz",e->getSamplePreviewRate());
|
||||||
|
|
||||||
int start=sampleSelStart;
|
int start=sampleSelStart;
|
||||||
int end=sampleSelEnd;
|
int end=sampleSelEnd;
|
||||||
|
@ -1269,7 +1396,23 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
|
||||||
ImGui::Text("%s",statusBar.c_str());
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0,0));
|
||||||
|
if (ImGui::BeginTable("SEStatus",3,ImGuiTableFlags_BordersInnerV)) {
|
||||||
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.7);
|
||||||
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.3);
|
||||||
|
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted(statusBar.c_str());
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted(statusBar2.c_str());
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted(statusBar3.c_str());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
|
||||||
int patLen=e->curSubSong->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>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS;
|
||||||
e->curSubSong->patLen=patLen;
|
e->curSubSong->patLen=patLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
|
||||||
int ordLen=e->curSubSong->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>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS;
|
||||||
e->curSubSong->ordersLen=ordLen;
|
e->curSubSong->ordersLen=ordLen;
|
||||||
if (curOrder>=ordLen) {
|
if (curOrder>=ordLen) {
|
||||||
setOrder(ordLen-1);
|
setOrder(ordLen-1);
|
||||||
|
|
|
@ -112,7 +112,7 @@ void FurnaceGUI::drawSysManager() {
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
if (e->song.systemLen<32) {
|
if (e->song.systemLen<DIV_MAX_CHIPS) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
|
|
@ -643,11 +643,11 @@ void FurnaceGUI::drawWaveEdit() {
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::Button("Scale Y")) {
|
if (ImGui::Button("Scale Y")) {
|
||||||
if (waveGenScaleY>0 && wave->max!=waveGenScaleY) e->lockEngine([this,wave]() {
|
if (waveGenScaleY>0 && wave->max!=(waveGenScaleY-1)) e->lockEngine([this,wave]() {
|
||||||
for (int i=0; i<wave->len; i++) {
|
for (int i=0; i<wave->len; i++) {
|
||||||
wave->data[i]=(wave->data[i]*(waveGenScaleY+1))/(wave->max+1);
|
wave->data[i]=(wave->data[i]*(waveGenScaleY+1))/(wave->max+1);
|
||||||
}
|
}
|
||||||
wave->max=waveGenScaleY;
|
wave->max=waveGenScaleY-1;
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue