dev96 - add virtual tempo
This commit is contained in:
parent
f1ca53561f
commit
cfa05143ab
1
TODO.md
1
TODO.md
|
@ -1,5 +1,6 @@
|
||||||
# to-do for 0.6pre1
|
# to-do for 0.6pre1
|
||||||
|
|
||||||
|
- finish ExtCh on OPN/OPNA
|
||||||
- RF5C68 system
|
- RF5C68 system
|
||||||
- ZX beeper system overlay percussion
|
- ZX beeper system overlay percussion
|
||||||
- ADPCM chips
|
- ADPCM chips
|
||||||
|
|
|
@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 96: Furnace dev96
|
||||||
- 95: Furnace dev95
|
- 95: Furnace dev95
|
||||||
- 94: Furnace dev94
|
- 94: Furnace dev94
|
||||||
- 93: Furnace dev93
|
- 93: Furnace dev93
|
||||||
|
@ -296,7 +297,10 @@ size | description
|
||||||
1 | SN duty macro always resets phase (>=86) or reserved
|
1 | SN duty macro always resets phase (>=86) or reserved
|
||||||
1 | pitch macro is linear (>=90) or reserved
|
1 | pitch macro is linear (>=90) or reserved
|
||||||
1 | pitch slide speed in full linear pitch mode (>=94) or reserved
|
1 | pitch slide speed in full linear pitch mode (>=94) or reserved
|
||||||
18 | reserved
|
14 | reserved
|
||||||
|
--- | **virtual tempo data**
|
||||||
|
2 | virtual tempo numerator of first song (>=96) or reserved
|
||||||
|
2 | virtual tempo denominator of first song (>=96) or reserved
|
||||||
--- | **additional subsongs** (>=95)
|
--- | **additional subsongs** (>=95)
|
||||||
STR | first subsong name
|
STR | first subsong name
|
||||||
STR | first subsong comment
|
STR | first subsong comment
|
||||||
|
@ -328,7 +332,8 @@ size | description
|
||||||
| - the limit is 256.
|
| - the limit is 256.
|
||||||
1 | highlight A
|
1 | highlight A
|
||||||
1 | highlight B
|
1 | highlight B
|
||||||
4 | reserved
|
2 | virtual tempo numerator
|
||||||
|
2 | virtual tempo denominator
|
||||||
STR | subsong name
|
STR | subsong name
|
||||||
STR | subsong comment
|
STR | subsong comment
|
||||||
??? | orders
|
??? | orders
|
||||||
|
|
|
@ -1068,6 +1068,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
||||||
endOfSong=false;
|
endOfSong=false;
|
||||||
} else {
|
} else {
|
||||||
ticks=1;
|
ticks=1;
|
||||||
|
tempoAccum=0;
|
||||||
totalTicks=0;
|
totalTicks=0;
|
||||||
totalSeconds=0;
|
totalSeconds=0;
|
||||||
totalTicksR=0;
|
totalTicksR=0;
|
||||||
|
@ -2669,6 +2670,7 @@ void DivEngine::quitDispatch() {
|
||||||
speedAB=false;
|
speedAB=false;
|
||||||
endOfSong=false;
|
endOfSong=false;
|
||||||
ticks=0;
|
ticks=0;
|
||||||
|
tempoAccum=0;
|
||||||
curRow=0;
|
curRow=0;
|
||||||
curOrder=0;
|
curOrder=0;
|
||||||
nextSpeed=3;
|
nextSpeed=3;
|
||||||
|
|
|
@ -45,8 +45,8 @@
|
||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||||
|
|
||||||
#define DIV_VERSION "dev95"
|
#define DIV_VERSION "dev96"
|
||||||
#define DIV_ENGINE_VERSION 95
|
#define DIV_ENGINE_VERSION 96
|
||||||
|
|
||||||
// for imports
|
// for imports
|
||||||
#define DIV_VERSION_MOD 0xff01
|
#define DIV_VERSION_MOD 0xff01
|
||||||
|
@ -312,6 +312,7 @@ class DivEngine {
|
||||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||||
unsigned char extValue;
|
unsigned char extValue;
|
||||||
unsigned char speed1, speed2;
|
unsigned char speed1, speed2;
|
||||||
|
short tempoAccum;
|
||||||
DivStatusView view;
|
DivStatusView view;
|
||||||
DivHaltPositions haltOn;
|
DivHaltPositions haltOn;
|
||||||
DivChannelState chan[DIV_MAX_CHANS];
|
DivChannelState chan[DIV_MAX_CHANS];
|
||||||
|
@ -942,6 +943,7 @@ class DivEngine {
|
||||||
extValue(0),
|
extValue(0),
|
||||||
speed1(3),
|
speed1(3),
|
||||||
speed2(3),
|
speed2(3),
|
||||||
|
tempoAccum(0),
|
||||||
view(DIV_STATUS_NOTHING),
|
view(DIV_STATUS_NOTHING),
|
||||||
haltOn(DIV_HALT_NONE),
|
haltOn(DIV_HALT_NONE),
|
||||||
audioEngine(DIV_AUDIO_NULL),
|
audioEngine(DIV_AUDIO_NULL),
|
||||||
|
|
|
@ -1404,11 +1404,19 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
for (int i=0; i<18; i++) {
|
for (int i=0; i<14; i++) {
|
||||||
reader.readC();
|
reader.readC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first song virtual tempo
|
||||||
|
if (ds.version>=96) {
|
||||||
|
subSong->virtualTempoN=reader.readS();
|
||||||
|
subSong->virtualTempoD=reader.readS();
|
||||||
|
} else {
|
||||||
|
reader.readI();
|
||||||
|
}
|
||||||
|
|
||||||
// subsongs
|
// subsongs
|
||||||
if (ds.version>=95) {
|
if (ds.version>=95) {
|
||||||
subSong->name=reader.readString();
|
subSong->name=reader.readString();
|
||||||
|
@ -1457,7 +1465,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
subSong->hilightA=reader.readC();
|
subSong->hilightA=reader.readC();
|
||||||
subSong->hilightB=reader.readC();
|
subSong->hilightB=reader.readC();
|
||||||
|
|
||||||
reader.readI(); // reserved
|
if (ds.version>=96) {
|
||||||
|
subSong->virtualTempoN=reader.readS();
|
||||||
|
subSong->virtualTempoD=reader.readS();
|
||||||
|
} else {
|
||||||
|
reader.readI();
|
||||||
|
}
|
||||||
|
|
||||||
subSong->name=reader.readString();
|
subSong->name=reader.readString();
|
||||||
subSong->notes=reader.readString();
|
subSong->notes=reader.readString();
|
||||||
|
@ -2858,9 +2871,13 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeC(song.snDutyReset);
|
w->writeC(song.snDutyReset);
|
||||||
w->writeC(song.pitchMacroIsLinear);
|
w->writeC(song.pitchMacroIsLinear);
|
||||||
w->writeC(song.pitchSlideSpeed);
|
w->writeC(song.pitchSlideSpeed);
|
||||||
for (int i=0; i<18; i++) {
|
for (int i=0; i<14; i++) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first subsong virtual tempo
|
||||||
|
w->writeS(subSong->virtualTempoN);
|
||||||
|
w->writeS(subSong->virtualTempoD);
|
||||||
|
|
||||||
// subsong list
|
// subsong list
|
||||||
w->writeString(subSong->name,false);
|
w->writeString(subSong->name,false);
|
||||||
|
@ -2891,7 +2908,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
w->writeS(subSong->ordersLen);
|
w->writeS(subSong->ordersLen);
|
||||||
w->writeC(subSong->hilightA);
|
w->writeC(subSong->hilightA);
|
||||||
w->writeC(subSong->hilightB);
|
w->writeC(subSong->hilightB);
|
||||||
w->writeI(0); // reserved
|
w->writeS(subSong->virtualTempoN);
|
||||||
|
w->writeS(subSong->virtualTempoD);
|
||||||
|
|
||||||
w->writeString(subSong->name,false);
|
w->writeString(subSong->name,false);
|
||||||
w->writeString(subSong->notes,false);
|
w->writeString(subSong->notes,false);
|
||||||
|
@ -3171,6 +3189,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 (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
|
||||||
|
addWarning(".dmf format does not support virtual tempo");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song.tuning<439.99 && song.tuning>440.01) {
|
||||||
|
addWarning(".dmf format does not support tuning");
|
||||||
|
}
|
||||||
|
|
||||||
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||||
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
addWarning("absolute duty/cutoff macro not available in .dmf!");
|
||||||
addWarning("duty precision will be lost");
|
addWarning("duty precision will be lost");
|
||||||
|
|
|
@ -900,16 +900,23 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
if (!freelance) {
|
if (!freelance) {
|
||||||
if (--subticks<=0) {
|
if (--subticks<=0) {
|
||||||
subticks=tickMult;
|
subticks=tickMult;
|
||||||
if (stepPlay!=1) if (--ticks<=0) {
|
if (stepPlay!=1) {
|
||||||
ret=endOfSong;
|
tempoAccum+=curSubSong->virtualTempoN;
|
||||||
if (endOfSong) {
|
while (tempoAccum>=curSubSong->virtualTempoD) {
|
||||||
if (song.loopModality!=2) {
|
tempoAccum-=curSubSong->virtualTempoD;
|
||||||
playSub(true);
|
if (--ticks<=0) {
|
||||||
|
ret=endOfSong;
|
||||||
|
if (endOfSong) {
|
||||||
|
if (song.loopModality!=2) {
|
||||||
|
playSub(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endOfSong=false;
|
||||||
|
if (stepPlay==2) stepPlay=1;
|
||||||
|
nextRow();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
endOfSong=false;
|
|
||||||
if (stepPlay==2) stepPlay=1;
|
|
||||||
nextRow();
|
|
||||||
}
|
}
|
||||||
// process stuff
|
// process stuff
|
||||||
for (int i=0; i<chans; i++) {
|
for (int i=0; i<chans; i++) {
|
||||||
|
|
|
@ -114,6 +114,7 @@ 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, speed1, speed2, arpLen;
|
||||||
|
short virtualTempoN, virtualTempoD;
|
||||||
bool pal;
|
bool pal;
|
||||||
bool customTempo;
|
bool customTempo;
|
||||||
float hz;
|
float hz;
|
||||||
|
@ -136,6 +137,8 @@ struct DivSubSong {
|
||||||
speed1(6),
|
speed1(6),
|
||||||
speed2(6),
|
speed2(6),
|
||||||
arpLen(1),
|
arpLen(1),
|
||||||
|
virtualTempoN(150),
|
||||||
|
virtualTempoD(150),
|
||||||
pal(true),
|
pal(true),
|
||||||
customTempo(false),
|
customTempo(false),
|
||||||
hz(60.0),
|
hz(60.0),
|
||||||
|
|
|
@ -792,14 +792,15 @@ void FurnaceGUI::prepareLayout() {
|
||||||
fclose(check);
|
fclose(check);
|
||||||
}
|
}
|
||||||
|
|
||||||
float FurnaceGUI::calcBPM(int s1, int s2, float hz) {
|
float FurnaceGUI::calcBPM(int s1, int s2, 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=s1+s2;
|
||||||
if (timeBase<1.0f) timeBase=1.0f;
|
if (timeBase<1.0f) timeBase=1.0f;
|
||||||
if (speedSum<1.0f) speedSum=1.0f;
|
if (speedSum<1.0f) speedSum=1.0f;
|
||||||
return 120.0f*hz/(timeBase*hl*speedSum);
|
if (vD<1) vD=1;
|
||||||
|
return (120.0f*hz/(timeBase*hl*speedSum))*(float)vN/(float)vD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::play(int row) {
|
void FurnaceGUI::play(int row) {
|
||||||
|
@ -2929,7 +2930,7 @@ bool FurnaceGUI::loop() {
|
||||||
if (e->isPlaying()) {
|
if (e->isPlaying()) {
|
||||||
int totalTicks=e->getTotalTicks();
|
int totalTicks=e->getTotalTicks();
|
||||||
int totalSeconds=e->getTotalSeconds();
|
int totalSeconds=e->getTotalSeconds();
|
||||||
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz()),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
|
||||||
} else {
|
} else {
|
||||||
bool hasInfo=false;
|
bool hasInfo=false;
|
||||||
String info;
|
String info;
|
||||||
|
|
|
@ -1255,7 +1255,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);
|
float calcBPM(int s1, int s2, 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);
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
e->curSubSong->timeBase=realTB-1;
|
e->curSubSong->timeBase=realTB-1;
|
||||||
}
|
}
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz));
|
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
@ -100,6 +100,28 @@ void FurnaceGUI::drawSongInfo() {
|
||||||
if (e->isPlaying()) play();
|
if (e->isPlaying()) play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("Virtual Tempo");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(avail);
|
||||||
|
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>255) e->curSubSong->virtualTempoN=255;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Numerator");
|
||||||
|
}
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(avail);
|
||||||
|
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>255) e->curSubSong->virtualTempoD=255;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Denominator (set to base tempo)");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::Text("Highlight");
|
ImGui::Text("Highlight");
|
||||||
|
|
Loading…
Reference in a new issue