dev139 - groove patterns!
This commit is contained in:
parent
ace2142286
commit
ac5986e44f
|
@ -596,6 +596,7 @@ src/gui/editControls.cpp
|
||||||
src/gui/effectList.cpp
|
src/gui/effectList.cpp
|
||||||
src/gui/findReplace.cpp
|
src/gui/findReplace.cpp
|
||||||
src/gui/gradient.cpp
|
src/gui/gradient.cpp
|
||||||
|
src/gui/grooves.cpp
|
||||||
src/gui/insEdit.cpp
|
src/gui/insEdit.cpp
|
||||||
src/gui/log.cpp
|
src/gui/log.cpp
|
||||||
src/gui/mixer.cpp
|
src/gui/mixer.cpp
|
||||||
|
|
|
@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 139: Furnace dev139
|
||||||
- 138: Furnace dev138
|
- 138: Furnace dev138
|
||||||
- 137: Furnace dev137
|
- 137: Furnace dev137
|
||||||
- 136: Furnace dev136
|
- 136: Furnace dev136
|
||||||
|
@ -403,6 +404,14 @@ size | description
|
||||||
--- | **a couple more compat flags** (>=138)
|
--- | **a couple more compat flags** (>=138)
|
||||||
1 | broken portamento during legato
|
1 | broken portamento during legato
|
||||||
7 | reserved
|
7 | reserved
|
||||||
|
--- | **speed pattern of first song** (>=139)
|
||||||
|
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
||||||
|
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||||
|
--- | **groove list** (>=139)
|
||||||
|
1 | number of entries
|
||||||
|
??? | groove entries. the format is:
|
||||||
|
| - 1 byte: length of groove
|
||||||
|
| - 16 bytes: groove pattern
|
||||||
```
|
```
|
||||||
|
|
||||||
# patchbay
|
# patchbay
|
||||||
|
@ -472,6 +481,9 @@ size | description
|
||||||
| - a list of channelCount C strings
|
| - a list of channelCount C strings
|
||||||
S?? | channel short names
|
S?? | channel short names
|
||||||
| - same as above
|
| - same as above
|
||||||
|
--- | **speed pattern** (>=139)
|
||||||
|
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
||||||
|
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||||
```
|
```
|
||||||
|
|
||||||
# chip flags
|
# chip flags
|
||||||
|
|
|
@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
||||||
case 0x08:
|
case 0x08:
|
||||||
return "08xy: Set panning (x: left; y: right)";
|
return "08xy: Set panning (x: left; y: right)";
|
||||||
case 0x09:
|
case 0x09:
|
||||||
return "09xx: Set speed 1";
|
return "09xx: Set groove pattern (speed 1 if no grooves exist)";
|
||||||
case 0x0a:
|
case 0x0a:
|
||||||
return "0Axy: Volume slide (0y: down; x0: up)";
|
return "0Axy: Volume slide (0y: down; x0: up)";
|
||||||
case 0x0b:
|
case 0x0b:
|
||||||
|
@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
||||||
case 0x0d:
|
case 0x0d:
|
||||||
return "0Dxx: Jump to next pattern";
|
return "0Dxx: Jump to next pattern";
|
||||||
case 0x0f:
|
case 0x0f:
|
||||||
return "0Fxx: Set speed 2";
|
return "0Fxx: Set speed (speed 2 if no grooves exist)";
|
||||||
case 0x80:
|
case 0x80:
|
||||||
return "80xx: Set panning (00: left; 80: center; FF: right)";
|
return "80xx: Set panning (00: left; 80: center; FF: right)";
|
||||||
case 0x81:
|
case 0x81:
|
||||||
|
@ -1959,14 +1959,12 @@ String DivEngine::getPlaybackDebugInfo() {
|
||||||
"cmdsPerSecond: %d\n"
|
"cmdsPerSecond: %d\n"
|
||||||
"globalPitch: %d\n"
|
"globalPitch: %d\n"
|
||||||
"extValue: %d\n"
|
"extValue: %d\n"
|
||||||
"speed1: %d\n"
|
|
||||||
"speed2: %d\n"
|
|
||||||
"tempoAccum: %d\n"
|
"tempoAccum: %d\n"
|
||||||
"totalProcessed: %d\n"
|
"totalProcessed: %d\n"
|
||||||
"bufferPos: %d\n",
|
"bufferPos: %d\n",
|
||||||
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
||||||
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||||
(int)extValue,(int)speed1,(int)speed2,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2091,7 +2089,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
||||||
lastLoopPos=-1;
|
lastLoopPos=-1;
|
||||||
}
|
}
|
||||||
endOfSong=false;
|
endOfSong=false;
|
||||||
speedAB=false;
|
// whaaaaa?
|
||||||
|
curSpeed=0;
|
||||||
playing=true;
|
playing=true;
|
||||||
skipping=true;
|
skipping=true;
|
||||||
memset(walked,0,8192);
|
memset(walked,0,8192);
|
||||||
|
@ -2439,15 +2438,14 @@ void DivEngine::reset() {
|
||||||
}
|
}
|
||||||
extValue=0;
|
extValue=0;
|
||||||
extValuePresent=0;
|
extValuePresent=0;
|
||||||
speed1=curSubSong->speed1;
|
speeds=curSubSong->speeds;
|
||||||
speed2=curSubSong->speed2;
|
|
||||||
firstTick=false;
|
firstTick=false;
|
||||||
shallStop=false;
|
shallStop=false;
|
||||||
shallStopSched=false;
|
shallStopSched=false;
|
||||||
pendingMetroTick=0;
|
pendingMetroTick=0;
|
||||||
elapsedBars=0;
|
elapsedBars=0;
|
||||||
elapsedBeats=0;
|
elapsedBeats=0;
|
||||||
nextSpeed=speed1;
|
nextSpeed=speeds.val[0];
|
||||||
divider=60;
|
divider=60;
|
||||||
if (curSubSong->customTempo) {
|
if (curSubSong->customTempo) {
|
||||||
divider=curSubSong->hz;
|
divider=curSubSong->hz;
|
||||||
|
@ -2647,12 +2645,8 @@ size_t DivEngine::getCurrentSubSong() {
|
||||||
return curSubSongIndex;
|
return curSubSongIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char DivEngine::getSpeed1() {
|
const DivGroovePattern& DivEngine::getSpeeds() {
|
||||||
return speed1;
|
return speeds;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char DivEngine::getSpeed2() {
|
|
||||||
return speed2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float DivEngine::getHz() {
|
float DivEngine::getHz() {
|
||||||
|
@ -4234,7 +4228,7 @@ void DivEngine::quitDispatch() {
|
||||||
clockDrift=0;
|
clockDrift=0;
|
||||||
chans=0;
|
chans=0;
|
||||||
playing=false;
|
playing=false;
|
||||||
speedAB=false;
|
curSpeed=0;
|
||||||
endOfSong=false;
|
endOfSong=false;
|
||||||
ticks=0;
|
ticks=0;
|
||||||
tempoAccum=0;
|
tempoAccum=0;
|
||||||
|
|
|
@ -47,8 +47,8 @@
|
||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev138"
|
#define DIV_VERSION "dev139"
|
||||||
#define DIV_ENGINE_VERSION 138
|
#define DIV_ENGINE_VERSION 139
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
#define DIV_VERSION_FC 0xff02
|
#define DIV_VERSION_FC 0xff02
|
||||||
|
@ -337,7 +337,6 @@ class DivEngine {
|
||||||
bool playing;
|
bool playing;
|
||||||
bool freelance;
|
bool freelance;
|
||||||
bool shallStop, shallStopSched;
|
bool shallStop, shallStopSched;
|
||||||
bool speedAB;
|
|
||||||
bool endOfSong;
|
bool endOfSong;
|
||||||
bool consoleMode;
|
bool consoleMode;
|
||||||
bool extValuePresent;
|
bool extValuePresent;
|
||||||
|
@ -359,7 +358,7 @@ class DivEngine {
|
||||||
bool midiOutClock;
|
bool midiOutClock;
|
||||||
int midiOutMode;
|
int midiOutMode;
|
||||||
int softLockCount;
|
int softLockCount;
|
||||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
|
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
|
||||||
size_t curSubSongIndex;
|
size_t curSubSongIndex;
|
||||||
size_t bufferPos;
|
size_t bufferPos;
|
||||||
double divider;
|
double divider;
|
||||||
|
@ -368,7 +367,7 @@ class DivEngine {
|
||||||
int stepPlay;
|
int stepPlay;
|
||||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||||
unsigned char extValue, pendingMetroTick;
|
unsigned char extValue, pendingMetroTick;
|
||||||
unsigned char speed1, speed2;
|
DivGroovePattern speeds;
|
||||||
short tempoAccum;
|
short tempoAccum;
|
||||||
DivStatusView view;
|
DivStatusView view;
|
||||||
DivHaltPositions haltOn;
|
DivHaltPositions haltOn;
|
||||||
|
@ -730,11 +729,8 @@ class DivEngine {
|
||||||
// get current subsong
|
// get current subsong
|
||||||
size_t getCurrentSubSong();
|
size_t getCurrentSubSong();
|
||||||
|
|
||||||
// get speed 1
|
// get speeds
|
||||||
unsigned char getSpeed1();
|
const DivGroovePattern& getSpeeds();
|
||||||
|
|
||||||
// get speed 2
|
|
||||||
unsigned char getSpeed2();
|
|
||||||
|
|
||||||
// get Hz
|
// get Hz
|
||||||
float getHz();
|
float getHz();
|
||||||
|
@ -1065,7 +1061,6 @@ class DivEngine {
|
||||||
freelance(false),
|
freelance(false),
|
||||||
shallStop(false),
|
shallStop(false),
|
||||||
shallStopSched(false),
|
shallStopSched(false),
|
||||||
speedAB(false),
|
|
||||||
endOfSong(false),
|
endOfSong(false),
|
||||||
consoleMode(false),
|
consoleMode(false),
|
||||||
extValuePresent(false),
|
extValuePresent(false),
|
||||||
|
@ -1099,6 +1094,7 @@ class DivEngine {
|
||||||
nextSpeed(3),
|
nextSpeed(3),
|
||||||
elapsedBars(0),
|
elapsedBars(0),
|
||||||
elapsedBeats(0),
|
elapsedBeats(0),
|
||||||
|
curSpeed(0),
|
||||||
curSubSongIndex(0),
|
curSubSongIndex(0),
|
||||||
bufferPos(0),
|
bufferPos(0),
|
||||||
divider(60),
|
divider(60),
|
||||||
|
@ -1116,8 +1112,6 @@ class DivEngine {
|
||||||
globalPitch(0),
|
globalPitch(0),
|
||||||
extValue(0),
|
extValue(0),
|
||||||
pendingMetroTick(0),
|
pendingMetroTick(0),
|
||||||
speed1(3),
|
|
||||||
speed2(3),
|
|
||||||
tempoAccum(0),
|
tempoAccum(0),
|
||||||
view(DIV_STATUS_NOTHING),
|
view(DIV_STATUS_NOTHING),
|
||||||
haltOn(DIV_HALT_NONE),
|
haltOn(DIV_HALT_NONE),
|
||||||
|
|
|
@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
ds.version=(unsigned char)reader.readC();
|
ds.version=(unsigned char)reader.readC();
|
||||||
logI("module version %d (0x%.2x)",ds.version,ds.version);
|
logI("module version %d (0x%.2x)",ds.version,ds.version);
|
||||||
if (ds.version>0x1a) {
|
if (ds.version>0x1b) {
|
||||||
logE("this version is not supported by Furnace yet!");
|
logE("this version is not supported by Furnace yet!");
|
||||||
lastError="this version is not supported by Furnace yet";
|
lastError="this version is not supported by Furnace yet";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.subsong[0]->timeBase=reader.readC();
|
ds.subsong[0]->timeBase=reader.readC();
|
||||||
ds.subsong[0]->speed1=reader.readC();
|
ds.subsong[0]->speeds.len=2;
|
||||||
|
ds.subsong[0]->speeds.val[0]=reader.readC();
|
||||||
if (ds.version>0x07) {
|
if (ds.version>0x07) {
|
||||||
ds.subsong[0]->speed2=reader.readC();
|
ds.subsong[0]->speeds.val[1]=reader.readC();
|
||||||
ds.subsong[0]->pal=reader.readC();
|
ds.subsong[0]->pal=reader.readC();
|
||||||
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||||
ds.subsong[0]->customTempo=reader.readC();
|
ds.subsong[0]->customTempo=reader.readC();
|
||||||
} else {
|
} else {
|
||||||
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
ds.subsong[0]->speeds.len=1;
|
||||||
}
|
}
|
||||||
if (ds.version>0x0a) {
|
if (ds.version>0x0a) {
|
||||||
String hz=reader.readString(3);
|
String hz=reader.readString(3);
|
||||||
|
@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
for (int i=0; i<ds.sampleLen; i++) {
|
for (int i=0; i<ds.sampleLen; i++) {
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
int length=reader.readI();
|
int length=reader.readI();
|
||||||
|
int cutStart=0;
|
||||||
|
int cutEnd=length;
|
||||||
int pitch=5;
|
int pitch=5;
|
||||||
int vol=50;
|
int vol=50;
|
||||||
short* data;
|
short* data;
|
||||||
|
@ -866,6 +869,29 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
|
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ds.version>=0x1a) {
|
||||||
|
// what the hell man...
|
||||||
|
cutStart=reader.readI();
|
||||||
|
cutEnd=reader.readI();
|
||||||
|
if (cutStart<0 || cutStart>length) {
|
||||||
|
logE("cutStart is out of range! (%d)",cutStart);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<0 || cutEnd>length) {
|
||||||
|
logE("cutEnd is out of range! (%d)",cutEnd);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cutEnd<cutStart) {
|
||||||
|
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
||||||
|
lastError="file is corrupt or unreadable at samples";
|
||||||
|
delete[] file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (length>0) {
|
if (length>0) {
|
||||||
if (ds.version>0x08) {
|
if (ds.version>0x08) {
|
||||||
if (ds.version<0x0b) {
|
if (ds.version<0x0b) {
|
||||||
|
@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
data=new short[length];
|
data=new short[length];
|
||||||
reader.read(data,length*2);
|
reader.read(data,length*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ds.version>0x1a) {
|
||||||
|
if (cutStart!=0 || cutEnd!=length) {
|
||||||
|
// cut data
|
||||||
|
short* newData=new short[cutEnd-cutStart];
|
||||||
|
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
||||||
|
delete[] data;
|
||||||
|
data=newData;
|
||||||
|
length=cutEnd-cutStart;
|
||||||
|
cutStart=0;
|
||||||
|
cutEnd=length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TA_BIG_ENDIAN
|
#ifdef TA_BIG_ENDIAN
|
||||||
// convert to big-endian
|
// convert to big-endian
|
||||||
|
@ -1742,8 +1781,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
reader.readI();
|
reader.readI();
|
||||||
|
|
||||||
subSong->timeBase=reader.readC();
|
subSong->timeBase=reader.readC();
|
||||||
subSong->speed1=reader.readC();
|
subSong->speeds.len=2;
|
||||||
subSong->speed2=reader.readC();
|
subSong->speeds.val[0]=reader.readC();
|
||||||
|
subSong->speeds.val[1]=reader.readC();
|
||||||
subSong->arpLen=reader.readC();
|
subSong->arpLen=reader.readC();
|
||||||
subSong->hz=reader.readF();
|
subSong->hz=reader.readF();
|
||||||
subSong->pal=(subSong->hz>=53);
|
subSong->pal=(subSong->hz>=53);
|
||||||
|
@ -2231,6 +2271,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ds.version>=139) {
|
||||||
|
subSong->speeds.len=reader.readC();
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
subSong->speeds.val[i]=reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
// grooves
|
||||||
|
unsigned char grooveCount=reader.readC();
|
||||||
|
for (int i=0; i<grooveCount; i++) {
|
||||||
|
DivGroovePattern gp;
|
||||||
|
gp.len=reader.readC();
|
||||||
|
for (int j=0; j<16; j++) {
|
||||||
|
gp.val[j]=reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
ds.grooves.push_back(gp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// read system flags
|
// read system flags
|
||||||
if (ds.version>=119) {
|
if (ds.version>=119) {
|
||||||
logD("reading chip flags...");
|
logD("reading chip flags...");
|
||||||
|
@ -2289,8 +2348,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
subSong=ds.subsong[i+1];
|
subSong=ds.subsong[i+1];
|
||||||
subSong->timeBase=reader.readC();
|
subSong->timeBase=reader.readC();
|
||||||
subSong->speed1=reader.readC();
|
subSong->speeds.len=2;
|
||||||
subSong->speed2=reader.readC();
|
subSong->speeds.val[0]=reader.readC();
|
||||||
|
subSong->speeds.val[1]=reader.readC();
|
||||||
subSong->arpLen=reader.readC();
|
subSong->arpLen=reader.readC();
|
||||||
subSong->hz=reader.readF();
|
subSong->hz=reader.readF();
|
||||||
subSong->pal=(subSong->hz>=53);
|
subSong->pal=(subSong->hz>=53);
|
||||||
|
@ -2338,6 +2398,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
for (int i=0; i<tchans; i++) {
|
for (int i=0; i<tchans; i++) {
|
||||||
subSong->chanShortName[i]=reader.readString();
|
subSong->chanShortName[i]=reader.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ds.version>=139) {
|
||||||
|
subSong->speeds.len=reader.readC();
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
subSong->speeds.val[i]=reader.readC();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2956,7 +3023,6 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
if (fxVal>0x20 && ds.name!="klisje paa klisje") {
|
if (fxVal>0x20 && ds.name!="klisje paa klisje") {
|
||||||
writeFxCol(0xf0,fxVal);
|
writeFxCol(0xf0,fxVal);
|
||||||
} else {
|
} else {
|
||||||
writeFxCol(0x09,fxVal);
|
|
||||||
writeFxCol(0x0f,fxVal);
|
writeFxCol(0x0f,fxVal);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -3435,8 +3501,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
ds.subsong[0]->pal=true;
|
ds.subsong[0]->pal=true;
|
||||||
ds.subsong[0]->customTempo=true;
|
ds.subsong[0]->customTempo=true;
|
||||||
ds.subsong[0]->pat[3].effectCols=3;
|
ds.subsong[0]->pat[3].effectCols=3;
|
||||||
ds.subsong[0]->speed1=3;
|
ds.subsong[0]->speeds.val[0]=3;
|
||||||
ds.subsong[0]->speed2=3;
|
ds.subsong[0]->speeds.len=1;
|
||||||
|
|
||||||
int lastIns[4];
|
int lastIns[4];
|
||||||
int lastNote[4];
|
int lastNote[4];
|
||||||
|
@ -3453,10 +3519,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
||||||
ds.subsong[0]->orders.ord[j][i]=i;
|
ds.subsong[0]->orders.ord[j][i]=i;
|
||||||
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
|
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
|
||||||
if (j==3 && seq[i].speed) {
|
if (j==3 && seq[i].speed) {
|
||||||
p->data[0][6]=0x09;
|
p->data[0][6]=0x0f;
|
||||||
p->data[0][7]=seq[i].speed;
|
p->data[0][7]=seq[i].speed;
|
||||||
p->data[0][8]=0x0f;
|
|
||||||
p->data[0][9]=seq[i].speed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ignoreNext=false;
|
bool ignoreNext=false;
|
||||||
|
@ -4343,8 +4407,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
||||||
w->writeC(subSong->timeBase);
|
w->writeC(subSong->timeBase);
|
||||||
w->writeC(subSong->speed1);
|
// these are for compatibility
|
||||||
w->writeC(subSong->speed2);
|
w->writeC(subSong->speeds.val[0]);
|
||||||
|
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||||
w->writeC(subSong->arpLen);
|
w->writeC(subSong->arpLen);
|
||||||
w->writeF(subSong->hz);
|
w->writeF(subSong->hz);
|
||||||
w->writeS(subSong->patLen);
|
w->writeS(subSong->patLen);
|
||||||
|
@ -4531,6 +4596,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// speeds of first song
|
||||||
|
w->writeC(subSong->speeds.len);
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
w->writeC(subSong->speeds.val[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// groove list
|
||||||
|
w->writeC((unsigned char)song.grooves.size());
|
||||||
|
for (const DivGroovePattern& i: song.grooves) {
|
||||||
|
w->writeC(i.len);
|
||||||
|
for (int j=0; j<16; j++) {
|
||||||
|
w->writeC(i.val[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
blockEndSeek=w->tell();
|
blockEndSeek=w->tell();
|
||||||
w->seek(blockStartSeek,SEEK_SET);
|
w->seek(blockStartSeek,SEEK_SET);
|
||||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||||
|
@ -4545,8 +4625,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
||||||
w->writeC(subSong->timeBase);
|
w->writeC(subSong->timeBase);
|
||||||
w->writeC(subSong->speed1);
|
w->writeC(subSong->speeds.val[0]);
|
||||||
w->writeC(subSong->speed2);
|
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||||
w->writeC(subSong->arpLen);
|
w->writeC(subSong->arpLen);
|
||||||
w->writeF(subSong->hz);
|
w->writeF(subSong->hz);
|
||||||
w->writeS(subSong->patLen);
|
w->writeS(subSong->patLen);
|
||||||
|
@ -4585,6 +4665,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeString(subSong->chanShortName[i],false);
|
w->writeString(subSong->chanShortName[i],false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// speeds
|
||||||
|
w->writeC(subSong->speeds.len);
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
w->writeC(subSong->speeds.val[i]);
|
||||||
|
}
|
||||||
|
|
||||||
blockEndSeek=w->tell();
|
blockEndSeek=w->tell();
|
||||||
w->seek(blockStartSeek,SEEK_SET);
|
w->seek(blockStartSeek,SEEK_SET);
|
||||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||||
|
@ -4840,8 +4926,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
w->writeC(curSubSong->hilightB);
|
w->writeC(curSubSong->hilightB);
|
||||||
|
|
||||||
w->writeC(curSubSong->timeBase);
|
w->writeC(curSubSong->timeBase);
|
||||||
w->writeC(curSubSong->speed1);
|
w->writeC(curSubSong->speeds.val[0]);
|
||||||
w->writeC(curSubSong->speed2);
|
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
|
||||||
w->writeC(curSubSong->pal);
|
w->writeC(curSubSong->pal);
|
||||||
w->writeC(curSubSong->customTempo);
|
w->writeC(curSubSong->customTempo);
|
||||||
char customHz[4];
|
char customHz[4];
|
||||||
|
@ -4865,6 +4951,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
addWarning("only the currently selected subsong will be saved");
|
addWarning("only the currently selected subsong will be saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!song.grooves.empty()) {
|
||||||
|
addWarning("grooves will not be saved");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curSubSong->speeds.len>2) {
|
||||||
|
addWarning("only the first two speeds will be effective");
|
||||||
|
}
|
||||||
|
|
||||||
if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
|
if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
|
||||||
addWarning(".dmf format does not support virtual tempo");
|
addWarning(".dmf format does not support virtual tempo");
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,11 +400,22 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
if (effectVal==-1) effectVal=0;
|
if (effectVal==-1) effectVal=0;
|
||||||
|
|
||||||
switch (effect) {
|
switch (effect) {
|
||||||
case 0x09: // speed 1
|
case 0x09: // select groove pattern/speed 1
|
||||||
if (effectVal>0) speed1=effectVal;
|
if (song.grooves.empty()) {
|
||||||
|
if (effectVal>0) speeds.val[0]=effectVal;
|
||||||
|
} else {
|
||||||
|
if (effectVal<(short)song.grooves.size()) {
|
||||||
|
speeds=song.grooves[effectVal];
|
||||||
|
curSpeed=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0x0f: // speed 2
|
case 0x0f: // speed 1/speed 2
|
||||||
if (effectVal>0) speed2=effectVal;
|
if (speeds.len==2 && song.grooves.empty()) {
|
||||||
|
if (effectVal>0) speeds.val[1]=effectVal;
|
||||||
|
} else {
|
||||||
|
if (effectVal>0) speeds.val[0]=effectVal;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0x0b: // change order
|
case 0x0b: // change order
|
||||||
if (changeOrd==-1 || song.jumpTreatment==0) {
|
if (changeOrd==-1 || song.jumpTreatment==0) {
|
||||||
|
@ -1071,6 +1082,9 @@ void DivEngine::nextRow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (song.brokenSpeedSel) {
|
if (song.brokenSpeedSel) {
|
||||||
|
unsigned char speed2=(speeds.len>=2)?speeds.val[1]:speeds.val[0];
|
||||||
|
unsigned char speed1=speeds.val[0];
|
||||||
|
|
||||||
if ((curSubSong->patLen&1) && curOrder&1) {
|
if ((curSubSong->patLen&1) && curOrder&1) {
|
||||||
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
|
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
|
||||||
nextSpeed=(curRow&1)?speed1:speed2;
|
nextSpeed=(curRow&1)?speed1:speed2;
|
||||||
|
@ -1079,14 +1093,10 @@ void DivEngine::nextRow() {
|
||||||
nextSpeed=(curRow&1)?speed2:speed1;
|
nextSpeed=(curRow&1)?speed2:speed1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (speedAB) {
|
ticks=speeds.val[curSpeed]*(curSubSong->timeBase+1);
|
||||||
ticks=speed2*(curSubSong->timeBase+1);
|
curSpeed++;
|
||||||
nextSpeed=speed1;
|
if (curSpeed>=speeds.len) curSpeed=0;
|
||||||
} else {
|
nextSpeed=speeds.val[curSpeed];
|
||||||
ticks=speed1*(curSubSong->timeBase+1);
|
|
||||||
nextSpeed=speed2;
|
|
||||||
}
|
|
||||||
speedAB=!speedAB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// post row details
|
// post row details
|
||||||
|
|
|
@ -127,10 +127,20 @@ enum DivSystem {
|
||||||
DIV_SYSTEM_YM2608_CSM
|
DIV_SYSTEM_YM2608_CSM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DivGroovePattern {
|
||||||
|
unsigned char val[16];
|
||||||
|
unsigned char len;
|
||||||
|
DivGroovePattern():
|
||||||
|
len(1) {
|
||||||
|
memset(val,6,16);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DivSubSong {
|
struct DivSubSong {
|
||||||
String name, notes;
|
String name, notes;
|
||||||
unsigned char hilightA, hilightB;
|
unsigned char hilightA, hilightB;
|
||||||
unsigned char timeBase, speed1, speed2, arpLen;
|
unsigned char timeBase, arpLen;
|
||||||
|
DivGroovePattern speeds;
|
||||||
short virtualTempoN, virtualTempoD;
|
short virtualTempoN, virtualTempoD;
|
||||||
bool pal;
|
bool pal;
|
||||||
bool customTempo;
|
bool customTempo;
|
||||||
|
@ -153,8 +163,6 @@ struct DivSubSong {
|
||||||
hilightA(4),
|
hilightA(4),
|
||||||
hilightB(16),
|
hilightB(16),
|
||||||
timeBase(0),
|
timeBase(0),
|
||||||
speed1(6),
|
|
||||||
speed2(6),
|
|
||||||
arpLen(1),
|
arpLen(1),
|
||||||
virtualTempoN(150),
|
virtualTempoN(150),
|
||||||
virtualTempoD(150),
|
virtualTempoD(150),
|
||||||
|
@ -338,6 +346,7 @@ struct DivSong {
|
||||||
|
|
||||||
std::vector<DivSubSong*> subsong;
|
std::vector<DivSubSong*> subsong;
|
||||||
std::vector<unsigned int> patchbay;
|
std::vector<unsigned int> patchbay;
|
||||||
|
std::vector<DivGroovePattern> grooves;
|
||||||
|
|
||||||
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound;
|
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound;
|
||||||
DivWavetable nullWave;
|
DivWavetable nullWave;
|
||||||
|
|
|
@ -265,6 +265,9 @@ void FurnaceGUI::doAction(int what) {
|
||||||
case GUI_ACTION_WINDOW_FIND:
|
case GUI_ACTION_WINDOW_FIND:
|
||||||
nextWindow=GUI_WINDOW_FIND;
|
nextWindow=GUI_WINDOW_FIND;
|
||||||
break;
|
break;
|
||||||
|
case GUI_ACTION_WINDOW_GROOVES:
|
||||||
|
nextWindow=GUI_WINDOW_GROOVES;
|
||||||
|
break;
|
||||||
|
|
||||||
case GUI_ACTION_COLLAPSE_WINDOW:
|
case GUI_ACTION_COLLAPSE_WINDOW:
|
||||||
collapseWindow=true;
|
collapseWindow=true;
|
||||||
|
@ -358,6 +361,9 @@ void FurnaceGUI::doAction(int what) {
|
||||||
case GUI_WINDOW_FIND:
|
case GUI_WINDOW_FIND:
|
||||||
findOpen=false;
|
findOpen=false;
|
||||||
break;
|
break;
|
||||||
|
case GUI_WINDOW_GROOVES:
|
||||||
|
groovesOpen=false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
154
src/gui/grooves.cpp
Normal file
154
src/gui/grooves.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2023 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gui.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
#include "IconsFontAwesome4.h"
|
||||||
|
#include <fmt/printf.h>
|
||||||
|
#include "intConst.h"
|
||||||
|
|
||||||
|
void FurnaceGUI::drawGrooves() {
|
||||||
|
if (nextWindow==GUI_WINDOW_GROOVES) {
|
||||||
|
groovesOpen=true;
|
||||||
|
ImGui::SetNextWindowFocus();
|
||||||
|
nextWindow=GUI_WINDOW_NOTHING;
|
||||||
|
}
|
||||||
|
if (!groovesOpen) return;
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||||
|
if (ImGui::Begin("Grooves",&groovesOpen,globalWinFlags)) {
|
||||||
|
int delGroove=-1;
|
||||||
|
|
||||||
|
ImGui::Text("use effect 09xx to select a groove pattern.");
|
||||||
|
if (!e->song.grooves.empty()) if (ImGui::BeginTable("GrooveList",3,ImGuiTableFlags_Borders)) {
|
||||||
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("#");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("pattern");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("remove");
|
||||||
|
|
||||||
|
int index=0;
|
||||||
|
for (DivGroovePattern& i: e->song.grooves) {
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::PushFont(patFont);
|
||||||
|
ImGui::Text("%.2X",index);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
String grooveStr;
|
||||||
|
|
||||||
|
if (curGroove==index) {
|
||||||
|
int intVersion[256];
|
||||||
|
unsigned char intVersionLen=i.len;
|
||||||
|
unsigned char ignoredLoop=0;
|
||||||
|
unsigned char ignoredRel=0;
|
||||||
|
memset(intVersion,0,sizeof(int));
|
||||||
|
for (int j=0; j<16; j++) {
|
||||||
|
intVersion[j]=i.val[j];
|
||||||
|
}
|
||||||
|
if (intVersionLen>16) intVersionLen=16;
|
||||||
|
grooveStr=fmt::sprintf("##_GRI%d",index);
|
||||||
|
bool wantedFocus=wantGrooveListFocus;
|
||||||
|
if (wantGrooveListFocus) {
|
||||||
|
wantGrooveListFocus=false;
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
ImGui::SetKeyboardFocusHere();
|
||||||
|
}
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::InputText(grooveStr.c_str(),&grooveListString)) {
|
||||||
|
decodeMMLStr(grooveListString,intVersion,intVersionLen,ignoredLoop,1,255,ignoredRel);
|
||||||
|
if (intVersionLen<1) {
|
||||||
|
intVersionLen=1;
|
||||||
|
intVersion[0]=6;
|
||||||
|
}
|
||||||
|
if (intVersionLen>16) intVersionLen=16;
|
||||||
|
e->lockEngine([&i,intVersion,intVersionLen]() {
|
||||||
|
i.len=intVersionLen;
|
||||||
|
for (int j=0; j<16; j++) {
|
||||||
|
i.val[j]=intVersion[j];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
if (!ImGui::IsItemActive() && !wantedFocus) {
|
||||||
|
curGroove=-1;
|
||||||
|
//encodeMMLStr(grooveListString,intVersion,intVersionLen,-1,-1,false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String grooveStr;
|
||||||
|
|
||||||
|
for (int j=0; j<i.len; j++) {
|
||||||
|
if (j>0) {
|
||||||
|
grooveStr+=' ';
|
||||||
|
}
|
||||||
|
grooveStr+=fmt::sprintf("%d",(int)i.val[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t groovePrevLen=grooveStr.size();
|
||||||
|
|
||||||
|
grooveStr+=fmt::sprintf("##_GR%d",index);
|
||||||
|
|
||||||
|
if (ImGui::Selectable(grooveStr.c_str(),false)) {
|
||||||
|
curGroove=index;
|
||||||
|
grooveListString=grooveStr.substr(0,groovePrevLen);
|
||||||
|
wantGrooveListFocus=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
String grooveID=fmt::sprintf(ICON_FA_TIMES "##GRR%d",index);
|
||||||
|
if (ImGui::Button(grooveID.c_str())) {
|
||||||
|
delGroove=index;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delGroove>=0) {
|
||||||
|
e->lockEngine([this,delGroove]() {
|
||||||
|
e->song.grooves.erase(e->song.grooves.begin()+delGroove);
|
||||||
|
});
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_FA_PLUS "##AddGroove")) {
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->song.grooves.push_back(DivGroovePattern());
|
||||||
|
});
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) {
|
||||||
|
curWindow=GUI_WINDOW_GROOVES;
|
||||||
|
} else {
|
||||||
|
curGroove=-1;
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
|
@ -988,15 +988,19 @@ void FurnaceGUI::prepareLayout() {
|
||||||
fclose(check);
|
fclose(check);
|
||||||
}
|
}
|
||||||
|
|
||||||
float FurnaceGUI::calcBPM(int s1, int s2, float hz, int vN, int vD) {
|
float FurnaceGUI::calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD) {
|
||||||
float hl=e->curSubSong->hilightA;
|
float hl=e->curSubSong->hilightA;
|
||||||
if (hl<=0.0f) hl=4.0f;
|
if (hl<=0.0f) hl=4.0f;
|
||||||
float timeBase=e->curSubSong->timeBase+1;
|
float timeBase=e->curSubSong->timeBase+1;
|
||||||
float speedSum=s1+s2;
|
float speedSum=0;
|
||||||
|
for (int i=0; i<MIN(16,speeds.len); i++) {
|
||||||
|
speedSum+=speeds.val[i];
|
||||||
|
}
|
||||||
|
speedSum/=MAX(1,speeds.len);
|
||||||
if (timeBase<1.0f) timeBase=1.0f;
|
if (timeBase<1.0f) timeBase=1.0f;
|
||||||
if (speedSum<1.0f) speedSum=1.0f;
|
if (speedSum<1.0f) speedSum=1.0f;
|
||||||
if (vD<1) vD=1;
|
if (vD<1) vD=1;
|
||||||
return (120.0f*hz/(timeBase*hl*speedSum))*(float)vN/(float)vD;
|
return (60.0f*hz/(timeBase*hl*speedSum))*(float)vN/(float)vD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::play(int row) {
|
void FurnaceGUI::play(int row) {
|
||||||
|
@ -3740,6 +3744,7 @@ bool FurnaceGUI::loop() {
|
||||||
if (ImGui::MenuItem("orders",BIND_FOR(GUI_ACTION_WINDOW_ORDERS),ordersOpen)) ordersOpen=!ordersOpen;
|
if (ImGui::MenuItem("orders",BIND_FOR(GUI_ACTION_WINDOW_ORDERS),ordersOpen)) ordersOpen=!ordersOpen;
|
||||||
if (ImGui::MenuItem("pattern",BIND_FOR(GUI_ACTION_WINDOW_PATTERN),patternOpen)) patternOpen=!patternOpen;
|
if (ImGui::MenuItem("pattern",BIND_FOR(GUI_ACTION_WINDOW_PATTERN),patternOpen)) patternOpen=!patternOpen;
|
||||||
if (ImGui::MenuItem("mixer",BIND_FOR(GUI_ACTION_WINDOW_MIXER),mixerOpen)) mixerOpen=!mixerOpen;
|
if (ImGui::MenuItem("mixer",BIND_FOR(GUI_ACTION_WINDOW_MIXER),mixerOpen)) mixerOpen=!mixerOpen;
|
||||||
|
if (ImGui::MenuItem("grooves",BIND_FOR(GUI_ACTION_WINDOW_GROOVES),groovesOpen)) groovesOpen=!groovesOpen;
|
||||||
if (ImGui::MenuItem("channels",BIND_FOR(GUI_ACTION_WINDOW_CHANNELS),channelsOpen)) channelsOpen=!channelsOpen;
|
if (ImGui::MenuItem("channels",BIND_FOR(GUI_ACTION_WINDOW_CHANNELS),channelsOpen)) channelsOpen=!channelsOpen;
|
||||||
if (ImGui::MenuItem("pattern manager",BIND_FOR(GUI_ACTION_WINDOW_PAT_MANAGER),patManagerOpen)) patManagerOpen=!patManagerOpen;
|
if (ImGui::MenuItem("pattern manager",BIND_FOR(GUI_ACTION_WINDOW_PAT_MANAGER),patManagerOpen)) patManagerOpen=!patManagerOpen;
|
||||||
if (ImGui::MenuItem("chip manager",BIND_FOR(GUI_ACTION_WINDOW_SYS_MANAGER),sysManagerOpen)) sysManagerOpen=!sysManagerOpen;
|
if (ImGui::MenuItem("chip manager",BIND_FOR(GUI_ACTION_WINDOW_SYS_MANAGER),sysManagerOpen)) sysManagerOpen=!sysManagerOpen;
|
||||||
|
@ -3778,7 +3783,21 @@ bool FurnaceGUI::loop() {
|
||||||
if (e->isPlaying()) {
|
if (e->isPlaying()) {
|
||||||
int totalTicks=e->getTotalTicks();
|
int totalTicks=e->getTotalTicks();
|
||||||
int totalSeconds=e->getTotalSeconds();
|
int totalSeconds=e->getTotalSeconds();
|
||||||
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
|
||||||
|
String info;
|
||||||
|
|
||||||
|
DivGroovePattern gp=e->getSpeeds();
|
||||||
|
if (gp.len==2) {
|
||||||
|
info=fmt::sprintf("| Speed %d:%d",gp.val[0],gp.val[1]);
|
||||||
|
} else if (gp.len==1) {
|
||||||
|
info=fmt::sprintf("| Speed %d",gp.val[0]);
|
||||||
|
} else {
|
||||||
|
info="| Groove";
|
||||||
|
}
|
||||||
|
|
||||||
|
info+=fmt::sprintf(" @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
||||||
|
|
||||||
|
ImGui::TextUnformatted(info.c_str());
|
||||||
} else {
|
} else {
|
||||||
bool hasInfo=false;
|
bool hasInfo=false;
|
||||||
String info;
|
String info;
|
||||||
|
@ -3917,6 +3936,7 @@ bool FurnaceGUI::loop() {
|
||||||
drawPattern();
|
drawPattern();
|
||||||
drawEditControls();
|
drawEditControls();
|
||||||
drawSpeed();
|
drawSpeed();
|
||||||
|
drawGrooves();
|
||||||
drawSongInfo();
|
drawSongInfo();
|
||||||
drawOrders();
|
drawOrders();
|
||||||
drawSampleList();
|
drawSampleList();
|
||||||
|
@ -5248,6 +5268,7 @@ bool FurnaceGUI::init() {
|
||||||
sysManagerOpen=e->getConfBool("sysManagerOpen",false);
|
sysManagerOpen=e->getConfBool("sysManagerOpen",false);
|
||||||
clockOpen=e->getConfBool("clockOpen",false);
|
clockOpen=e->getConfBool("clockOpen",false);
|
||||||
speedOpen=e->getConfBool("speedOpen",true);
|
speedOpen=e->getConfBool("speedOpen",true);
|
||||||
|
groovesOpen=e->getConfBool("groovesOpen",false);
|
||||||
regViewOpen=e->getConfBool("regViewOpen",false);
|
regViewOpen=e->getConfBool("regViewOpen",false);
|
||||||
logOpen=e->getConfBool("logOpen",false);
|
logOpen=e->getConfBool("logOpen",false);
|
||||||
effectListOpen=e->getConfBool("effectListOpen",false);
|
effectListOpen=e->getConfBool("effectListOpen",false);
|
||||||
|
@ -5623,6 +5644,7 @@ void FurnaceGUI::commitState() {
|
||||||
e->setConf("sysManagerOpen",sysManagerOpen);
|
e->setConf("sysManagerOpen",sysManagerOpen);
|
||||||
e->setConf("clockOpen",clockOpen);
|
e->setConf("clockOpen",clockOpen);
|
||||||
e->setConf("speedOpen",speedOpen);
|
e->setConf("speedOpen",speedOpen);
|
||||||
|
e->setConf("groovesOpen",groovesOpen);
|
||||||
e->setConf("regViewOpen",regViewOpen);
|
e->setConf("regViewOpen",regViewOpen);
|
||||||
e->setConf("logOpen",logOpen);
|
e->setConf("logOpen",logOpen);
|
||||||
e->setConf("effectListOpen",effectListOpen);
|
e->setConf("effectListOpen",effectListOpen);
|
||||||
|
@ -5835,6 +5857,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
dragDestinationY(0),
|
dragDestinationY(0),
|
||||||
oldBeat(-1),
|
oldBeat(-1),
|
||||||
oldBar(-1),
|
oldBar(-1),
|
||||||
|
curGroove(-1),
|
||||||
soloTimeout(0.0f),
|
soloTimeout(0.0f),
|
||||||
exportFadeOut(5.0),
|
exportFadeOut(5.0),
|
||||||
editControlsOpen(true),
|
editControlsOpen(true),
|
||||||
|
@ -5870,6 +5893,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
sysManagerOpen(false),
|
sysManagerOpen(false),
|
||||||
clockOpen(false),
|
clockOpen(false),
|
||||||
speedOpen(true),
|
speedOpen(true),
|
||||||
|
groovesOpen(false),
|
||||||
clockShowReal(true),
|
clockShowReal(true),
|
||||||
clockShowRow(true),
|
clockShowRow(true),
|
||||||
clockShowBeat(true),
|
clockShowBeat(true),
|
||||||
|
@ -5898,10 +5922,12 @@ FurnaceGUI::FurnaceGUI():
|
||||||
latchNibble(false),
|
latchNibble(false),
|
||||||
nonLatchNibble(false),
|
nonLatchNibble(false),
|
||||||
keepLoopAlive(false),
|
keepLoopAlive(false),
|
||||||
|
keepGrooveAlive(false),
|
||||||
orderScrollLocked(false),
|
orderScrollLocked(false),
|
||||||
orderScrollTolerance(false),
|
orderScrollTolerance(false),
|
||||||
dragMobileMenu(false),
|
dragMobileMenu(false),
|
||||||
dragMobileEditButton(false),
|
dragMobileEditButton(false),
|
||||||
|
wantGrooveListFocus(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),
|
||||||
|
|
|
@ -321,6 +321,7 @@ enum FurnaceGUIWindows {
|
||||||
GUI_WINDOW_SUBSONGS,
|
GUI_WINDOW_SUBSONGS,
|
||||||
GUI_WINDOW_FIND,
|
GUI_WINDOW_FIND,
|
||||||
GUI_WINDOW_CLOCK,
|
GUI_WINDOW_CLOCK,
|
||||||
|
GUI_WINDOW_GROOVES,
|
||||||
GUI_WINDOW_SPOILER
|
GUI_WINDOW_SPOILER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -466,6 +467,7 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_WINDOW_SUBSONGS,
|
GUI_ACTION_WINDOW_SUBSONGS,
|
||||||
GUI_ACTION_WINDOW_FIND,
|
GUI_ACTION_WINDOW_FIND,
|
||||||
GUI_ACTION_WINDOW_CLOCK,
|
GUI_ACTION_WINDOW_CLOCK,
|
||||||
|
GUI_ACTION_WINDOW_GROOVES,
|
||||||
|
|
||||||
GUI_ACTION_COLLAPSE_WINDOW,
|
GUI_ACTION_COLLAPSE_WINDOW,
|
||||||
GUI_ACTION_CLOSE_WINDOW,
|
GUI_ACTION_CLOSE_WINDOW,
|
||||||
|
@ -1097,7 +1099,7 @@ class FurnaceGUI {
|
||||||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
|
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
|
||||||
String workingDirLayout, workingDirROM, workingDirTest;
|
String workingDirLayout, workingDirROM, workingDirTest;
|
||||||
String mmlString[32];
|
String mmlString[32];
|
||||||
String mmlStringW, mmlStringSNES;
|
String mmlStringW, mmlStringSNES, grooveString, grooveListString;
|
||||||
|
|
||||||
std::vector<DivSystem> sysSearchResults;
|
std::vector<DivSystem> sysSearchResults;
|
||||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||||
|
@ -1456,6 +1458,7 @@ class FurnaceGUI {
|
||||||
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
|
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
|
||||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
|
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
|
||||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
||||||
|
int curGroove;
|
||||||
float soloTimeout;
|
float soloTimeout;
|
||||||
|
|
||||||
double exportFadeOut;
|
double exportFadeOut;
|
||||||
|
@ -1465,6 +1468,7 @@ class FurnaceGUI {
|
||||||
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
||||||
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
||||||
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen;
|
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen;
|
||||||
|
bool groovesOpen;
|
||||||
|
|
||||||
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
|
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
|
||||||
float clockMetroTick[16];
|
float clockMetroTick[16];
|
||||||
|
@ -1472,7 +1476,7 @@ 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, dragMobileEditButton;
|
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
|
||||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||||
float peak[DIV_MAX_OUTPUTS];
|
float peak[DIV_MAX_OUTPUTS];
|
||||||
|
@ -1801,7 +1805,7 @@ class FurnaceGUI {
|
||||||
void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow);
|
void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow);
|
||||||
void popAccentColors();
|
void popAccentColors();
|
||||||
|
|
||||||
float calcBPM(int s1, int s2, float hz, int vN, int vD);
|
float calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD);
|
||||||
|
|
||||||
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
|
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
|
||||||
|
|
||||||
|
@ -1821,6 +1825,7 @@ class FurnaceGUI {
|
||||||
void drawEditControls();
|
void drawEditControls();
|
||||||
void drawSongInfo(bool asChild=false);
|
void drawSongInfo(bool asChild=false);
|
||||||
void drawSpeed(bool asChild=false);
|
void drawSpeed(bool asChild=false);
|
||||||
|
void drawGrooves();
|
||||||
void drawOrders();
|
void drawOrders();
|
||||||
void drawPattern();
|
void drawPattern();
|
||||||
void drawInsList(bool asChild=false);
|
void drawInsList(bool asChild=false);
|
||||||
|
|
|
@ -520,6 +520,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
||||||
D("WINDOW_SUBSONGS", "Subsongs", 0),
|
D("WINDOW_SUBSONGS", "Subsongs", 0),
|
||||||
D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
|
D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
|
||||||
D("WINDOW_CLOCK", "Clock", 0),
|
D("WINDOW_CLOCK", "Clock", 0),
|
||||||
|
D("WINDOW_GROOVES", "Grooves", 0),
|
||||||
|
|
||||||
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
|
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
|
||||||
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
|
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
|
||||||
|
|
|
@ -31,19 +31,26 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
||||||
if (!speedOpen && !asChild) return;
|
if (!speedOpen && !asChild) return;
|
||||||
bool began=asChild?ImGui::BeginChild("Speed"):ImGui::Begin("Speed",&speedOpen,globalWinFlags);
|
bool began=asChild?ImGui::BeginChild("Speed"):ImGui::Begin("Speed",&speedOpen,globalWinFlags);
|
||||||
if (began) {
|
if (began) {
|
||||||
if (ImGui::BeginTable("Props",3,ImGuiTableFlags_SizingStretchProp)) {
|
if (ImGui::BeginTable("Props",2,ImGuiTableFlags_SizingStretchProp)) {
|
||||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
|
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
|
||||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
|
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
|
||||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) {
|
if (ImGui::SmallButton(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) {
|
||||||
tempoView=!tempoView;
|
tempoView=!tempoView;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
if (tempoView) {
|
||||||
|
ImGui::SetTooltip("click to display tick rate");
|
||||||
|
} else {
|
||||||
|
ImGui::SetTooltip("click to display base tempo");
|
||||||
|
}
|
||||||
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
float avail=ImGui::GetContentRegionAvail().x;
|
float avail=ImGui::GetContentRegionAvail().x;
|
||||||
ImGui::SetNextItemWidth(avail);
|
float halfAvail=(avail-ImGui::GetStyle().ItemSpacing.x)*0.5;
|
||||||
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz;
|
float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz;
|
||||||
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
|
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
|
||||||
if (tempoView) setHz/=2.5;
|
if (tempoView) setHz/=2.5;
|
||||||
|
@ -52,40 +59,112 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
||||||
e->setSongRate(setHz,setHz<52);
|
e->setSongRate(setHz,setHz<52);
|
||||||
}
|
}
|
||||||
if (tempoView) {
|
if (tempoView) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::Text("= %gHz",e->curSubSong->hz);
|
ImGui::Text("= %gHz",e->curSubSong->hz);
|
||||||
} else {
|
} else {
|
||||||
if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) {
|
if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::Text("PAL");
|
ImGui::Text("PAL");
|
||||||
}
|
}
|
||||||
if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) {
|
if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::Text("NTSC");
|
ImGui::Text("NTSC");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Speed");
|
if (keepGrooveAlive || e->curSubSong->speeds.len>2) {
|
||||||
ImGui::TableNextColumn();
|
if (ImGui::SmallButton("Groove")) {
|
||||||
ImGui::SetNextItemWidth(avail);
|
e->lockEngine([this]() {
|
||||||
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED
|
e->curSubSong->speeds.len=1;
|
||||||
if (e->curSubSong->speed1<1) e->curSubSong->speed1=1;
|
});
|
||||||
if (e->isPlaying()) play();
|
if (e->isPlaying()) play();
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("click for one speed");
|
||||||
|
}
|
||||||
|
} else if (e->curSubSong->speeds.len>1) {
|
||||||
|
if (ImGui::SmallButton("Speeds")) {
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->curSubSong->speeds.len=4;
|
||||||
|
e->curSubSong->speeds.val[2]=e->curSubSong->speeds.val[0];
|
||||||
|
e->curSubSong->speeds.val[3]=e->curSubSong->speeds.val[1];
|
||||||
|
});
|
||||||
|
if (e->isPlaying()) play();
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("click for groove pattern");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ImGui::SmallButton("Speed")) {
|
||||||
|
e->lockEngine([this]() {
|
||||||
|
e->curSubSong->speeds.len=2;
|
||||||
|
e->curSubSong->speeds.val[1]=e->curSubSong->speeds.val[0];
|
||||||
|
});
|
||||||
|
if (e->isPlaying()) play();
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("click for two (alternating) speeds");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
if (keepGrooveAlive || e->curSubSong->speeds.len>2) {
|
||||||
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED
|
int intVersion[256];
|
||||||
if (e->curSubSong->speed2<1) e->curSubSong->speed2=1;
|
unsigned char intVersionLen=e->curSubSong->speeds.len;
|
||||||
if (e->isPlaying()) play();
|
unsigned char ignoredLoop=0;
|
||||||
|
unsigned char ignoredRel=0;
|
||||||
|
memset(intVersion,0,sizeof(int));
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
intVersion[i]=e->curSubSong->speeds.val[i];
|
||||||
|
}
|
||||||
|
if (intVersionLen>16) intVersionLen=16;
|
||||||
|
|
||||||
|
keepGrooveAlive=false;
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(avail);
|
||||||
|
if (ImGui::InputText("##SpeedG",&grooveString)) {
|
||||||
|
decodeMMLStr(grooveString,intVersion,intVersionLen,ignoredLoop,1,255,ignoredRel);
|
||||||
|
if (intVersionLen<1) {
|
||||||
|
intVersionLen=1;
|
||||||
|
intVersion[0]=6;
|
||||||
|
}
|
||||||
|
if (intVersionLen>16) intVersionLen=16;
|
||||||
|
e->lockEngine([this,intVersion,intVersionLen]() {
|
||||||
|
e->curSubSong->speeds.len=intVersionLen;
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
e->curSubSong->speeds.val[i]=intVersion[i];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (e->isPlaying()) play();
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
if (!ImGui::IsItemActive()) {
|
||||||
|
encodeMMLStr(grooveString,intVersion,intVersionLen,-1,-1,false);
|
||||||
|
} else {
|
||||||
|
keepGrooveAlive=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
|
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speeds.val[0],&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
|
if (e->curSubSong->speeds.val[0]<1) e->curSubSong->speeds.val[0]=1;
|
||||||
|
if (e->isPlaying()) play();
|
||||||
|
}
|
||||||
|
if (e->curSubSong->speeds.len>1) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
|
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speeds.val[1],&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
|
if (e->curSubSong->speeds.val[1]<1) e->curSubSong->speeds.val[1]=1;
|
||||||
|
if (e->isPlaying()) play();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Virtual Tempo");
|
ImGui::Text("Virtual Tempo");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
|
if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
|
||||||
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
|
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
|
||||||
|
@ -93,8 +172,8 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("Numerator");
|
ImGui::SetTooltip("Numerator");
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
|
if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
|
||||||
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
|
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
|
||||||
|
@ -105,28 +184,28 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("TimeBase");
|
ImGui::Text("Divider");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
unsigned char realTB=e->curSubSong->timeBase+1;
|
unsigned char realTB=e->curSubSong->timeBase+1;
|
||||||
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
|
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||||
if (realTB<1) realTB=1;
|
if (realTB<1) realTB=1;
|
||||||
if (realTB>16) realTB=16;
|
if (realTB>16) realTB=16;
|
||||||
e->curSubSong->timeBase=realTB-1;
|
e->curSubSong->timeBase=realTB-1;
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
|
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Highlight");
|
ImGui::Text("Highlight");
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) {
|
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) {
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(avail);
|
ImGui::SetNextItemWidth(halfAvail);
|
||||||
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) {
|
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) {
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue