From 96983e3d48fe52ad913688d966725ae1d87e8036 Mon Sep 17 00:00:00 2001 From: cam900 Date: Thu, 5 Jan 2023 09:49:21 +0900 Subject: [PATCH 01/11] Add SSG and FM/ADPCM volume mixer config for OPN/A/B --- src/engine/platform/fmshared_OPN.h | 4 +++ src/engine/platform/ym2203.cpp | 7 +++-- src/engine/platform/ym2608.cpp | 12 +++++--- src/engine/platform/ym2610.cpp | 10 ++++--- src/engine/platform/ym2610b.cpp | 10 ++++--- src/engine/platform/ym2610shared.h | 2 ++ src/gui/sysConf.cpp | 48 ++++++++++++++++++++++++++++++ 7 files changed, 79 insertions(+), 14 deletions(-) diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index d83ad0770..427a231cf 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -154,6 +154,8 @@ class DivPlatformOPN: public DivPlatformFMBase { unsigned int ayDiv; unsigned char csmChan; unsigned char lfoValue; + unsigned char ssgVol; + unsigned char fmVol; bool extSys, useCombo, fbAllOps; DivConfig ayFlags; @@ -172,6 +174,8 @@ class DivPlatformOPN: public DivPlatformFMBase { ayDiv(a), csmChan(cc), lfoValue(0), + ssgVol(255), + fmVol(255), extSys(isExtSys), useCombo(false), fbAllOps(false) {} diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 3419a1b6e..85c566d49 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -210,11 +210,12 @@ void DivPlatformYM2203::acquire_combo(short* bufL, short* bufR, size_t start, si ); os&=~3; + os=(os*fmVol)/255; // ymfm part fm->generate(&fmout); - os+=((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1); + os+=(((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)/255; if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -255,7 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short* bufL, short* bufR, size_t start, siz fm->generate(&fmout); - os=fmout.data[0]+((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1); + os=((fmout.data[0]*fmVol)/255)+((((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)/255); if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -1042,6 +1043,8 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); + ssgVol=flags.getInt("ssgVol",255); + fmVol=flags.getInt("fmVol",255); rate=fm->sample_rate(chipClock); for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index fc0b08de6..503b1a423 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -374,15 +374,17 @@ void DivPlatformYM2608::acquire_combo(short* bufL, short* bufR, size_t start, si os[0]>>=1; os[1]>>=1; + os[0]=(os[0]*fmVol)/255; + os[1]=(os[1]*fmVol)/255; // ymfm part fm->generate(&fmout); - os[0]+=fmout.data[0]+(fmout.data[2]>>1); + os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=fmout.data[1]+(fmout.data[2]>>1); + os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -439,11 +441,11 @@ void DivPlatformYM2608::acquire_ymfm(short* bufL, short* bufR, size_t start, siz fm->generate(&fmout); - os[0]=fmout.data[0]+(fmout.data[2]>>1); + os[0]=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=fmout.data[1]+(fmout.data[2]>>1); + os[1]=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -1539,6 +1541,8 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); + ssgVol=flags.getInt("ssgVol",255); + fmVol=flags.getInt("fmVol",255); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 9251a39f3..1aaeddef2 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -305,15 +305,17 @@ void DivPlatformYM2610::acquire_combo(short* bufL, short* bufR, size_t start, si os[0]>>=1; os[1]>>=1; + os[0]=(os[0]*fmVol)/255; + os[1]=(os[1]*fmVol)/255; // ymfm part fm->generate(&fmout); - os[0]+=fmout.data[0]+(fmout.data[2]>>1); + os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=fmout.data[1]+(fmout.data[2]>>1); + os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -372,11 +374,11 @@ void DivPlatformYM2610::acquire_ymfm(short* bufL, short* bufR, size_t start, siz fm->generate(&fmout); - os[0]=fmout.data[0]+(fmout.data[2]>>1); + os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=fmout.data[1]+(fmout.data[2]>>1); + os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 593ecfa0b..9d79c0be5 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -373,15 +373,17 @@ void DivPlatformYM2610B::acquire_combo(short* bufL, short* bufR, size_t start, s os[0]>>=1; os[1]>>=1; + os[0]=(os[0]*fmVol)/255; + os[1]=(os[1]*fmVol)/255; // ymfm part fm->generate(&fmout); - os[0]+=fmout.data[0]+(fmout.data[2]>>1); + os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=fmout.data[1]+(fmout.data[2]>>1); + os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -438,11 +440,11 @@ void DivPlatformYM2610B::acquire_ymfm(short* bufL, short* bufR, size_t start, si fm->generate(&fmout); - os[0]=fmout.data[0]+(fmout.data[2]>>1); + os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=fmout.data[1]+(fmout.data[2]>>1); + os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index ab1bd0528..45c330cd3 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -222,6 +222,8 @@ class DivPlatformYM2610Base: public DivPlatformOPN { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); + ssgVol=flags.getInt("ssgVol",255); + fmVol=flags.getInt("fmVol",255); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 8e41fa00c..363ed1add 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -472,6 +472,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); + int ssgVol=flags.getInt("ssgVol",255); + int fmVol=flags.getInt("fmVol",255); if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) { clockSel=0; @@ -491,11 +493,25 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } } + if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (ssgVol<0) ssgVol=0; + if (ssgVol>255) ssgVol=255; + altered=true; + } rightClickable + + if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,255)) { + if (fmVol<0) fmVol=0; + if (fmVol>255) fmVol=255; + altered=true; + } rightClickable + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); flags.set("noExtMacros",noExtMacros); flags.set("fbAllOps",fbAllOps); + flags.set("ssgVol",ssgVol); + flags.set("fmVol",fmVol); }); } break; @@ -854,6 +870,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); + int ssgVol=flags.getInt("ssgVol",255); + int fmVol=flags.getInt("fmVol",255); ImGui::Text("Clock rate:"); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { @@ -894,6 +912,18 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } + if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (ssgVol<0) ssgVol=0; + if (ssgVol>255) ssgVol=255; + altered=true; + } rightClickable + + if (CWSliderInt("FM Volume",&fmVol,0,255)) { + if (fmVol<0) fmVol=0; + if (fmVol>255) fmVol=255; + altered=true; + } rightClickable + if (type==DIV_SYSTEM_YM2203_EXT || type==DIV_SYSTEM_YM2203_CSM) { if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) { altered=true; @@ -909,6 +939,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo flags.set("prescale",prescale); flags.set("noExtMacros",noExtMacros); flags.set("fbAllOps",fbAllOps); + flags.set("ssgVol",ssgVol); + flags.set("fmVol",fmVol); }); } break; @@ -920,6 +952,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); + int ssgVol=flags.getInt("ssgVol",255); + int fmVol=flags.getInt("fmVol",255); ImGui::Text("Clock rate:"); if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) { @@ -944,6 +978,18 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } + if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (ssgVol<0) ssgVol=0; + if (ssgVol>255) ssgVol=255; + altered=true; + } rightClickable + + if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,255)) { + if (fmVol<0) fmVol=0; + if (fmVol>255) fmVol=255; + altered=true; + } rightClickable + if (type==DIV_SYSTEM_YM2608_EXT || type==DIV_SYSTEM_YM2608_CSM) { if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) { altered=true; @@ -959,6 +1005,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo flags.set("prescale",prescale); flags.set("noExtMacros",noExtMacros); flags.set("fbAllOps",fbAllOps); + flags.set("ssgVol",ssgVol); + flags.set("fmVol",fmVol); }); } break; From fcc5b6e5eb80523cc15d20480f7449d2de6d5ce6 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 14 Jan 2023 11:55:40 +0900 Subject: [PATCH 02/11] Replace divider to shift --- src/engine/platform/fmshared_OPN.h | 8 +++---- src/engine/platform/ym2203.cpp | 10 ++++----- src/engine/platform/ym2608.cpp | 16 ++++++------- src/engine/platform/ym2610.cpp | 12 +++++----- src/engine/platform/ym2610b.cpp | 12 +++++----- src/engine/platform/ym2610shared.h | 4 ++-- src/gui/sysConf.cpp | 36 +++++++++++++++--------------- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 427a231cf..387e7d61e 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -154,8 +154,8 @@ class DivPlatformOPN: public DivPlatformFMBase { unsigned int ayDiv; unsigned char csmChan; unsigned char lfoValue; - unsigned char ssgVol; - unsigned char fmVol; + unsigned short ssgVol; + unsigned short fmVol; bool extSys, useCombo, fbAllOps; DivConfig ayFlags; @@ -174,8 +174,8 @@ class DivPlatformOPN: public DivPlatformFMBase { ayDiv(a), csmChan(cc), lfoValue(0), - ssgVol(255), - fmVol(255), + ssgVol(256), + fmVol(256), extSys(isExtSys), useCombo(false), fbAllOps(false) {} diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index d9cccae2b..8f3059199 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -210,12 +210,12 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) { ); os&=~3; - os=(os*fmVol)/255; + os=(os*fmVol)>>8; // ymfm part fm->generate(&fmout); - os+=(((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)/255; + os+=(((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)>>8; if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -256,7 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os=((fmout.data[0]*fmVol)/255)+((((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)/255); + os=((fmout.data[0]*fmVol)>>8)+((((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)>>8); if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -1043,8 +1043,8 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",255); - fmVol=flags.getInt("fmVol",255); + ssgVol=flags.getInt("ssgVol",256); + fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index f5487fe68..ed6afe460 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -374,17 +374,17 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) { os[0]>>=1; os[1]>>=1; - os[0]=(os[0]*fmVol)/255; - os[1]=(os[1]*fmVol)/255; + os[0]=(os[0]*fmVol)>>8; + os[1]=(os[1]*fmVol)>>8; // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -441,11 +441,11 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -1552,8 +1552,8 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",255); - fmVol=flags.getInt("fmVol",255); + ssgVol=flags.getInt("ssgVol",256); + fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 99aa6929d..938e000a5 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -305,17 +305,17 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) { os[0]>>=1; os[1]>>=1; - os[0]=(os[0]*fmVol)/255; - os[1]=(os[1]*fmVol)/255; + os[0]=(os[0]*fmVol)>>8; + os[1]=(os[1]*fmVol)>>8; // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -374,11 +374,11 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 55a1dfe53..343232e74 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -373,17 +373,17 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) { os[0]>>=1; os[1]>>=1; - os[0]=(os[0]*fmVol)/255; - os[1]=(os[1]*fmVol)/255; + os[0]=(os[0]*fmVol)>>8; + os[1]=(os[1]*fmVol)>>8; // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -440,11 +440,11 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)/255)+(((fmout.data[2]>>1)*ssgVol)/255); + os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index ba1ab0306..89865b0a6 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -222,8 +222,8 @@ class DivPlatformYM2610Base: public DivPlatformOPN { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",255); - fmVol=flags.getInt("fmVol",255); + ssgVol=flags.getInt("ssgVol",256); + fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index c7e561a8f..a72342dee 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -472,8 +472,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",255); - int fmVol=flags.getInt("fmVol",255); + int ssgVol=flags.getInt("ssgVol",256); + int fmVol=flags.getInt("fmVol",256); if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) { clockSel=0; @@ -493,15 +493,15 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } } - if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (CWSliderInt("SSG Volume",&ssgVol,0,256)) { if (ssgVol<0) ssgVol=0; - if (ssgVol>255) ssgVol=255; + if (ssgVol>256) ssgVol=256; altered=true; } rightClickable - if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,255)) { + if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,256)) { if (fmVol<0) fmVol=0; - if (fmVol>255) fmVol=255; + if (fmVol>256) fmVol=256; altered=true; } rightClickable @@ -870,8 +870,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",255); - int fmVol=flags.getInt("fmVol",255); + int ssgVol=flags.getInt("ssgVol",256); + int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { @@ -912,15 +912,15 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } - if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (CWSliderInt("SSG Volume",&ssgVol,0,256)) { if (ssgVol<0) ssgVol=0; - if (ssgVol>255) ssgVol=255; + if (ssgVol>256) ssgVol=256; altered=true; } rightClickable - if (CWSliderInt("FM Volume",&fmVol,0,255)) { + if (CWSliderInt("FM Volume",&fmVol,0,256)) { if (fmVol<0) fmVol=0; - if (fmVol>255) fmVol=255; + if (fmVol>256) fmVol=256; altered=true; } rightClickable @@ -952,8 +952,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",255); - int fmVol=flags.getInt("fmVol",255); + int ssgVol=flags.getInt("ssgVol",256); + int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) { @@ -978,15 +978,15 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } - if (CWSliderInt("SSG Volume",&ssgVol,0,255)) { + if (CWSliderInt("SSG Volume",&ssgVol,0,256)) { if (ssgVol<0) ssgVol=0; - if (ssgVol>255) ssgVol=255; + if (ssgVol>256) ssgVol=256; altered=true; } rightClickable - if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,255)) { + if (CWSliderInt("FM/ADPCM Volume",&fmVol,0,256)) { if (fmVol<0) fmVol=0; - if (fmVol>255) fmVol=255; + if (fmVol>256) fmVol=256; altered=true; } rightClickable From 7e1acaf9d5d7f09b61373e9c5fc253eec9abac42 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 5 Feb 2023 12:46:23 +0900 Subject: [PATCH 03/11] Add define value for number of chip defines --- src/engine/defines.h | 1 + src/engine/engine.h | 10 +++++----- src/engine/sysDef.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/engine/defines.h b/src/engine/defines.h index 0f53bd777..9564f418d 100644 --- a/src/engine/defines.h +++ b/src/engine/defines.h @@ -24,6 +24,7 @@ #define DIV_MAX_CHIPS 32 #define DIV_MAX_CHANS 128 #define DIV_MAX_PATTERNS 256 +#define DIV_MAX_CHIP_DEFS 256 // in-pattern #define DIV_MAX_ROWS 256 diff --git a/src/engine/engine.h b/src/engine/engine.h index fc552cacc..89251a291 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -391,9 +391,9 @@ class DivEngine { std::vector midiOuts; std::vector cmdStream; std::vector possibleInsTypes; - static DivSysDef* sysDefs[256]; - static DivSystem sysFileMapFur[256]; - static DivSystem sysFileMapDMF[256]; + static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS]; + static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS]; + static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS]; struct SamplePreview { double rate; @@ -1162,11 +1162,11 @@ class DivEngine { memset(tremTable,0,128*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); - memset(sysDefs,0,256*sizeof(void*)); + memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*)); memset(walked,0,8192); memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*))); - for (int i=0; i<256; i++) { + for (int i=0; iid!=0) { sysFileMapFur[sysDefs[i]->id]=(DivSystem)i; From ac5986e44fab92230abee5f5df28242072636b7f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 02:56:39 -0500 Subject: [PATCH 04/11] dev139 - groove patterns! --- CMakeLists.txt | 1 + papers/format.md | 12 ++++ src/engine/engine.cpp | 26 +++---- src/engine/engine.h | 20 ++---- src/engine/fileOps.cpp | 134 ++++++++++++++++++++++++++++------ src/engine/playback.cpp | 34 +++++---- src/engine/song.h | 15 +++- src/gui/doAction.cpp | 6 ++ src/gui/grooves.cpp | 154 ++++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 34 +++++++-- src/gui/gui.h | 11 ++- src/gui/guiConst.cpp | 1 + src/gui/speed.cpp | 133 +++++++++++++++++++++++++++------- 13 files changed, 483 insertions(+), 98 deletions(-) create mode 100644 src/gui/grooves.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 458cd448e..f994fe8bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -596,6 +596,7 @@ src/gui/editControls.cpp src/gui/effectList.cpp src/gui/findReplace.cpp src/gui/gradient.cpp +src/gui/grooves.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp diff --git a/papers/format.md b/papers/format.md index 389b6e61e..3f93f451d 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 139: Furnace dev139 - 138: Furnace dev138 - 137: Furnace dev137 - 136: Furnace dev136 @@ -403,6 +404,14 @@ size | description --- | **a couple more compat flags** (>=138) 1 | broken portamento during legato 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 @@ -472,6 +481,9 @@ size | description | - a list of channelCount C strings S?? | channel short names | - 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 diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 17926e0ec..34e7c6b04 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0x08: return "08xy: Set panning (x: left; y: right)"; case 0x09: - return "09xx: Set speed 1"; + return "09xx: Set groove pattern (speed 1 if no grooves exist)"; case 0x0a: return "0Axy: Volume slide (0y: down; x0: up)"; case 0x0b: @@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0x0d: return "0Dxx: Jump to next pattern"; case 0x0f: - return "0Fxx: Set speed 2"; + return "0Fxx: Set speed (speed 2 if no grooves exist)"; case 0x80: return "80xx: Set panning (00: left; 80: center; FF: right)"; case 0x81: @@ -1959,14 +1959,12 @@ String DivEngine::getPlaybackDebugInfo() { "cmdsPerSecond: %d\n" "globalPitch: %d\n" "extValue: %d\n" - "speed1: %d\n" - "speed2: %d\n" "tempoAccum: %d\n" "totalProcessed: %d\n" "bufferPos: %d\n", curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift, 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; } endOfSong=false; - speedAB=false; + // whaaaaa? + curSpeed=0; playing=true; skipping=true; memset(walked,0,8192); @@ -2439,15 +2438,14 @@ void DivEngine::reset() { } extValue=0; extValuePresent=0; - speed1=curSubSong->speed1; - speed2=curSubSong->speed2; + speeds=curSubSong->speeds; firstTick=false; shallStop=false; shallStopSched=false; pendingMetroTick=0; elapsedBars=0; elapsedBeats=0; - nextSpeed=speed1; + nextSpeed=speeds.val[0]; divider=60; if (curSubSong->customTempo) { divider=curSubSong->hz; @@ -2647,12 +2645,8 @@ size_t DivEngine::getCurrentSubSong() { return curSubSongIndex; } -unsigned char DivEngine::getSpeed1() { - return speed1; -} - -unsigned char DivEngine::getSpeed2() { - return speed2; +const DivGroovePattern& DivEngine::getSpeeds() { + return speeds; } float DivEngine::getHz() { @@ -4234,7 +4228,7 @@ void DivEngine::quitDispatch() { clockDrift=0; chans=0; playing=false; - speedAB=false; + curSpeed=0; endOfSong=false; ticks=0; tempoAccum=0; diff --git a/src/engine/engine.h b/src/engine/engine.h index fc552cacc..b3604baa3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev138" -#define DIV_ENGINE_VERSION 138 +#define DIV_VERSION "dev139" +#define DIV_ENGINE_VERSION 139 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -337,7 +337,6 @@ class DivEngine { bool playing; bool freelance; bool shallStop, shallStopSched; - bool speedAB; bool endOfSong; bool consoleMode; bool extValuePresent; @@ -359,7 +358,7 @@ class DivEngine { bool midiOutClock; int midiOutMode; 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 bufferPos; double divider; @@ -368,7 +367,7 @@ class DivEngine { int stepPlay; int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch; unsigned char extValue, pendingMetroTick; - unsigned char speed1, speed2; + DivGroovePattern speeds; short tempoAccum; DivStatusView view; DivHaltPositions haltOn; @@ -730,11 +729,8 @@ class DivEngine { // get current subsong size_t getCurrentSubSong(); - // get speed 1 - unsigned char getSpeed1(); - - // get speed 2 - unsigned char getSpeed2(); + // get speeds + const DivGroovePattern& getSpeeds(); // get Hz float getHz(); @@ -1065,7 +1061,6 @@ class DivEngine { freelance(false), shallStop(false), shallStopSched(false), - speedAB(false), endOfSong(false), consoleMode(false), extValuePresent(false), @@ -1099,6 +1094,7 @@ class DivEngine { nextSpeed(3), elapsedBars(0), elapsedBeats(0), + curSpeed(0), curSubSongIndex(0), bufferPos(0), divider(60), @@ -1116,8 +1112,6 @@ class DivEngine { globalPitch(0), extValue(0), pendingMetroTick(0), - speed1(3), - speed2(3), tempoAccum(0), view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index e2629dfa0..aac777f34 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } ds.version=(unsigned char)reader.readC(); 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!"); lastError="this version is not supported by Furnace yet"; delete[] file; @@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } 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) { - ds.subsong[0]->speed2=reader.readC(); + ds.subsong[0]->speeds.val[1]=reader.readC(); ds.subsong[0]->pal=reader.readC(); ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50; ds.subsong[0]->customTempo=reader.readC(); } else { - ds.subsong[0]->speed2=ds.subsong[0]->speed1; + ds.subsong[0]->speeds.len=1; } if (ds.version>0x0a) { String hz=reader.readString(3); @@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { for (int i=0; idepth=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 (cutEnd0) { if (ds.version>0x08) { if (ds.version<0x0b) { @@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { data=new short[length]; 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 // convert to big-endian @@ -1742,8 +1781,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { reader.readI(); subSong->timeBase=reader.readC(); - subSong->speed1=reader.readC(); - subSong->speed2=reader.readC(); + subSong->speeds.len=2; + subSong->speeds.val[0]=reader.readC(); + subSong->speeds.val[1]=reader.readC(); subSong->arpLen=reader.readC(); subSong->hz=reader.readF(); 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=119) { logD("reading chip flags..."); @@ -2289,8 +2348,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { subSong=ds.subsong[i+1]; subSong->timeBase=reader.readC(); - subSong->speed1=reader.readC(); - subSong->speed2=reader.readC(); + subSong->speeds.len=2; + subSong->speeds.val[0]=reader.readC(); + subSong->speeds.val[1]=reader.readC(); subSong->arpLen=reader.readC(); subSong->hz=reader.readF(); subSong->pal=(subSong->hz>=53); @@ -2338,6 +2398,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { for (int i=0; ichanShortName[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") { writeFxCol(0xf0,fxVal); } else { - writeFxCol(0x09,fxVal); writeFxCol(0x0f,fxVal); } break; @@ -3435,8 +3501,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { ds.subsong[0]->pal=true; ds.subsong[0]->customTempo=true; ds.subsong[0]->pat[3].effectCols=3; - ds.subsong[0]->speed1=3; - ds.subsong[0]->speed2=3; + ds.subsong[0]->speeds.val[0]=3; + ds.subsong[0]->speeds.len=1; int lastIns[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; DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true); 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][8]=0x0f; - p->data[0][9]=seq[i].speed; } bool ignoreNext=false; @@ -4343,8 +4407,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeI(0); w->writeC(subSong->timeBase); - w->writeC(subSong->speed1); - w->writeC(subSong->speed2); + // these are for compatibility + 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->writeF(subSong->hz); w->writeS(subSong->patLen); @@ -4531,6 +4596,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { 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(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -4545,8 +4625,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeI(0); w->writeC(subSong->timeBase); - w->writeC(subSong->speed1); - 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->writeF(subSong->hz); w->writeS(subSong->patLen); @@ -4585,6 +4665,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { 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(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -4840,8 +4926,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { w->writeC(curSubSong->hilightB); w->writeC(curSubSong->timeBase); - w->writeC(curSubSong->speed1); - w->writeC(curSubSong->speed2); + w->writeC(curSubSong->speeds.val[0]); + w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]); w->writeC(curSubSong->pal); w->writeC(curSubSong->customTempo); char customHz[4]; @@ -4865,6 +4951,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { 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) { addWarning(".dmf format does not support virtual tempo"); } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index abcb8dc56..823b2f8a9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -400,11 +400,22 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==-1) effectVal=0; switch (effect) { - case 0x09: // speed 1 - if (effectVal>0) speed1=effectVal; + case 0x09: // select groove pattern/speed 1 + 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; - case 0x0f: // speed 2 - if (effectVal>0) speed2=effectVal; + case 0x0f: // speed 1/speed 2 + if (speeds.len==2 && song.grooves.empty()) { + if (effectVal>0) speeds.val[1]=effectVal; + } else { + if (effectVal>0) speeds.val[0]=effectVal; + } break; case 0x0b: // change order if (changeOrd==-1 || song.jumpTreatment==0) { @@ -1071,6 +1082,9 @@ void DivEngine::nextRow() { } 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) { ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1); nextSpeed=(curRow&1)?speed1:speed2; @@ -1079,14 +1093,10 @@ void DivEngine::nextRow() { nextSpeed=(curRow&1)?speed2:speed1; } } else { - if (speedAB) { - ticks=speed2*(curSubSong->timeBase+1); - nextSpeed=speed1; - } else { - ticks=speed1*(curSubSong->timeBase+1); - nextSpeed=speed2; - } - speedAB=!speedAB; + ticks=speeds.val[curSpeed]*(curSubSong->timeBase+1); + curSpeed++; + if (curSpeed>=speeds.len) curSpeed=0; + nextSpeed=speeds.val[curSpeed]; } // post row details diff --git a/src/engine/song.h b/src/engine/song.h index 6aaec7c12..72fbea0ef 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -127,10 +127,20 @@ enum DivSystem { DIV_SYSTEM_YM2608_CSM }; +struct DivGroovePattern { + unsigned char val[16]; + unsigned char len; + DivGroovePattern(): + len(1) { + memset(val,6,16); + } +}; + struct DivSubSong { String name, notes; unsigned char hilightA, hilightB; - unsigned char timeBase, speed1, speed2, arpLen; + unsigned char timeBase, arpLen; + DivGroovePattern speeds; short virtualTempoN, virtualTempoD; bool pal; bool customTempo; @@ -153,8 +163,6 @@ struct DivSubSong { hilightA(4), hilightB(16), timeBase(0), - speed1(6), - speed2(6), arpLen(1), virtualTempoN(150), virtualTempoD(150), @@ -338,6 +346,7 @@ struct DivSong { std::vector subsong; std::vector patchbay; + std::vector grooves; DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound; DivWavetable nullWave; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 60511f09e..83ab4e37e 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -265,6 +265,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_FIND: nextWindow=GUI_WINDOW_FIND; break; + case GUI_ACTION_WINDOW_GROOVES: + nextWindow=GUI_WINDOW_GROOVES; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -358,6 +361,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_FIND: findOpen=false; break; + case GUI_WINDOW_GROOVES: + groovesOpen=false; + break; default: break; } diff --git a/src/gui/grooves.cpp b/src/gui/grooves.cpp new file mode 100644 index 000000000..560a09e68 --- /dev/null +++ b/src/gui/grooves.cpp @@ -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 +#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; j0) { + 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(); +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 67df8c87e..5b20c47eb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -988,15 +988,19 @@ void FurnaceGUI::prepareLayout() { 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; if (hl<=0.0f) hl=4.0f; float timeBase=e->curSubSong->timeBase+1; - float speedSum=s1+s2; + float speedSum=0; + for (int i=0; iisPlaying()) { int totalTicks=e->getTotalTicks(); 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 { bool hasInfo=false; String info; @@ -3917,6 +3936,7 @@ bool FurnaceGUI::loop() { drawPattern(); drawEditControls(); drawSpeed(); + drawGrooves(); drawSongInfo(); drawOrders(); drawSampleList(); @@ -5248,6 +5268,7 @@ bool FurnaceGUI::init() { sysManagerOpen=e->getConfBool("sysManagerOpen",false); clockOpen=e->getConfBool("clockOpen",false); speedOpen=e->getConfBool("speedOpen",true); + groovesOpen=e->getConfBool("groovesOpen",false); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); effectListOpen=e->getConfBool("effectListOpen",false); @@ -5623,6 +5644,7 @@ void FurnaceGUI::commitState() { e->setConf("sysManagerOpen",sysManagerOpen); e->setConf("clockOpen",clockOpen); e->setConf("speedOpen",speedOpen); + e->setConf("groovesOpen",groovesOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); e->setConf("effectListOpen",effectListOpen); @@ -5835,6 +5857,7 @@ FurnaceGUI::FurnaceGUI(): dragDestinationY(0), oldBeat(-1), oldBar(-1), + curGroove(-1), soloTimeout(0.0f), exportFadeOut(5.0), editControlsOpen(true), @@ -5870,6 +5893,7 @@ FurnaceGUI::FurnaceGUI(): sysManagerOpen(false), clockOpen(false), speedOpen(true), + groovesOpen(false), clockShowReal(true), clockShowRow(true), clockShowBeat(true), @@ -5898,10 +5922,12 @@ FurnaceGUI::FurnaceGUI(): latchNibble(false), nonLatchNibble(false), keepLoopAlive(false), + keepGrooveAlive(false), orderScrollLocked(false), orderScrollTolerance(false), dragMobileMenu(false), dragMobileEditButton(false), + wantGrooveListFocus(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING), diff --git a/src/gui/gui.h b/src/gui/gui.h index 440581a7b..ba8dc933f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -321,6 +321,7 @@ enum FurnaceGUIWindows { GUI_WINDOW_SUBSONGS, GUI_WINDOW_FIND, GUI_WINDOW_CLOCK, + GUI_WINDOW_GROOVES, GUI_WINDOW_SPOILER }; @@ -466,6 +467,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_SUBSONGS, GUI_ACTION_WINDOW_FIND, GUI_ACTION_WINDOW_CLOCK, + GUI_ACTION_WINDOW_GROOVES, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -1097,7 +1099,7 @@ class FurnaceGUI { String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds; String workingDirLayout, workingDirROM, workingDirTest; String mmlString[32]; - String mmlStringW, mmlStringSNES; + String mmlStringW, mmlStringSNES, grooveString, grooveListString; std::vector sysSearchResults; std::vector newSongSearchResults; @@ -1456,6 +1458,7 @@ class FurnaceGUI { 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 wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar; + int curGroove; float soloTimeout; double exportFadeOut; @@ -1465,6 +1468,7 @@ class FurnaceGUI { bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen; + bool groovesOpen; bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime; float clockMetroTick[16]; @@ -1472,7 +1476,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; - bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton; + bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; std::atomic curWindowThreadSafe; 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 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); @@ -1821,6 +1825,7 @@ class FurnaceGUI { void drawEditControls(); void drawSongInfo(bool asChild=false); void drawSpeed(bool asChild=false); + void drawGrooves(); void drawOrders(); void drawPattern(); void drawInsList(bool asChild=false); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 2ffdbbbc3..0445704ed 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -520,6 +520,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SUBSONGS", "Subsongs", 0), D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), D("WINDOW_CLOCK", "Clock", 0), + D("WINDOW_GROOVES", "Grooves", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp index 1d09331b4..796f0ec19 100644 --- a/src/gui/speed.cpp +++ b/src/gui/speed.cpp @@ -31,19 +31,26 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (!speedOpen && !asChild) return; bool began=asChild?ImGui::BeginChild("Speed"):ImGui::Begin("Speed",&speedOpen,globalWinFlags); 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("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { + if (ImGui::SmallButton(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { tempoView=!tempoView; } + if (ImGui::IsItemHovered()) { + if (tempoView) { + ImGui::SetTooltip("click to display tick rate"); + } else { + ImGui::SetTooltip("click to display base tempo"); + } + } ImGui::TableNextColumn(); 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; if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED if (tempoView) setHz/=2.5; @@ -52,40 +59,112 @@ void FurnaceGUI::drawSpeed(bool asChild) { e->setSongRate(setHz,setHz<52); } if (tempoView) { - ImGui::TableNextColumn(); + ImGui::SameLine(); ImGui::Text("= %gHz",e->curSubSong->hz); } else { if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { - ImGui::TableNextColumn(); + ImGui::SameLine(); ImGui::Text("PAL"); } if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { - ImGui::TableNextColumn(); + ImGui::SameLine(); ImGui::Text("NTSC"); } } ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("Speed"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed1<1) e->curSubSong->speed1=1; - if (e->isPlaying()) play(); + if (keepGrooveAlive || e->curSubSong->speeds.len>2) { + if (ImGui::SmallButton("Groove")) { + e->lockEngine([this]() { + e->curSubSong->speeds.len=1; + }); + 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::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed2<1) e->curSubSong->speed2=1; - if (e->isPlaying()) play(); + if (keepGrooveAlive || e->curSubSong->speeds.len>2) { + int intVersion[256]; + unsigned char intVersionLen=e->curSubSong->speeds.len; + 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::TableNextColumn(); ImGui::Text("Virtual Tempo"); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SetNextItemWidth(halfAvail); 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; @@ -93,8 +172,8 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Numerator"); } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SameLine(); + ImGui::SetNextItemWidth(halfAvail); 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; @@ -105,28 +184,28 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("TimeBase"); + ImGui::Text("Divider"); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SetNextItemWidth(halfAvail); unsigned char realTB=e->curSubSong->timeBase+1; if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED if (realTB<1) realTB=1; if (realTB>16) realTB=16; e->curSubSong->timeBase=realTB-1; } - ImGui::TableNextColumn(); - ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); + ImGui::SameLine(); + ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Highlight"); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SetNextItemWidth(halfAvail); if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { MARK_MODIFIED; } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SameLine(); + ImGui::SetNextItemWidth(halfAvail); if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { MARK_MODIFIED; } From 1a24fbe35f022883c709f13237d6f69f8003b0c5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 03:50:32 -0500 Subject: [PATCH 05/11] GUI: add ability to change sub-song comment --- src/gui/subSongs.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index 9801ab57c..b6e49e5b2 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -106,6 +106,12 @@ void FurnaceGUI::drawSubSongs() { if (ImGui::InputText("##SubSongName",&e->curSubSong->name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } + + if (ImGui::GetContentRegionAvail().y>(10.0f*dpiScale)) { + if (ImGui::InputTextMultiline("##SubSongNotes",&e->curSubSong->notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo)) { + MARK_MODIFIED; + } + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS; ImGui::End(); From 3cacbe44344b92a9e4f3fb148ea4c18f0032163b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 03:54:30 -0500 Subject: [PATCH 06/11] TIA: fix non-working arp macro --- src/engine/platform/tia.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index d46a2b67b..1e6db02ca 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -135,22 +135,27 @@ void DivPlatformTIA::tick(bool sysTick) { int bf=chan[i].baseFreq; if (!parent->song.oldArpStrategy) { if (!chan[i].fixedArp) { - bf+=chan[i].arpOff; + bf+=chan[i].arpOff<<8; } } - chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch+chan[i].pitch2); - if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { - if (bf<39*256) { - rWrite(0x15+i,6); - chan[i].freq=dealWithFreq(6,bf,chan[i].pitch+chan[i].pitch2); - } else if (bf<59*256) { - rWrite(0x15+i,12); - chan[i].freq=dealWithFreq(12,bf,chan[i].pitch+chan[i].pitch2); - } else { - rWrite(0x15+i,chan[i].shape); + if (chan[i].fixedArp) { + chan[i].freq=chan[i].baseNoteOverride&31; + } else { + chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch+chan[i].pitch2); + if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { + if (bf<39*256) { + rWrite(0x15+i,6); + chan[i].freq=dealWithFreq(6,bf,chan[i].pitch+chan[i].pitch2); + } else if (bf<59*256) { + rWrite(0x15+i,12); + chan[i].freq=dealWithFreq(12,bf,chan[i].pitch+chan[i].pitch2); + } else { + rWrite(0x15+i,chan[i].shape); + } } + if (chan[i].freq>31) chan[i].freq=31; } - if (chan[i].freq>31) chan[i].freq=31; + if (chan[i].keyOff) { rWrite(0x19+i,0); } From 1fa45ec57d9a88475d55c2b571fa35356d6ad658 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 04:09:47 -0500 Subject: [PATCH 07/11] YM2612: why --- src/engine/platform/genesisext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 5c9d986a4..cc763d098 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -589,6 +589,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); + opChan[i].freqChanged=false; } writeMask|=(unsigned char)(opChan[i].mask && opChan[i].active)<<(4+i); if (opChan[i].keyOn) { From fda333a76bb1ca0bb4474f3fe8bbe2b55bdfae39 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 04:57:09 -0500 Subject: [PATCH 08/11] default SSG vol is now 128 to eliminate one shift --- src/engine/platform/fmshared_OPN.h | 2 +- src/engine/platform/ym2203.cpp | 6 +++--- src/engine/platform/ym2608.cpp | 10 +++++----- src/engine/platform/ym2610.cpp | 8 ++++---- src/engine/platform/ym2610b.cpp | 8 ++++---- src/engine/platform/ym2610shared.h | 2 +- src/gui/sysConf.cpp | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 387e7d61e..a74adcd2c 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -174,7 +174,7 @@ class DivPlatformOPN: public DivPlatformFMBase { ayDiv(a), csmChan(cc), lfoValue(0), - ssgVol(256), + ssgVol(128), fmVol(256), extSys(isExtSys), useCombo(false), diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 8f3059199..c8b5ad900 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -215,7 +215,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) { // ymfm part fm->generate(&fmout); - os+=(((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)>>8; + os+=((fmout.data[1]+fmout.data[2]+fmout.data[3])*ssgVol)>>8; if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -256,7 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os=((fmout.data[0]*fmVol)>>8)+((((fmout.data[1]+fmout.data[2]+fmout.data[3])>>1)*ssgVol)>>8); + os=((fmout.data[0]*fmVol)>>8)+(((fmout.data[1]+fmout.data[2]+fmout.data[3])*ssgVol)>>8); if (os<-32768) os=-32768; if (os>32767) os=32767; @@ -1043,7 +1043,7 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",256); + ssgVol=flags.getInt("ssgVol",128); fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<6; i++) { diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index ed6afe460..2fa133cf9 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -380,11 +380,11 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) { // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]+=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]+=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -441,11 +441,11 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -1552,7 +1552,7 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",256); + ssgVol=flags.getInt("ssgVol",128); fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 938e000a5..eb398d179 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -311,11 +311,11 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) { // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]+=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]+=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -374,11 +374,11 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]+=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]+=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 343232e74..8d4b16ad7 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -379,11 +379,11 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) { // ymfm part fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]+=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]+=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; @@ -440,11 +440,11 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) { fm->generate(&fmout); - os[0]+=((fmout.data[0]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[0]+=((fmout.data[0]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; - os[1]+=((fmout.data[1]*fmVol)>>8)+(((fmout.data[2]>>1)*ssgVol)>>8); + os[1]+=((fmout.data[1]*fmVol)>>8)+((fmout.data[2]*ssgVol)>>8); if (os[1]<-32768) os[1]=-32768; if (os[1]>32767) os[1]=32767; diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 89865b0a6..dfcf571da 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -222,7 +222,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN { CHECK_CUSTOM_CLOCK; noExtMacros=flags.getBool("noExtMacros",false); fbAllOps=flags.getBool("fbAllOps",false); - ssgVol=flags.getInt("ssgVol",256); + ssgVol=flags.getInt("ssgVol",128); fmVol=flags.getInt("fmVol",256); rate=fm->sample_rate(chipClock); for (int i=0; i<16; i++) { diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index a72342dee..1229b0db6 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -472,7 +472,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",256); + int ssgVol=flags.getInt("ssgVol",128); int fmVol=flags.getInt("fmVol",256); if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) { @@ -870,7 +870,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",256); + int ssgVol=flags.getInt("ssgVol",128); int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); @@ -952,7 +952,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int prescale=flags.getInt("prescale",0); bool noExtMacros=flags.getBool("noExtMacros",false); bool fbAllOps=flags.getBool("fbAllOps",false); - int ssgVol=flags.getInt("ssgVol",256); + int ssgVol=flags.getInt("ssgVol",128); int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); From c665326b2dc00de1fab17957b8b565e73d20dfe1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 15:24:11 -0500 Subject: [PATCH 09/11] GUI: add option to select render backend --- src/gui/gui.cpp | 22 ++++++++++++++++++++++ src/gui/gui.h | 4 +++- src/gui/settings.cpp | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5b20c47eb..5b0386fb4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5488,10 +5488,32 @@ bool FurnaceGUI::init() { } #endif + int numDrivers=SDL_GetNumRenderDrivers(); + if (numDrivers<0) { + logW("could not list render drivers! %s",SDL_GetError()); + } else { + SDL_RendererInfo ri; + for (int i=0; isetConf("renderDriver",""); + e->saveConf(); + lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace."); + } return false; } diff --git a/src/gui/gui.h b/src/gui/gui.h index ba8dc933f..381b3b427 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1105,7 +1105,7 @@ class FurnaceGUI { std::vector newSongSearchResults; std::deque recentFile; std::vector makeInsTypeList; - + std::vector availRenderDrivers; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; bool vgmExportDirectStream, displayInsTypeList; @@ -1307,6 +1307,7 @@ class FurnaceGUI { String midiInDevice; String midiOutDevice; String c163Name; + String renderDriver; String initialSysName; String noteOffLabel; String noteRelLabel; @@ -1443,6 +1444,7 @@ class FurnaceGUI { midiInDevice(""), midiOutDevice(""), c163Name(""), + renderDriver(""), initialSysName("Sega Genesis/Mega Drive"), noteOffLabel("OFF"), noteRelLabel("==="), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 50a36a9e8..70a435fc5 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1207,6 +1207,18 @@ void FurnaceGUI::drawSettings() { ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; if (ImGui::BeginChild("SettingsView",settingsViewSize)) { + if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) { + if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) { + settings.renderDriver=""; + } + for (String& i: availRenderDrivers) { + if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) { + settings.renderDriver=i; + } + } + ImGui::EndCombo(); + } + bool dpiScaleAuto=(settings.dpiScale<0.5f); if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) { if (dpiScaleAuto) { @@ -2447,6 +2459,7 @@ void FurnaceGUI::syncSettings() { settings.midiInDevice=e->getConfString("midiInDevice",""); settings.midiOutDevice=e->getConfString("midiOutDevice",""); settings.c163Name=e->getConfString("c163Name",DIV_C163_DEFAULT_NAME); + settings.renderDriver=e->getConfString("renderDriver",""); settings.audioQuality=e->getConfInt("audioQuality",0); settings.audioBufSize=e->getConfInt("audioBufSize",1024); settings.audioRate=e->getConfInt("audioRate",44100); @@ -2762,6 +2775,7 @@ void FurnaceGUI::commitSettings() { e->setConf("midiInDevice",settings.midiInDevice); e->setConf("midiOutDevice",settings.midiOutDevice); e->setConf("c163Name",settings.c163Name); + e->setConf("renderDriver",settings.renderDriver); e->setConf("audioQuality",settings.audioQuality); e->setConf("audioBufSize",settings.audioBufSize); e->setConf("audioRate",settings.audioRate); From 26739bba1167d875d9479a178ebed09ae702332d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 5 Feb 2023 17:42:48 -0500 Subject: [PATCH 10/11] YM2612: fix CSM on ymfm finally --- src/engine/platform/genesis.cpp | 19 ++++++++++++++----- src/engine/platform/genesis.h | 5 +++-- src/engine/platform/sound/ymfm/ymfm_fm.ipp | 6 ++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 239ad5f7c..05d3c08be 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -30,17 +30,26 @@ void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) { if (tnum==1) { - countB=duration_in_clocks; + setB=duration_in_clocks; } else if (tnum==0) { - countA=duration_in_clocks; + setA=duration_in_clocks; } - //logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks); } void DivYM2612Interface::clock() { - if (countA>=0) { + if (setA>=0) { countA-=144; - if (countA<0) m_engine->engine_timer_expired(0); + if (countA<0) { + m_engine->engine_timer_expired(0); + countA+=setA; + } + } + if (setB>=0) { + countB-=144; + if (countB<0) { + m_engine->engine_timer_expired(1); + countB+=setB; + } } } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 37449eb91..87b37e841 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -25,6 +25,7 @@ class DivYM2612Interface: public ymfm::ymfm_interface { + int setA, setB; int countA, countB; public: @@ -32,8 +33,8 @@ class DivYM2612Interface: public ymfm::ymfm_interface { void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks); DivYM2612Interface(): ymfm::ymfm_interface(), - countA(-1), - countB(-1) {} + countA(0), + countB(0) {} }; class DivPlatformGenesis: public DivPlatformOPN { diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 4fdff8a38..81b351fe9 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -431,6 +431,12 @@ bool fm_operator::prepare() // clock the key state clock_keystate(uint32_t(m_keyon_live != 0)); + if (m_keyon_live & (1< Date: Sun, 5 Feb 2023 18:50:17 -0500 Subject: [PATCH 11/11] GUI: highlight cur display & play order separately issue #932 --- src/gui/gui.h | 1 + src/gui/guiConst.cpp | 1 + src/gui/orders.cpp | 11 +++++++++++ src/gui/settings.cpp | 3 ++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 381b3b427..ca4333343 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -113,6 +113,7 @@ enum FurnaceGUIColors { GUI_COLOR_ORDER_ROW_INDEX, GUI_COLOR_ORDER_ACTIVE, + GUI_COLOR_ORDER_SELECTED, GUI_COLOR_ORDER_SIMILAR, GUI_COLOR_ORDER_INACTIVE, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 0445704ed..80a76c43d 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -742,6 +742,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_ORDER_ROW_INDEX,"",ImVec4(0.5f,0.8f,1.0f,1.0f)), D(GUI_COLOR_ORDER_ACTIVE,"",ImVec4(0.4f,0.7f,1.0f,0.25f)), + D(GUI_COLOR_ORDER_SELECTED,"",ImVec4(0.6f,0.8f,1.0f,0.75f)), D(GUI_COLOR_ORDER_SIMILAR,"",ImVec4(0.5f,1.0f,1.0f,1.0f)), D(GUI_COLOR_ORDER_INACTIVE,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 914217787..16a9c1bbb 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -19,8 +19,10 @@ #include "gui.h" #include +#include #include "IconsFontAwesome4.h" #include "imgui_internal.h" +#include "../ta-log.h" void FurnaceGUI::drawMobileOrderSel() { if (!portrait) return; @@ -129,6 +131,7 @@ void FurnaceGUI::drawOrders() { } } ImGui::TableNextRow(0,lineHeight); + ImVec2 ra=ImGui::GetContentRegionAvail(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]); for (int i=0; igetTotalChannelCount(); i++) { @@ -141,6 +144,14 @@ void FurnaceGUI::drawOrders() { ImGui::TableNextRow(0,lineHeight); if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE])); ImGui::TableNextColumn(); + if ((!followPattern && curOrder==i) || (followPattern && oldOrder1==i)) { + // draw a border + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImVec2 rBegin=ImGui::GetCursorScreenPos(); + rBegin.y-=ImGui::GetStyle().CellPadding.y; + ImVec2 rEnd=ImVec2(rBegin.x+ra.x,rBegin.y+lineHeight); + dl->AddRect(rBegin,rEnd,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_SELECTED]),2.0f*dpiScale); + } ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_ORDER_ROW_INDEX]); bool highlightLoop=(i>=loopOrder && i<=loopEnd); if (highlightLoop) ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(uiColors[GUI_COLOR_SONG_LOOP])); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 70a435fc5..da803e923 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1811,7 +1811,8 @@ void FurnaceGUI::drawSettings() { } if (ImGui::TreeNode("Orders")) { UI_COLOR_CONFIG(GUI_COLOR_ORDER_ROW_INDEX,"Order number"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_ACTIVE,"Current order background"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_ACTIVE,"Playing order background"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_SELECTED,"Selected order"); UI_COLOR_CONFIG(GUI_COLOR_ORDER_SIMILAR,"Similar patterns"); UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); ImGui::TreePop();