Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

This commit is contained in:
cam900 2022-12-10 14:29:13 +09:00
commit 37e83c0905
45 changed files with 497 additions and 198 deletions

View file

@ -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

View file

@ -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.

BIN
demos/TimeTrial.fur Normal file

Binary file not shown.

BIN
demos/atari breakbeat.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/duty fun.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/genesis thing.fur Normal file

Binary file not shown.

BIN
demos/overdrive.fur Normal file

Binary file not shown.

BIN
demos/snowdin.fur Executable file

Binary file not shown.

BIN
demos/very chill snes.fur Normal file

Binary file not shown.

36
src/engine/defines.h Normal file
View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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);
} }

View file

@ -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);
} }
}; };

View file

@ -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*));
} }

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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);
} }
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-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) { 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];

View file

@ -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);
} }
} }

View file

@ -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();

View file

@ -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;
} }

View file

@ -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;

View file

@ -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++) {

View file

@ -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();
} }

View file

@ -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",

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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]));
} }

View file

@ -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]));
} }

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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();
} }

View file

@ -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!");
} }
ImGui::TableNextColumn();
ImGui::Text("Chips"); if (selColumns>1) {
ImGui::TableNextColumn();
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();
ImGui::TableNextColumn(); 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)) { 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();
} }
} }
} }

View file

@ -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);

View file

@ -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();

View file

@ -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;
}); });
} }