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
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -275,6 +275,7 @@ jobs:
|
|||
cp -v ../LICENSE LICENSE.txt
|
||||
cp -v ../README.md README.txt
|
||||
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
|
||||
sha256sum ../${binPath}/furnace.exe > checksum.txt
|
||||
|
||||
popd
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ the coding style is described here:
|
|||
- no spaces in operations except for `||` and `&&`
|
||||
- no space between variable name and assignment
|
||||
- space between macro in string literals
|
||||
- space after comment delimiter
|
||||
- C++ pointer style: `void* variable` rather than `void *variable`
|
||||
- indent switch cases
|
||||
- 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;
|
||||
}
|
||||
case DIV_EXPORT_MODE_MANY_SYS: {
|
||||
SNDFILE* sf[32];
|
||||
SF_INFO si[32];
|
||||
String fname[32];
|
||||
SFWrapper sfWrap[32];
|
||||
SNDFILE* sf[DIV_MAX_CHIPS];
|
||||
SF_INFO si[DIV_MAX_CHIPS];
|
||||
String fname[DIV_MAX_CHIPS];
|
||||
SFWrapper sfWrap[DIV_MAX_CHIPS];
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
sf[i]=NULL;
|
||||
si[i].samplerate=got.rate;
|
||||
|
@ -915,7 +915,7 @@ void DivEngine::runExportThread() {
|
|||
float* outBuf[2];
|
||||
outBuf[0]=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++) {
|
||||
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
|
||||
}
|
||||
|
@ -1399,7 +1399,7 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64) {
|
|||
c.loadFromMemory(description);
|
||||
}
|
||||
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));
|
||||
if (song.system[index]==DIV_SYSTEM_NULL) {
|
||||
break;
|
||||
|
@ -1456,7 +1456,7 @@ void DivEngine::swapChannels(int src, int dest) {
|
|||
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[src][i]^=curOrders->ord[dest][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) {
|
||||
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;
|
||||
}
|
||||
curPat[ch].wipePatterns();
|
||||
|
@ -1649,8 +1649,8 @@ void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
|||
}
|
||||
|
||||
bool DivEngine::addSystem(DivSystem which) {
|
||||
if (song.systemLen>32) {
|
||||
lastError="max number of systems is 32";
|
||||
if (song.systemLen>DIV_MAX_CHIPS) {
|
||||
lastError=fmt::sprintf("max number of systems is %d",DIV_MAX_CHIPS);
|
||||
return false;
|
||||
}
|
||||
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++) {
|
||||
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];
|
||||
String prevChanName[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];
|
||||
|
||||
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];
|
||||
}
|
||||
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 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]->pat[j].data[k]=prevPat[swappedChannels[j]][k];
|
||||
}
|
||||
|
@ -2770,7 +2770,7 @@ void DivEngine::delInstrument(int index) {
|
|||
song.insLen=song.ins.size();
|
||||
for (int i=0; i<chans; i++) {
|
||||
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;
|
||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||
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) {
|
||||
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);
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
|
@ -3478,14 +3478,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
order[i]=curOrders->ord[i][curOrder];
|
||||
}
|
||||
} else {
|
||||
bool used[256];
|
||||
bool used[DIV_MAX_PATTERNS];
|
||||
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++) {
|
||||
used[curOrders->ord[i][j]]=true;
|
||||
}
|
||||
order[i]=0xff;
|
||||
for (int j=0; j<256; j++) {
|
||||
order[i]=(DIV_MAX_PATTERNS-1);
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (!used[j]) {
|
||||
order[i]=j;
|
||||
break;
|
||||
|
@ -3520,7 +3520,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
warnings="";
|
||||
BUSY_BEGIN_SOFT;
|
||||
for (int i=0; i<chans; i++) {
|
||||
|
@ -3528,14 +3528,14 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
logD("channel %d",i);
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
// 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);
|
||||
if (curPat[i].data[j]==NULL) {
|
||||
int origOrd=order[i];
|
||||
order[i]=j;
|
||||
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||
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);
|
||||
didNotFind=false;
|
||||
break;
|
||||
|
@ -3631,7 +3631,7 @@ void DivEngine::moveOrderDown() {
|
|||
void DivEngine::exchangeIns(int one, int two) {
|
||||
for (int i=0; i<chans; i++) {
|
||||
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;
|
||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
|
||||
|
|
|
@ -318,7 +318,7 @@ enum DivChanTypes {
|
|||
extern const char* cmdName[];
|
||||
|
||||
class DivEngine {
|
||||
DivDispatchContainer disCont[32];
|
||||
DivDispatchContainer disCont[DIV_MAX_CHIPS];
|
||||
TAAudio* output;
|
||||
TAAudioDesc want, got;
|
||||
String exportPath;
|
||||
|
|
|
@ -1581,7 +1581,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
unsigned int wavePtr[256];
|
||||
unsigned int samplePtr[256];
|
||||
unsigned int subSongPtr[256];
|
||||
unsigned int sysFlagsPtr[32];
|
||||
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
|
||||
std::vector<int> patPtr;
|
||||
int numberOfSubSongs=0;
|
||||
char magic[5];
|
||||
|
@ -1760,7 +1760,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
|
@ -1772,7 +1772,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subSong->ordersLen>256) {
|
||||
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
@ -1804,7 +1804,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
logD("systems:");
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
unsigned char sysID=reader.readC();
|
||||
ds.system[i]=systemFromFileFur(sysID);
|
||||
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
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
ds.systemVol[i]=reader.readC();
|
||||
if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) {
|
||||
ds.systemVol[i]/=4;
|
||||
|
@ -1834,15 +1834,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// 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
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
sysFlagsPtr[i]=reader.readI();
|
||||
}
|
||||
|
||||
// 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 ||
|
||||
ds.system[i]==DIV_SYSTEM_GENESIS_EXT ||
|
||||
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.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) {
|
||||
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++) {
|
||||
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);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
delete[] file;
|
||||
|
@ -2198,7 +2198,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
// read system flags
|
||||
if (ds.version>=119) {
|
||||
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 (!reader.seek(sysFlagsPtr[i],SEEK_SET)) {
|
||||
|
@ -2416,7 +2416,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (index<0 || index>255) {
|
||||
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||
logE("pattern index out of range!",i);
|
||||
lastError="pattern index out of range!";
|
||||
ds.unload();
|
||||
|
@ -4175,14 +4175,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
// fail if values are out of range
|
||||
/*
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("maximum song length is 256!");
|
||||
lastError="maximum song length is 256";
|
||||
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||
logE("maximum song length is %d!",DIV_MAX_PATTERNS);
|
||||
lastError=fmt::sprintf("maximum song length is %d",DIV_MAX_PATTERNS);
|
||||
return NULL;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
logE("maximum pattern length is 256!");
|
||||
lastError="maximum pattern length is 256";
|
||||
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||
logE("maximum pattern length is %d!",DIV_MAX_ROWS);
|
||||
lastError=fmt::sprintf("maximum pattern length is %d",DIV_MAX_ROWS);
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
|
@ -4236,18 +4236,18 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); 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;
|
||||
patsToWrite.push_back(PatToWrite(j,i,k));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool alreadyAdded[256];
|
||||
bool alreadyAdded[DIV_MAX_PATTERNS];
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); 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++) {
|
||||
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
||||
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->writeI(patsToWrite.size());
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (i>=song.systemLen) {
|
||||
w->writeC(0);
|
||||
} 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]);
|
||||
}
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
w->writeC(song.systemPan[i]);
|
||||
}
|
||||
|
||||
// chip flags (we'll seek here later)
|
||||
sysFlagsPtrSeek=w->tell();
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
w->writeI(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
#define _ORDERS_H
|
||||
|
||||
struct DivOrders {
|
||||
unsigned char ord[DIV_MAX_CHANS][256];
|
||||
unsigned char ord[DIV_MAX_CHANS][DIV_MAX_PATTERNS];
|
||||
|
||||
DivOrders() {
|
||||
memset(ord,0,DIV_MAX_CHANS*256);
|
||||
memset(ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
static DivPattern emptyPat;
|
||||
|
||||
DivPattern::DivPattern() {
|
||||
memset(data,-1,256*32*sizeof(short));
|
||||
for (int i=0; i<256; i++) {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=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>> ret;
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]!=NULL) {
|
||||
// compare
|
||||
for (int j=0; j<256; j++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (j==i) 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];
|
||||
data[j]=NULL;
|
||||
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>> ret;
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]==NULL) {
|
||||
for (int j=i; j<256; j++) {
|
||||
for (int j=i; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (data[j]!=NULL) {
|
||||
data[i]=data[j];
|
||||
data[j]=NULL;
|
||||
logV("%d -> %d",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() {
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]!=NULL) {
|
||||
delete data[i];
|
||||
data[i]=NULL;
|
||||
|
@ -95,5 +95,5 @@ void DivPattern::copyOn(DivPattern* dest) {
|
|||
|
||||
DivChannelData::DivChannelData():
|
||||
effectCols(1) {
|
||||
memset(data,0,256*sizeof(void*));
|
||||
memset(data,0,DIV_MAX_PATTERNS*sizeof(void*));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
struct DivPattern {
|
||||
String name;
|
||||
short data[256][32];
|
||||
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
|
||||
|
||||
/**
|
||||
* copy this pattern to another.
|
||||
|
@ -42,7 +42,7 @@ struct DivChannelData {
|
|||
// 3: volume
|
||||
// 4-5+: effect/effect value
|
||||
// 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.
|
||||
|
|
|
@ -125,6 +125,8 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
|||
if (parent->song.linearPitch==2 || !easyNoise) {
|
||||
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) {
|
||||
return MAX(0,easyStartingPeriod-(note-easyThreshold));
|
||||
}
|
||||
|
@ -132,19 +134,23 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
|||
}
|
||||
|
||||
int DivPlatformSMS::snCalcFreq(int ch) {
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold<<7)) {
|
||||
int ret=(((easyStartingPeriod<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold<<7)))>>7;
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
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;
|
||||
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) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.next();
|
||||
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;
|
||||
// old formula
|
||||
// ((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].inPorta) {
|
||||
// TODO: check whether this weird octave boundary thing applies to other systems as well
|
||||
// TODO: add compatibility flag. this is horrible.
|
||||
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
while (areYouSerious>0x60) areYouSerious-=12;
|
||||
|
@ -422,7 +427,7 @@ void DivPlatformSMS::reset() {
|
|||
YMPSG_Init(&sn_nuked,isRealSN,12,isRealSN?13:15,isRealSN?16383:32767);
|
||||
snNoiseMode=3;
|
||||
rWrite(0,0xe7);
|
||||
updateSNMode=false;
|
||||
updateSNMode=true;
|
||||
oldValue=0xff;
|
||||
lastPan=0xff;
|
||||
if (stereo) {
|
||||
|
@ -464,38 +469,24 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
|||
switch (flags.getInt("clockSel",0)) {
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=4000000;
|
||||
easyThreshold=86;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 3:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
easyThreshold=72;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 4:
|
||||
chipClock=3000000;
|
||||
easyThreshold=81;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 5:
|
||||
chipClock=2000000;
|
||||
easyThreshold=74;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 6:
|
||||
chipClock=COLOR_NTSC/8.0;
|
||||
easyThreshold=48;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
|
@ -569,6 +560,7 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
|||
stereo=false;
|
||||
break;
|
||||
}
|
||||
|
||||
rate=chipClock/divider;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
@ -65,8 +65,6 @@ class DivPlatformSMS: public DivDispatch {
|
|||
int divider=16;
|
||||
double toneDivider=64.0;
|
||||
double noiseDivider=64.0;
|
||||
int easyThreshold;
|
||||
int easyStartingPeriod;
|
||||
bool updateSNMode;
|
||||
bool resetPhase;
|
||||
bool isRealSN;
|
||||
|
|
|
@ -1592,7 +1592,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
if (disCont[i].lastAvail>0) {
|
||||
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);
|
||||
}
|
||||
if (disCont[i].runtotal>disCont[i].bbInLen) {
|
||||
logV("growing dispatch %d bbIn to %d",i,disCont[i].runtotal+256);
|
||||
delete[] disCont[i].bbIn[0];
|
||||
|
|
|
@ -57,9 +57,9 @@ void DivSample::putSampleData(SafeWriter* w) {
|
|||
w->writeI(loop?loopStart:-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;
|
||||
for (int j=0; j<32; j++) {
|
||||
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||
if (renderOn[i][j]) out|=1<<j;
|
||||
}
|
||||
w->writeI(out);
|
||||
|
@ -137,9 +137,9 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
|||
loopEnd=reader.readI();
|
||||
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();
|
||||
for (int j=0; j<32; j++) {
|
||||
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||
renderOn[i][j]=outMask&(1<<j);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define _SAMPLE_H
|
||||
|
||||
#include "../ta-utils.h"
|
||||
#include "defines.h"
|
||||
#include "safeWriter.h"
|
||||
#include "dataErrors.h"
|
||||
#include <deque>
|
||||
|
@ -116,7 +117,7 @@ struct DivSample {
|
|||
// - 2: Pingpong loop
|
||||
DivSampleLoopMode loopMode;
|
||||
|
||||
bool renderOn[4][32];
|
||||
bool renderOn[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
|
||||
// these are the new data structures.
|
||||
signed char* data8; // 8
|
||||
|
@ -332,11 +333,10 @@ struct DivSample {
|
|||
lengthBRR(0),
|
||||
lengthVOX(0),
|
||||
samples(0) {
|
||||
for (int i=0; i<32; i++) {
|
||||
renderOn[0][i]=true;
|
||||
renderOn[1][i]=true;
|
||||
renderOn[2][i]=true;
|
||||
renderOn[3][i]=true;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||
renderOn[j][i]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
~DivSample();
|
||||
|
|
|
@ -25,7 +25,7 @@ void DivSubSong::clearData() {
|
|||
pat[i].wipePatterns();
|
||||
}
|
||||
|
||||
memset(orders.ord,0,DIV_MAX_CHANS*256);
|
||||
memset(orders.ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||
ordersLen=1;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ void DivSubSong::optimizePatterns() {
|
|||
logD("optimizing channel %d...",i);
|
||||
std::vector<std::pair<int,int>> clearOuts=pat[i].optimize();
|
||||
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) {
|
||||
orders.ord[i][k]=j.second;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ void DivSubSong::rearrangePatterns() {
|
|||
logD("re-arranging channel %d...",i);
|
||||
std::vector<std::pair<int,int>> clearOuts=pat[i].rearrange();
|
||||
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) {
|
||||
orders.ord[i][k]=j.second;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#define DIV_MAX_CHANS 128
|
||||
|
||||
#include "defines.h"
|
||||
#include "../ta-utils.h"
|
||||
#include "config.h"
|
||||
#include "orders.h"
|
||||
|
@ -231,11 +230,11 @@ struct DivSong {
|
|||
bool isDMF;
|
||||
|
||||
// system
|
||||
DivSystem system[32];
|
||||
DivSystem system[DIV_MAX_CHIPS];
|
||||
unsigned char systemLen;
|
||||
signed char systemVol[32];
|
||||
signed char systemPan[32];
|
||||
DivConfig systemFlags[32];
|
||||
signed char systemVol[DIV_MAX_CHIPS];
|
||||
signed char systemPan[DIV_MAX_CHIPS];
|
||||
DivConfig systemFlags[DIV_MAX_CHIPS];
|
||||
|
||||
// song information
|
||||
String name, author, systemName;
|
||||
|
@ -429,7 +428,7 @@ struct DivSong {
|
|||
snNoLowPeriods(false),
|
||||
disableSampleMacro(false),
|
||||
autoSystem(true) {
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
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(version);
|
||||
|
||||
bool willExport[32];
|
||||
bool isSecond[32];
|
||||
int streamIDs[32];
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
bool isSecond[DIV_MAX_CHIPS];
|
||||
int streamIDs[DIV_MAX_CHIPS];
|
||||
double loopTimer[DIV_MAX_CHANS];
|
||||
double loopFreq[DIV_MAX_CHANS];
|
||||
int loopSample[DIV_MAX_CHANS];
|
||||
bool sampleDir[DIV_MAX_CHANS];
|
||||
std::vector<unsigned int> chipVol;
|
||||
std::vector<DivDelayedWrite> delayedWrites[32];
|
||||
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
|
||||
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
|
|
|
@ -58,11 +58,21 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
|||
min=reader.readI();
|
||||
max=reader.readI();
|
||||
|
||||
if (len>256 || min!=0 || max>255) {
|
||||
logV("invalid len/min/max: %d %d %d",len,min,max);
|
||||
if (len>256) {
|
||||
logE("invalid len: %d",len);
|
||||
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++) {
|
||||
data[i]=reader.readI();
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ const char* aboutLine[]={
|
|||
"freq-mod",
|
||||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"Jimmy-DS",
|
||||
"kleeder",
|
||||
"jaezu",
|
||||
"Laggy",
|
||||
|
@ -87,7 +88,7 @@ const char* aboutLine[]={
|
|||
"Mahbod Karamoozian",
|
||||
"Miker",
|
||||
"nicco1690",
|
||||
"NikonTeen",
|
||||
"<nk>",
|
||||
"potatoTeto",
|
||||
"psxdominator",
|
||||
"Raijin",
|
||||
|
|
|
@ -693,7 +693,7 @@ void FurnaceGUI::actualSampleList() {
|
|||
DivDispatch* dispatch=e->getDispatch(j);
|
||||
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->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
|
||||
memWarning=true;
|
||||
|
|
|
@ -522,7 +522,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_PAT_INCREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
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;
|
||||
case GUI_ACTION_PAT_DECREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
|
|
|
@ -19,7 +19,22 @@
|
|||
|
||||
#include "gui.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() {
|
||||
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::SetNextWindowSize(portrait?ImVec2(canvasW,0.16*canvasW):ImVec2(0.16*canvasH,canvasH));
|
||||
if (ImGui::Begin("Mobile Controls",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|globalWinFlags)) {
|
||||
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
||||
ImVec2 buttonSize=ImVec2(avail,avail);
|
||||
|
||||
const char* mobButtonName=ICON_FA_CHEVRON_RIGHT "##MobileMenu";
|
||||
if (portrait) mobButtonName=ICON_FA_CHEVRON_UP "##MobileMenu";
|
||||
if (mobileMenuOpen) {
|
||||
|
|
|
@ -119,7 +119,7 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
|||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
||||
for (int j=0; j<e->curSubSong->patLen; j++) {
|
||||
for (int k=0; k<32; k++) {
|
||||
for (int k=0; k<DIV_MAX_COLS; 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]));
|
||||
}
|
||||
|
|
|
@ -220,20 +220,20 @@ void FurnaceGUI::doReplace() {
|
|||
UndoStep us;
|
||||
us.type=GUI_UNDO_REPLACE;
|
||||
|
||||
short prevVal[32];
|
||||
memset(prevVal,0,32*sizeof(short));
|
||||
short prevVal[DIV_MAX_COLS];
|
||||
memset(prevVal,0,DIV_MAX_COLS*sizeof(short));
|
||||
|
||||
for (FurnaceGUIQueryResult& i: curQueryResults) {
|
||||
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);
|
||||
if (touched[i.x]==NULL) {
|
||||
touched[i.x]=new bool[256*256];
|
||||
memset(touched[i.x],0,256*256*sizeof(bool));
|
||||
touched[i.x]=new bool[DIV_MAX_PATTERNS*DIV_MAX_ROWS];
|
||||
memset(touched[i.x],0,DIV_MAX_PATTERNS*DIV_MAX_ROWS*sizeof(bool));
|
||||
}
|
||||
if (touched[i.x][(patIndex<<8)|i.y]) continue;
|
||||
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) {
|
||||
switch (queryReplaceNoteMode) {
|
||||
|
@ -462,7 +462,7 @@ void FurnaceGUI::doReplace() {
|
|||
}
|
||||
|
||||
// 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]) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (dragMobileEditButton) {
|
||||
dragMobileEditButton=false;
|
||||
}
|
||||
if (selecting) {
|
||||
if (!selectingFull) cursor=selEnd;
|
||||
finishSelection();
|
||||
|
@ -4763,7 +4766,7 @@ bool FurnaceGUI::loop() {
|
|||
if (ImGui::Button("Orders")) {
|
||||
stop();
|
||||
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->setOrder(0);
|
||||
|
@ -4779,8 +4782,8 @@ bool FurnaceGUI::loop() {
|
|||
e->lockEngine([this]() {
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
|
||||
memset(pat->data,-1,256*32*sizeof(short));
|
||||
for (int j=0; j<256; j++) {
|
||||
memset(pat->data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int j=0; j<DIV_MAX_ROWS; j++) {
|
||||
pat->data[j][0]=0;
|
||||
pat->data[j][1]=0;
|
||||
}
|
||||
|
@ -5656,6 +5659,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pendingInsSingle(false),
|
||||
displayPendingRawSample(false),
|
||||
snesFilterHex(false),
|
||||
mobileEdit(false),
|
||||
vgmExportVersion(0x171),
|
||||
drawHalt(10),
|
||||
zsmExportTickRate(60),
|
||||
|
@ -5664,6 +5668,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
displayInsTypeListMakeInsSample(-1),
|
||||
mobileMenuPos(0.0f),
|
||||
autoButtonSize(0.0f),
|
||||
mobileEditAnim(0.0f),
|
||||
mobileEditButtonPos(0.7f,0.7f),
|
||||
mobileEditButtonSize(60.0f,60.0f),
|
||||
curSysSection(NULL),
|
||||
pendingRawSampleDepth(8),
|
||||
pendingRawSampleChannels(1),
|
||||
|
@ -5794,12 +5801,14 @@ FurnaceGUI::FurnaceGUI():
|
|||
orderScrollLocked(false),
|
||||
orderScrollTolerance(false),
|
||||
dragMobileMenu(false),
|
||||
dragMobileEditButton(false),
|
||||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
curWindowLast(GUI_WINDOW_NOTHING),
|
||||
curWindowThreadSafe(GUI_WINDOW_NOTHING),
|
||||
lastPatternWidth(0.0f),
|
||||
longThreshold(0.48f),
|
||||
buttonLongThreshold(0.20f),
|
||||
latchNote(-1),
|
||||
latchIns(-2),
|
||||
latchVol(-1),
|
||||
|
@ -5923,6 +5932,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
sampleSelStart(-1),
|
||||
sampleSelEnd(-1),
|
||||
sampleInfo(true),
|
||||
sampleCompatRate(false),
|
||||
sampleDragActive(false),
|
||||
sampleDragMode(false),
|
||||
sampleDrag16(false),
|
||||
|
@ -6022,7 +6032,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
valueKeys[SDLK_KP_8]=8;
|
||||
valueKeys[SDLK_KP_9]=9;
|
||||
|
||||
memset(willExport,1,32*sizeof(bool));
|
||||
memset(willExport,1,DIV_MAX_CHIPS*sizeof(bool));
|
||||
|
||||
peak[0]=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_BUTTON_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=buttonLongThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<buttonLongThreshold)
|
||||
// for now
|
||||
#define NOTIFY_LONG_HOLD \
|
||||
if (vibrator && vibratorAvailable) { \
|
||||
|
@ -1088,14 +1088,16 @@ class FurnaceGUI {
|
|||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
|
||||
bool willExport[32];
|
||||
bool mobileEdit;
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
int vgmExportVersion;
|
||||
int drawHalt;
|
||||
int zsmExportTickRate;
|
||||
int macroPointSize;
|
||||
int waveEditStyle;
|
||||
int displayInsTypeListMakeInsSample;
|
||||
float mobileMenuPos, autoButtonSize;
|
||||
float mobileMenuPos, autoButtonSize, mobileEditAnim;
|
||||
ImVec2 mobileEditButtonPos, mobileEditButtonSize;
|
||||
const int* curSysSection;
|
||||
DivInstrumentFM opllPreview;
|
||||
|
||||
|
@ -1435,13 +1437,14 @@ class FurnaceGUI {
|
|||
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
||||
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 keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu;
|
||||
bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton;
|
||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||
float peak[2];
|
||||
float patChanX[DIV_MAX_CHANS+1];
|
||||
float patChanSlideY[DIV_MAX_CHANS+1];
|
||||
float lastPatternWidth, longThreshold;
|
||||
float buttonLongThreshold;
|
||||
String nextDesc;
|
||||
String nextDescName;
|
||||
|
||||
|
@ -1616,7 +1619,7 @@ class FurnaceGUI {
|
|||
int resampleStrat;
|
||||
float amplifyVol;
|
||||
int sampleSelStart, sampleSelEnd;
|
||||
bool sampleInfo;
|
||||
bool sampleInfo, sampleCompatRate;
|
||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||
void* sampleDragTarget;
|
||||
ImVec2 sampleDragStart;
|
||||
|
|
|
@ -177,10 +177,10 @@ void FurnaceGUI::drawOrders() {
|
|||
e->lockSave([this,i,j]() {
|
||||
if (changeAllOrders) {
|
||||
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 {
|
||||
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);
|
||||
|
|
|
@ -30,8 +30,8 @@ void FurnaceGUI::drawPatManager() {
|
|||
}
|
||||
if (!patManagerOpen) return;
|
||||
char id[1024];
|
||||
unsigned char isUsed[256];
|
||||
bool isNull[256];
|
||||
unsigned char isUsed[DIV_MAX_PATTERNS];
|
||||
bool isNull[DIV_MAX_PATTERNS];
|
||||
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
|
||||
ImGui::Text("Global Tasks");
|
||||
|
||||
|
@ -52,19 +52,19 @@ void FurnaceGUI::drawPatManager() {
|
|||
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
memset(isUsed,0,256);
|
||||
memset(isNull,0,256*sizeof(bool));
|
||||
memset(isUsed,0,DIV_MAX_PATTERNS);
|
||||
memset(isNull,0,DIV_MAX_PATTERNS*sizeof(bool));
|
||||
for (int j=0; j<e->curSubSong->ordersLen; 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);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",e->getChannelShortName(i));
|
||||
|
||||
ImGui::PushID(1000+i);
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
snprintf(id,1023,"%.2X",k);
|
||||
|
|
|
@ -875,11 +875,11 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
|
||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=DIV_MAX_EFFECTS);
|
||||
snprintf(chanID,2048,">##_RCH%d",i);
|
||||
if (ImGui::SmallButton(chanID)) {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -134,7 +134,32 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
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::TableNextColumn();
|
||||
if (ImGui::Button(sampleInfo?(ICON_FA_CHEVRON_UP "##SECollapse"):(ICON_FA_CHEVRON_DOWN "##SECollapse"))) {
|
||||
|
@ -143,7 +168,20 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Text("Info");
|
||||
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();
|
||||
bool doLoop=(sample->isLoopable());
|
||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||
|
@ -164,8 +202,11 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
|
||||
if (selColumns>1) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Chips");
|
||||
}
|
||||
|
||||
if (sampleInfo) {
|
||||
ImGui::TableNextRow();
|
||||
|
@ -216,21 +257,110 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("C-4 (Hz)");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED
|
||||
if (sample->centerRate<100) sample->centerRate=100;
|
||||
if (sample->centerRate>65535) sample->centerRate=65535;
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
int sampleNote=round(64.0+(128.0*12.0*log((double)targetRate/8363.0)/log(2.0)));
|
||||
int sampleNoteCoarse=60+(sampleNote>>7);
|
||||
int sampleNoteFine=(sampleNote&127)-64;
|
||||
|
||||
if (sampleNoteCoarse<0) {
|
||||
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::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
||||
if (sample->rate<100) sample->rate=100;
|
||||
if (sample->rate>96000) sample->rate=96000;
|
||||
if (ImGui::InputInt("##SampleRate",&targetRate,10,200)) { MARK_MODIFIED
|
||||
if (targetRate<100) targetRate=100;
|
||||
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();
|
||||
|
@ -298,35 +428,8 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (selColumns>1) {
|
||||
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)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -336,7 +439,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::Text("%d",i+1);
|
||||
}
|
||||
char id[1024];
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||
if (!isTypeVisible[i]) continue;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
@ -873,7 +976,26 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
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.
|
||||
/*if (ImGui::GetContentRegionAvail().y>(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 statusBar2="";
|
||||
String statusBar3=fmt::sprintf("%d samples, %d bytes",sample->samples,sample->getCurBufLen());
|
||||
bool drawSelection=false;
|
||||
|
||||
if (!sampleDragMode) {
|
||||
|
@ -1181,12 +1305,15 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
|
||||
if (posX>=0) {
|
||||
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
|
||||
statusBar2=fmt::sprintf("(%d, %d)",posX,posY);
|
||||
}
|
||||
}
|
||||
|
||||
if (e->isPreviewingSample()) {
|
||||
statusBar+=fmt::sprintf(" | %.2fHz",e->getSamplePreviewRate());
|
||||
if (!statusBar2.empty()) {
|
||||
statusBar2+=" | ";
|
||||
}
|
||||
statusBar2+=fmt::sprintf("%.2fHz",e->getSamplePreviewRate());
|
||||
|
||||
int start=sampleSelStart;
|
||||
int end=sampleSelEnd;
|
||||
|
@ -1269,7 +1396,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
|
||||
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;
|
||||
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
|
||||
if (patLen<1) patLen=1;
|
||||
if (patLen>256) patLen=256;
|
||||
if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS;
|
||||
e->curSubSong->patLen=patLen;
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
|
|||
int ordLen=e->curSubSong->ordersLen;
|
||||
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
|
||||
if (ordLen<1) ordLen=1;
|
||||
if (ordLen>256) ordLen=256;
|
||||
if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS;
|
||||
e->curSubSong->ordersLen=ordLen;
|
||||
if (curOrder>=ordLen) {
|
||||
setOrder(ordLen-1);
|
||||
|
|
|
@ -112,7 +112,7 @@ void FurnaceGUI::drawSysManager() {
|
|||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (e->song.systemLen<32) {
|
||||
if (e->song.systemLen<DIV_MAX_CHIPS) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
|
@ -643,11 +643,11 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
ImGui::TableNextColumn();
|
||||
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++) {
|
||||
wave->data[i]=(wave->data[i]*(waveGenScaleY+1))/(wave->max+1);
|
||||
}
|
||||
wave->max=waveGenScaleY;
|
||||
wave->max=waveGenScaleY-1;
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue