diff --git a/CMakeLists.txt b/CMakeLists.txt index fbce94b6f..4ececc810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -614,6 +614,7 @@ src/gui/scaling.cpp src/gui/settings.cpp src/gui/songInfo.cpp src/gui/songNotes.cpp +src/gui/speed.cpp src/gui/spoiler.cpp src/gui/stats.cpp src/gui/subSongs.cpp diff --git a/demos/arcade/Salamander_Starfield.fur b/demos/arcade/Salamander_Starfield.fur new file mode 100644 index 000000000..be2690a5e Binary files /dev/null and b/demos/arcade/Salamander_Starfield.fur differ diff --git a/demos/arcade/iji_tor_SegaPCM.fur b/demos/arcade/iji_tor_SegaPCM.fur deleted file mode 100644 index 84a6ac5b3..000000000 Binary files a/demos/arcade/iji_tor_SegaPCM.fur and /dev/null differ diff --git a/demos/arcade/iji_tor_segaxboard.fur b/demos/arcade/iji_tor_segaxboard.fur new file mode 100644 index 000000000..602382bc9 Binary files /dev/null and b/demos/arcade/iji_tor_segaxboard.fur differ diff --git a/demos/genesis/Plok_Beach.fur b/demos/genesis/Plok_Beach.fur new file mode 100644 index 000000000..3c2fd8288 Binary files /dev/null and b/demos/genesis/Plok_Beach.fur differ diff --git a/demos/misc/Wicked_Express_X68000.fur b/demos/misc/Wicked_Express_X68000.fur new file mode 100644 index 000000000..b5ef6bdc0 Binary files /dev/null and b/demos/misc/Wicked_Express_X68000.fur differ diff --git a/demos/msx/Gyruss_Stage_2.fur b/demos/msx/Gyruss_Stage_2.fur new file mode 100644 index 000000000..9b901f0e1 Binary files /dev/null and b/demos/msx/Gyruss_Stage_2.fur differ diff --git a/demos/opl/e3m2_opl3.fur b/demos/opl/e3m2_opl3.fur new file mode 100644 index 000000000..63d4751ec Binary files /dev/null and b/demos/opl/e3m2_opl3.fur differ diff --git a/demos/specs2/Tim_Follin.fur b/demos/specs2/Tim_Follin.fur new file mode 100644 index 000000000..6eade6c27 Binary files /dev/null and b/demos/specs2/Tim_Follin.fur differ diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp index 464ce5bc9..13eb70838 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp @@ -8,11 +8,11 @@ #include "k005289.hpp" -void k005289_core::tick() +void k005289_core::tick(const unsigned int cycles) { for (timer_t &elem : m_timer) { - elem.tick(); + elem.tick(cycles); } } @@ -24,12 +24,12 @@ void k005289_core::reset() } } -void k005289_core::timer_t::tick() -{ - if (bitfield(++m_counter, 0, 12) == 0) +void k005289_core::timer_t::tick(const unsigned int cycles) { + m_counter-=cycles; + while (m_counter < 0) { m_addr = bitfield(m_addr + 1, 0, 5); - m_counter = m_freq; + m_counter += 0x1000-(m_freq&0xfff); } } diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp index 2c2b0715a..981af8897 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp @@ -32,7 +32,7 @@ class k005289_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const unsigned int cycles=1); // accessors // Replace current frequency to lastest loaded pitch @@ -63,7 +63,7 @@ class k005289_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const unsigned int cycles); // accessors // TG1/2 pin diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md index 2a66bd8cb..6ce9914d5 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md @@ -1,5 +1,9 @@ # Konami SCC +## modified + +the emulation core has been modified for optimization. + ## Summary - 5 voice wavetable diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp index 07cbb60e8..8c88699b0 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp @@ -10,17 +10,17 @@ #include "scc.hpp" // shared SCC features -void scc_core::tick() +void scc_core::tick(const int cycles) { m_out = 0; for (auto &elem : m_voice) { - elem.tick(); + elem.tick(cycles); m_out += elem.out(); } } -void scc_core::voice_t::tick() +void scc_core::voice_t::tick(const int cycles) { if (m_pitch >= 9) // or voice is halted { @@ -28,23 +28,27 @@ void scc_core::voice_t::tick() const u16 temp = m_counter; if (m_host.m_test.freq_4bit()) // 4 bit frequency mode { - m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); - m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - cycles, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - cycles, 0, 4) << 8); } else { - m_counter = bitfield(m_counter - 1, 0, 12); + m_counter = bitfield(m_counter - cycles, 0, 12); } // handle counter carry - const bool carry = m_host.m_test.freq_8bit() + const bool carry = (tempm_pitch) { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter+=m_pitch-1; + } } } // get output diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp index 10b153e7f..2c58ea885 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp @@ -41,7 +41,7 @@ class scc_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const int cycles=1); // accessors inline void reset_addr() { m_addr = 0; } @@ -151,7 +151,7 @@ class scc_core : public vgsound_emu_core // internal state virtual void reset(); - void tick(); + void tick(const int cycles=1); // getters inline s32 out() { return m_out; } // output to DA0...DA10 pin diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index c7952a55b..555df814f 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -45,6 +45,16 @@ no plans have been made for TX81Z MIDI passthrough, because: - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `1Exx`: set LFO AM depth. +- `1Fxx`: set LFO PM depth. +- `24xx`: set LFO 2 speed. +- `25xx`: set LFO 2 waveform. `xx` may be one of the following: + - `00`: saw + - `01`: square + - `02`: triangle + - `03`: noise +- `26xx`: set LFO 2 AM depth. +- `27xx`: set LFO 2 PM depth. - `28xy`: set reverb of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c9453c49c..e3d6df9d4 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -103,9 +103,8 @@ enum DivDispatchCmds { DIV_CMD_FM_AM_DEPTH, // (depth) DIV_CMD_FM_PM_DEPTH, // (depth) - DIV_CMD_GENESIS_LFO, // unused? - - DIV_CMD_ARCADE_LFO, // unused? + DIV_CMD_FM_LFO2, // (speed) + DIV_CMD_FM_LFO2_WAVE, // (waveform) DIV_CMD_STD_NOISE_FREQ, // (freq) DIV_CMD_STD_NOISE_MODE, // (mode) @@ -227,6 +226,9 @@ enum DivDispatchCmds { DIV_CMD_SURROUND_PANNING, // (out, val) + DIV_CMD_FM_AM2_DEPTH, // (depth) + DIV_CMD_FM_PM2_DEPTH, // (depth) + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_CMD_MAX diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ce58ec752..fee4b011c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4472,6 +4472,9 @@ bool DivEngine::init() { for (int i=0; i<64; i++) { vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); } + for (int i=0; i<128; i++) { + tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI))); + } for (int i=0; i<4096; i++) { reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0))); pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0))); diff --git a/src/engine/engine.h b/src/engine/engine.h index d0f6f98b9..fc552cacc 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -413,6 +413,7 @@ class DivEngine { } sPreview; short vibTable[64]; + short tremTable[128]; int reversePitchTable[4096]; int pitchTable[4096]; char c163NameCS[1024]; @@ -1158,6 +1159,7 @@ class DivEngine { memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(vibTable,0,64*sizeof(short)); + memset(tremTable,0,128*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); memset(sysDefs,0,256*sizeof(void*)); diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index 97ae9cdea..103d9ee6b 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -56,7 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic } if (delay>0) { delay--; - had=false; + if (!linger) had=false; return; } if (began && source.delay>0) { @@ -523,4 +523,4 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) { return NULL; } -#undef CONSIDER \ No newline at end of file +#undef CONSIDER diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 782ee062e..94202de19 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) { for (size_t h=0; h=64) writeOscBuf=0; + if (++writeOscBuf>=8) writeOscBuf=0; out<<=6; // scale output to 16 bit @@ -332,9 +332,9 @@ void DivPlatformBubSysWSG::notifyInsDeletion(void* ins) { void DivPlatformBubSysWSG::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC; CHECK_CUSTOM_CLOCK; - rate=chipClock; + rate=chipClock/8; for (int i=0; i<2; i++) { - oscBuf[i]->rate=rate/64; + oscBuf[i]->rate=rate/8; } } diff --git a/src/engine/platform/fmshared_OPM.h b/src/engine/platform/fmshared_OPM.h index 02c30b7eb..e922e8abb 100644 --- a/src/engine/platform/fmshared_OPM.h +++ b/src/engine/platform/fmshared_OPM.h @@ -41,8 +41,14 @@ class DivPlatformOPM: public DivPlatformFMBase { 0x00, 0x08, 0x10, 0x18 }; + unsigned char lfoValue, lfoValue2, lfoShape, lfoShape2; + DivPlatformOPM(): - DivPlatformFMBase() {} + DivPlatformFMBase(), + lfoValue(0), + lfoValue2(0), + lfoShape(0), + lfoShape2(0) {} }; #endif diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b75823602..a8228bedc 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -225,6 +225,9 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { //OPN2_Write(&fm,0,0); for (int i=0; i<6; i++) { + int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + if (chOut<-32768) chOut=-32768; + if (chOut>32767) chOut=32767; if (i==5) { if (fm_ymfm->debug_dac_enable()) { if (softPCM) { @@ -234,10 +237,10 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=chOut; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=chOut; } } diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 42e31b8b9..5c9d986a4 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -651,10 +651,10 @@ void DivPlatformGenesisExt::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index afaba9f94..ff57f9afb 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1555,7 +1555,11 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { - if (ch>=totalChans) return NULL; + if (oplType==759) { + if (ch>=totalChans+1) return NULL; + } else { + if (ch>=totalChans) return NULL; + } if (oplType==3 && ch<12) { if (chan[ch&(~1)].fourOp) { if (ch&1) { diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index e1fa7424f..cd97ff309 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -82,9 +82,7 @@ const char** DivPlatformSCC::getRegisterSheet() { void DivPlatformSCC::acquire(short** buf, size_t len) { for (size_t h=0; htick(); - } + scc->tick(16); short out=(short)scc->out()<<5; buf[0][h]=out; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 6679eba25..518dd6e73 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -22,7 +22,7 @@ #include "../../ta-log.h" #include -#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(0x200+a,v);}}} +#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}}} const char* regCheatSheetSN[]={ "DATA", "0", diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index ead26a81b..7184cdc43 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -811,7 +811,7 @@ const void* DivPlatformSNES::getSampleMem(int index) { size_t DivPlatformSNES::getSampleMemCapacity(int index) { // TODO change it based on current echo buffer size - return index == 0 ? 65536 : 0; + return index == 0 ? (65536-echoDelay*2048) : 0; } size_t DivPlatformSNES::getSampleMemUsage(int index) { @@ -825,7 +825,7 @@ bool DivPlatformSNES::isSampleLoaded(int index, int sample) { } void DivPlatformSNES::renderSamples(int sysID) { - memset(copyOfSampleMem,0,getSampleMemCapacity()); + memset(copyOfSampleMem,0,65536); memset(sampleOff,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index f7bb24b31..9033a0d79 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -1,3 +1,25 @@ +/* su.cpp/su.h - Sound Unit emulator + * Copyright (C) 2015-2023 tildearrow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #define _USE_MATH_DEFINES #include "su.h" #include @@ -391,7 +413,7 @@ void SoundUnit::Reset() { #ifdef TA_BIG_ENDIAN const unsigned char suBERemap[32]={ - 0x01, 0x00, 0x02, 0x03, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, + 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x11, 0x10, 0x12, 0x13, 0x15, 0x14, 0x16, 0x17, 0x19, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1e }; #endif diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index 528d545b5..d8e0f2709 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -1,7 +1,27 @@ +/* su.cpp/su.h - Sound Unit emulator + * Copyright (C) 2015-2023 tildearrow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include #include #include -#include class SoundUnit { signed char SCsine[256]; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 7a7addea8..7367f50ce 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -497,6 +497,9 @@ void DivPlatformSoundUnit::reset() { rWrite(0x9d,ilCtrl); rWrite(0xbc,ilSize); rWrite(0xbd,fil1); + + // copy sample memory + memcpy(su->pcm,sampleMem,sampleMemSize?65536:8192); } int DivPlatformSoundUnit::getOutputCount() { @@ -545,7 +548,7 @@ void DivPlatformSoundUnit::poke(std::vector& wlist) { } const void* DivPlatformSoundUnit::getSampleMem(int index) { - return (index==0)?su->pcm:NULL; + return (index==0)?sampleMem:NULL; } size_t DivPlatformSoundUnit::getSampleMemCapacity(int index) { @@ -563,7 +566,7 @@ bool DivPlatformSoundUnit::isSampleLoaded(int index, int sample) { } void DivPlatformSoundUnit::renderSamples(int sysID) { - memset(su->pcm,0,getSampleMemCapacity(0)); + memset(sampleMem,0,sampleMemSize?65536:8192); memset(sampleOffSU,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); @@ -582,10 +585,10 @@ void DivPlatformSoundUnit::renderSamples(int sysID) { break; } if (memPos+paddedLen>=getSampleMemCapacity(0)) { - memcpy(su->pcm+memPos,s->data8,getSampleMemCapacity(0)-memPos); + memcpy(sampleMem+memPos,s->data8,getSampleMemCapacity(0)-memPos); logW("out of PCM memory for sample %d!",i); } else { - memcpy(su->pcm+memPos,s->data8,paddedLen); + memcpy(sampleMem+memPos,s->data8,paddedLen); sampleLoaded[i]=true; } sampleOffSU[i]=memPos; @@ -593,6 +596,8 @@ void DivPlatformSoundUnit::renderSamples(int sysID) { } sampleMemLen=memPos; sysIDCache=sysID; + + memcpy(su->pcm,sampleMem,sampleMemSize?65536:8192); } int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { @@ -604,6 +609,8 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const Di oscBuf[i]=new DivDispatchOscBuffer; } su=new SoundUnit(); + sampleMem=new unsigned char[65536]; + memset(sampleMem,0,65536); sysIDCache=0; setFlags(flags); reset(); @@ -615,6 +622,7 @@ void DivPlatformSoundUnit::quit() { delete oscBuf[i]; } delete su; + delete sampleMem; } DivPlatformSoundUnit::~DivPlatformSoundUnit() { diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 0102db03e..13b4d4bba 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -89,6 +89,7 @@ class DivPlatformSoundUnit: public DivDispatch { short tempR; unsigned char sampleBank, lfoMode, lfoSpeed; SoundUnit* su; + unsigned char* sampleMem; size_t sampleMemLen; unsigned char regPool[128]; double NOTE_SU(int ch, int note); diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 7b29524ec..d46a2b67b 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -138,14 +138,14 @@ void DivPlatformTIA::tick(bool sysTick) { bf+=chan[i].arpOff; } } - chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch)+chan[i].pitch2; + 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; + 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; + chan[i].freq=dealWithFreq(12,bf,chan[i].pitch+chan[i].pitch2); } else { rWrite(0x15+i,chan[i].shape); } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index db728489e..80e69f195 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -26,6 +26,8 @@ #define ADDR_WS_FINE 0x100 // actually 0xc0 but bit 5 of data selects address #define ADDR_EGS_REV 0x120 +// actually 0x38 but bits 7 and 2 select address +#define ADDR_FMS2_AMS2 0x140 const char* regCheatSheetOPZ[]={ "Test", "00", @@ -139,7 +141,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.wave.had) { - rWrite(0x1b,chan[i].std.wave.val&3); + lfoShape=chan[i].std.wave.val&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } if (chan[i].std.pitch.had) { @@ -177,7 +180,28 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.ex3.had) { - immWrite(0x18,chan[i].std.ex3.val); + lfoValue=chan[i].std.ex3.val; + immWrite(0x18,lfoValue); + } + + if (chan[i].std.ex5.had) { + amDepth2=chan[i].std.ex5.val; + immWrite(0x17,amDepth2); + } + + if (chan[i].std.ex6.had) { + pmDepth2=chan[i].std.ex6.val; + immWrite(0x17,0x80|pmDepth2); + } + + if (chan[i].std.ex7.had) { + lfoValue2=chan[i].std.ex7.val; + immWrite(0x16,lfoValue2); + } + + if (chan[i].std.ex8.had) { + lfoShape2=chan[i].std.ex8.val&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } if (chan[i].std.alg.had) { @@ -286,6 +310,12 @@ void DivPlatformTX81Z::tick(bool sysTick) { oldWrites[i]=pendingWrites[i]; } } + for (int i=320; i<328; i++) { + if (pendingWrites[i]!=oldWrites[i]) { + immWrite(0x38+(i&7),(0x84|pendingWrites[i])); + oldWrites[i]=pendingWrites[i]; + } + } int hardResetElapsed=0; bool mustHardReset=false; @@ -405,7 +435,7 @@ void DivPlatformTX81Z::commitState(int ch, DivInstrument* ins) { rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); }*/ rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3)); - //rWrite(chanOffs[ch]+ADDR_FMS_AMS,0x84|((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3)); + rWrite(chanOffs[ch]+ADDR_FMS2_AMS2,((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3)); } } @@ -528,11 +558,23 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { break; } case DIV_CMD_FM_LFO: { - rWrite(0x18,c.value); + lfoValue=c.value; + immWrite(0x18,lfoValue); break; } case DIV_CMD_FM_LFO_WAVE: { - rWrite(0x1b,c.value&3); + lfoShape=c.value&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); + break; + } + case DIV_CMD_FM_LFO2: { + lfoValue2=c.value; + immWrite(0x16,lfoValue2); + break; + } + case DIV_CMD_FM_LFO2_WAVE: { + lfoShape2=c.value&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); break; } case DIV_CMD_FM_FB: { @@ -810,6 +852,16 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { immWrite(0x19,0x80|pmDepth); break; } + case DIV_CMD_FM_AM2_DEPTH: { + amDepth2=c.value; + immWrite(0x17,amDepth); + break; + } + case DIV_CMD_FM_PM2_DEPTH: { + pmDepth2=c.value; + immWrite(0x17,0x80|pmDepth); + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; @@ -880,7 +932,7 @@ void DivPlatformTX81Z::forceIns() { rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7)); }*/ rWrite(chanOffs[i]+ADDR_FMS_AMS,((chan[i].state.fms&7)<<4)|(chan[i].state.ams&3)); - //rWrite(chanOffs[i]+ADDR_FMS_AMS,0x84|((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3)); + rWrite(chanOffs[i]+ADDR_FMS2_AMS2,((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; @@ -888,6 +940,11 @@ void DivPlatformTX81Z::forceIns() { } immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); + immWrite(0x17,amDepth2); + immWrite(0x17,0x80|pmDepth2); + immWrite(0x18,lfoValue); + immWrite(0x16,lfoValue2); + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } void DivPlatformTX81Z::notifyInsChange(int ins) { @@ -958,12 +1015,19 @@ void DivPlatformTX81Z::reset() { delay=0; amDepth=0x7f; pmDepth=0x7f; + amDepth2=0x7f; + pmDepth2=0x7f; + lfoValue=0; + lfoValue2=0; + lfoShape=0; + lfoShape2=0; - //rWrite(0x18,0x10); immWrite(0x18,0x00); // LFO Freq Off + immWrite(0x16,0x00); immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); - //rWrite(0x1b,0x00); + immWrite(0x17,amDepth2); + immWrite(0x17,0x80|pmDepth2); extMode=false; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index e15158b7c..d1ebd543e 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -45,7 +45,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { DivDispatchOscBuffer* oscBuf[8]; int baseFreqOff; int pcmL, pcmR, pcmCycles; - unsigned char amDepth, pmDepth; + unsigned char amDepth, pmDepth, amDepth2, pmDepth2; ymfm::ym2414* fm_ymfm; ymfm::ym2414::output_data out_ymfm; diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 441ec7018..c14be9dc7 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -558,10 +558,10 @@ void DivPlatformYM2203Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 3a56a167b..96409ce76 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -582,10 +582,10 @@ void DivPlatformYM2608Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 0d113d052..e025ec2fa 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -578,10 +578,10 @@ void DivPlatformYM2610BExt::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==extChanOffs && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index e8a531dae..ccb877483 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -578,10 +578,10 @@ void DivPlatformYM2610Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==extChanOffs && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index f016dd532..ac1534879 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -103,9 +103,8 @@ const char* cmdName[]={ "FM_AM_DEPTH", "FM_PM_DEPTH", - "GENESIS_LFO", - - "ARCADE_LFO", + "FM_LFO2", + "FM_LFO2_WAVE", "STD_NOISE_FREQ", "STD_NOISE_MODE", @@ -227,6 +226,9 @@ const char* cmdName[]={ "SURROUND_PANNING", + "FM_AM2_DEPTH", + "FM_PM2_DEPTH", + "ALWAYS_SET_VOLUME" }; @@ -699,6 +701,13 @@ void DivEngine::processRow(int i, bool afterDelay) { // - then a volume slide down starts to the low boundary, and then when this is reached a volume slide up begins // - this process repeats until 0700 or 0Axy are found // - note that a volume value does not stop tremolo - instead it glitches this whole thing up + if (chan[i].tremoloDepth==0) { + chan[i].tremoloPos=0; + } + chan[i].tremoloDepth=effectVal&15; + chan[i].tremoloRate=effectVal>>4; + // tremolo and vol slides are incompatiblw + chan[i].volSpeed=0; break; case 0x0a: // volume ramp // TODO: non-0x-or-x0 value should be treated as 00 @@ -708,6 +717,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*64; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -848,10 +860,16 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); break; case 0xf3: // fine volume ramp up + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=-effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; @@ -878,6 +896,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*256; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -1249,6 +1270,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } + } else if (chan[i].tremoloDepth>0) { + chan[i].tremoloPos+=chan[i].tremoloRate; + chan[i].tremoloPos&=127; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,MAX(0,chan[i].volume-(tremTable[chan[i].tremoloPos]*chan[i].tremoloDepth))>>8)); } } if (chan[i].vibratoDepth>0) { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index fe75ea24e..533078516 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -512,6 +512,10 @@ void DivEngine::registerSystems() { EffectHandlerMap fmOPZPostEffectHandlerMap(fmOPMPostEffectHandlerMap); fmOPZPostEffectHandlerMap.insert({ + {0x24, {DIV_CMD_FM_LFO2, "24xx: Set LFO 2 speed"}}, + {0x25, {DIV_CMD_FM_LFO2_WAVE, "25xx: Set LFO 2 waveform (0 saw, 1 square, 2 triangle, 3 noise)"}}, + {0x26, {DIV_CMD_FM_AM2_DEPTH, "26xx: Set AM 2 depth (0 to 7F)", effectValAnd<127>}}, + {0x27, {DIV_CMD_FM_PM2_DEPTH, "27xx: Set PM 2 depth (0 to 7F)", effectValAnd<127>}}, {0x28, {DIV_CMD_FM_REV, "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2a, {DIV_CMD_FM_WS, "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2b, {DIV_CMD_FM_EG_SHIFT, "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index afac6f2e8..8490a67a3 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -29,6 +29,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; unsigned char smsAddr=isSecond?0x30:0x50; + unsigned char ggAddr=isSecond?0x3f:0x4f; unsigned char rf5c68Addr=isSecond?0xb1:0xb0; if (write.addr==0xffffffff) { // Furnace fake reset switch (sys) { @@ -647,7 +648,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } break; case DIV_SYSTEM_SMS: - w->writeC(smsAddr); + if (write.addr==1) { + w->writeC(ggAddr); + } else { + w->writeC(smsAddr); + } w->writeC(write.val); break; case DIV_SYSTEM_T6W28: diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 94b3c0f00..350aba450 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -75,6 +75,7 @@ const char* aboutLine[]={ "Dippy", "djtuBIG-MaliceX", "dumbut", + "EpicTyphlosion", "FΛDE", "Forte", "Fragmare", @@ -92,6 +93,7 @@ const char* aboutLine[]={ "LVintageNerd", "Mahbod Karamoozian", "Martin Demsky", + "MelonadeM", "Miker", "nicco1690", "", diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index c3745d616..09da1b257 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -50,8 +50,10 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) { selecting=true; selectingFull=false; dragSourceX=xCoarse; + dragSourceXFine=xFine; dragSourceY=y; dragDestinationX=xCoarse; + dragDestinationXFine=xFine; dragDestinationY=y; dragStart=selStart; dragEnd=selEnd; @@ -86,6 +88,7 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { if (!selecting) return; if (dragging) { dragDestinationX=xCoarse; + if (dragStart.xFine>=3 && dragStart.xCoarse==dragEnd.xCoarse) dragDestinationXFine=(dragSourceXFine&1)?((xFine-1)|1):((xFine+1)&(~1)); dragDestinationY=y; cursorDrag.xCoarse=xCoarse; cursorDrag.xFine=xFine; @@ -104,6 +107,15 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { dragDestinationX=lastChannel-(dragEnd.xCoarse-dragSourceX)-1; } + if (dragStart.xFine>=3 && dragStart.xCoarse==dragEnd.xCoarse) { + if (dragEnd.xFine+(dragDestinationXFine-dragSourceXFine)>(2+e->curPat[dragDestinationX].effectCols*2)) { + dragDestinationXFine=(2+e->curPat[dragDestinationX].effectCols*2)-dragEnd.xFine+dragSourceXFine; + } + if (dragStart.xFine+(dragDestinationXFine-dragSourceXFine)<3) { + dragDestinationXFine=3-dragStart.xFine+dragSourceXFine; + } + } + if (dragStart.y+(dragDestinationY-dragSourceY)<0) { dragDestinationY=dragSourceY-dragStart.y; } @@ -113,10 +125,10 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { } selStart.xCoarse=dragStart.xCoarse+(dragDestinationX-dragSourceX); - selStart.xFine=dragStart.xFine; + selStart.xFine=dragStart.xFine+(dragDestinationXFine-dragSourceXFine); selStart.y=dragStart.y+(dragDestinationY-dragSourceY); selEnd.xCoarse=dragEnd.xCoarse+(dragDestinationX-dragSourceX); - selEnd.xFine=dragEnd.xFine; + selEnd.xFine=dragEnd.xFine+(dragDestinationXFine-dragSourceXFine); selEnd.y=dragEnd.y+(dragDestinationY-dragSourceY); } else { if (selectingFull) { @@ -156,7 +168,7 @@ void FurnaceGUI::finishSelection() { selectingFull=false; if (dragging) { - if (dragSourceX==dragDestinationX && dragSourceY==dragDestinationY) { + if (dragSourceX==dragDestinationX && dragSourceY==dragDestinationY && dragSourceXFine==dragDestinationXFine) { cursor=cursorDrag; selStart=cursorDrag; selEnd=cursorDrag; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index b857b17cf..60511f09e 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -193,6 +193,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_SONG_INFO: nextWindow=GUI_WINDOW_SONG_INFO; break; + case GUI_ACTION_WINDOW_SPEED: + nextWindow=GUI_WINDOW_SPEED; + break; case GUI_ACTION_WINDOW_PATTERN: nextWindow=GUI_WINDOW_PATTERN; break; @@ -274,6 +277,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_SONG_INFO: songInfoOpen=false; break; + case GUI_WINDOW_SPEED: + speedOpen=false; + break; case GUI_WINDOW_ORDERS: ordersOpen=false; break; @@ -386,10 +392,10 @@ void FurnaceGUI::doAction(int what) { doSelectAll(); break; case GUI_ACTION_PAT_CUT: - doCopy(true); + doCopy(true,true,selStart,selEnd); break; case GUI_ACTION_PAT_COPY: - doCopy(false); + doCopy(false,true,selStart,selEnd); break; case GUI_ACTION_PAT_PASTE: doPaste(); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 556557a92..0d8341b28 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -358,66 +358,75 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { makeUndo(GUI_UNDO_PATTERN_DELETE); } -void FurnaceGUI::doCopy(bool cut) { - finishSelection(); - if (cut) { - curNibble=false; - prepareUndo(GUI_UNDO_PATTERN_CUT); +String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd) { + if (writeClipboard) { + finishSelection(); + if (cut) { + curNibble=false; + prepareUndo(GUI_UNDO_PATTERN_CUT); + } } - clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); + String clipb=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,sStart.xFine); - for (int j=selStart.y; j<=selEnd.y; j++) { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; + for (int j=sStart.y; j<=sEnd.y; j++) { + int iCoarse=sStart.xCoarse; + int iFine=sStart.xFine; if (iFine>3 && !(iFine&1)) { iFine--; } - clipboard+='\n'; - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + clipb+='\n'; + for (; iCoarse<=sEnd.xCoarse; iCoarse++) { if (!e->curSubSong->chanShow[iCoarse]) continue; DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarsecurPat[iCoarse].effectCols*2 && (iCoarsedata[j][0],pat->data[j][1]); + clipb+=noteNameNormal(pat->data[j][0],pat->data[j][1]); if (cut) { pat->data[j][0]=0; pat->data[j][1]=0; } } else { if (pat->data[j][iFine+1]==-1) { - clipboard+=".."; + clipb+=".."; } else { - clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); + clipb+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); } if (cut) { pat->data[j][iFine+1]=-1; } } } - clipboard+='|'; + clipb+='|'; iFine=0; } } - SDL_SetClipboardText(clipboard.c_str()); - if (cut) { - makeUndo(GUI_UNDO_PATTERN_CUT); + if (writeClipboard) { + SDL_SetClipboardText(clipb.c_str()); + if (cut) { + makeUndo(GUI_UNDO_PATTERN_CUT); + } + clipboard=clipb; } + return clipb; } -void FurnaceGUI::doPaste(PasteMode mode, int arg) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; +void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { + if (readClipboard) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); } - SDL_free(clipText); + clipb=clipboard; } std::vector data; String tempS; - for (char i: clipboard) { + for (char i: clipb) { if (i=='\r') continue; if (i=='\n') { data.push_back(tempS); @@ -562,15 +571,18 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg) { i=1; } } - if (settings.cursorPastePos) { - cursor.y=j; - if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; - selStart=cursor; - selEnd=cursor; - updateScroll(cursor.y); - } - makeUndo(GUI_UNDO_PATTERN_PASTE); + if (readClipboard) { + if (settings.cursorPastePos) { + cursor.y=j; + if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); + } } void FurnaceGUI::doChangeIns(int ins) { @@ -926,72 +938,21 @@ void FurnaceGUI::doExpand(int multiplier) { } void FurnaceGUI::doDrag() { - DivPattern* patBuffer=NULL; int len=dragEnd.xCoarse-dragStart.xCoarse+1; - DETERMINE_FIRST_LAST; - if (len<1) return; - patBuffer=new DivPattern[len]; prepareUndo(GUI_UNDO_PATTERN_DRAG); // copy and clear - { - int iCoarse=dragStart.xCoarse; - int iFine=dragStart.xFine; - int iCoarseP=0; - for (; iCoarse<=dragEnd.xCoarse; iCoarse++) { - if (!e->curSubSong->chanShow[iCoarse]) continue; - DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarsedata[j][iFine]; - pat->data[j][iFine]=0; - if (dragStart.y==dragEnd.y) pat->data[j][2]=-1; - } - patBuffer[iCoarseP].data[row][iFine+1]=pat->data[j][iFine+1]; - pat->data[j][iFine+1]=(iFine<1)?0:-1; + String c=doCopy(true,false,dragStart,dragEnd); - if (dragStart.y==dragEnd.y && iFine>2 && iFine&1 && settings.effectDeletionAltersValue) { - pat->data[j][iFine+2]=-1; - } - row++; - } - } - iFine=0; - iCoarseP++; - } - } + logV("copy: %s",c); // replace - { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int iCoarseP=0; - for (; iCoarse<=selEnd.xCoarse && iCoarsePlastChannel) continue; - if (!e->curSubSong->chanShow[iCoarse]) continue; - DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse=e->curSubSong->patLen) continue; - if (iFine==0) { - pat->data[j][iFine]=patBuffer[iCoarseP].data[row][iFine]; - } - pat->data[j][iFine+1]=patBuffer[iCoarseP].data[row][iFine+1]; - } - } - iFine=0; - iCoarseP++; - } - } + cursor=selStart; + doPaste(GUI_PASTE_MODE_NORMAL,0,false,c); - delete[] patBuffer; makeUndo(GUI_UNDO_PATTERN_DRAG); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e0c458b35..67df8c87e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1427,7 +1427,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Open File", {"compatible files", "*.fur *.dmf *.mod *.fc13 *.fc14 *.smod *.fc", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod,.fc13,.fc14,.smod,.fc},.*", workingDirSong, dpiScale @@ -1493,7 +1493,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "VOPM preset bank", "*.opm", "Wohlstand WOPL bank", "*.wopl", "Wohlstand WOPN bank", "*.wopn", - "all files", ".*"}, + "all files", "*"}, "all compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.gyb,.opm,.wopl,.wopn},.*", workingDirIns, dpiScale, @@ -1560,7 +1560,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load Wavetable", {"compatible files", "*.fuw *.dmw", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fuw,.dmw},.*", workingDirWave, dpiScale, @@ -1604,7 +1604,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load Sample", {"compatible files", "*.wav *.dmc *.brr", - "all files", ".*"}, + "all files", "*"}, "compatible files{.wav,.dmc,.brr},.*", workingDirSample, dpiScale, @@ -1617,7 +1617,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Raw Sample", - {"all files", ".*"}, + {"all files", "*"}, ".*", workingDirSample, dpiScale @@ -1784,7 +1784,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load ROM", {"compatible files", "*.rom *.bin", - "all files", ".*"}, + "all files", "*"}, "compatible files{.rom,.bin},.*", workingDirROM, dpiScale @@ -1796,7 +1796,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "Open Test", {"compatible files", "*.fur *.dmf *.mod", "another option", "*.wav *.ttf", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod},another option{.wav,.ttf},.*", workingDirTest, dpiScale, @@ -1815,7 +1815,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "Open Test (Multi)", {"compatible files", "*.fur *.dmf *.mod", "another option", "*.wav *.ttf", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod},another option{.wav,.ttf},.*", workingDirTest, dpiScale, @@ -2303,8 +2303,8 @@ void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; editOptsVisible=true; - if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); - if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false); + if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true,true,selStart,selEnd); + if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false,true,selStart,selEnd); if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste(); if (ImGui::BeginMenu("paste special...")) { if (ImGui::MenuItem("paste mix",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX))) doPaste(GUI_PASTE_MODE_MIX_FG); @@ -3729,6 +3729,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("window")) { if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen; + if (ImGui::MenuItem("speed",BIND_FOR(GUI_ACTION_WINDOW_SPEED),speedOpen)) speedOpen=!speedOpen; if (settings.unifiedDataView) { if (ImGui::MenuItem("assets",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen; } else { @@ -3915,6 +3916,7 @@ bool FurnaceGUI::loop() { drawSpoiler(); drawPattern(); drawEditControls(); + drawSpeed(); drawSongInfo(); drawOrders(); drawSampleList(); @@ -5245,6 +5247,7 @@ bool FurnaceGUI::init() { patManagerOpen=e->getConfBool("patManagerOpen",false); sysManagerOpen=e->getConfBool("sysManagerOpen",false); clockOpen=e->getConfBool("clockOpen",false); + speedOpen=e->getConfBool("speedOpen",true); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); effectListOpen=e->getConfBool("effectListOpen",false); @@ -5357,6 +5360,10 @@ bool FurnaceGUI::init() { logD("auto-detecting UI scale factor."); dpiScale=getScaleFactor(videoBackend); logD("scale factor: %f",dpiScale); + if (dpiScale<0.1f) { + logW("scale what?"); + dpiScale=1.0f; + } } #if !(defined(__APPLE__) || defined(_WIN32)) @@ -5615,6 +5622,7 @@ void FurnaceGUI::commitState() { e->setConf("patManagerOpen",patManagerOpen); e->setConf("sysManagerOpen",sysManagerOpen); e->setConf("clockOpen",clockOpen); + e->setConf("speedOpen",speedOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); e->setConf("effectListOpen",effectListOpen); @@ -5861,6 +5869,7 @@ FurnaceGUI::FurnaceGUI(): patManagerOpen(false), sysManagerOpen(false), clockOpen(false), + speedOpen(true), clockShowReal(true), clockShowRow(true), clockShowBeat(true), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5cb28b8f1..440581a7b 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -292,6 +292,7 @@ enum FurnaceGUIWindows { GUI_WINDOW_NOTHING=0, GUI_WINDOW_EDIT_CONTROLS, GUI_WINDOW_SONG_INFO, + GUI_WINDOW_SPEED, GUI_WINDOW_ORDERS, GUI_WINDOW_INS_LIST, GUI_WINDOW_PATTERN, @@ -439,6 +440,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_INS_LIST, GUI_ACTION_WINDOW_INS_EDIT, GUI_ACTION_WINDOW_SONG_INFO, + GUI_ACTION_WINDOW_SPEED, GUI_ACTION_WINDOW_PATTERN, GUI_ACTION_WINDOW_WAVE_LIST, GUI_ACTION_WINDOW_WAVE_EDIT, @@ -1453,7 +1455,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, dragSourceY, dragDestinationX, dragDestinationY, oldBeat, oldBar; + int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar; float soloTimeout; double exportFadeOut; @@ -1462,7 +1464,7 @@ class FurnaceGUI { bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; - bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen; + bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen; bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime; float clockMetroTick[16]; @@ -1818,6 +1820,7 @@ class FurnaceGUI { void drawMobileOrderSel(); void drawEditControls(); void drawSongInfo(bool asChild=false); + void drawSpeed(bool asChild=false); void drawOrders(); void drawPattern(); void drawInsList(bool asChild=false); @@ -1889,8 +1892,8 @@ class FurnaceGUI { void doPullDelete(); void doInsert(); void doTranspose(int amount, OperationMask& mask); - void doCopy(bool cut); - void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0); + String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); + void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate(); void doFade(int p0, int p1, bool mode); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index dc634d03e..5dc512d4c 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -494,6 +494,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_INS_LIST", "Instrument List", 0), D("WINDOW_INS_EDIT", "Instrument Editor", 0), D("WINDOW_SONG_INFO", "Song Information", 0), + D("WINDOW_SPEED", "Speed", 0), D("WINDOW_PATTERN", "Pattern", 0), D("WINDOW_WAVE_LIST", "Wavetable List", 0), D("WINDOW_WAVE_EDIT", "Wavetable Editor", 0), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 11c368265..9fb803bea 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1782,14 +1782,22 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI ImGui::TableNextColumn(); float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; int totalFit=MIN(255,availableWidth/MAX(1,macroPointSize*dpiScale)); - if (macroDragScroll>255-totalFit) { - macroDragScroll=255-totalFit; + int scrollMax=0; + for (FurnaceGUIMacroDesc& i: macros) { + if (i.macro->len>scrollMax) scrollMax=i.macro->len; } + scrollMax-=totalFit; + if (scrollMax<0) scrollMax=0; + if (macroDragScroll>scrollMax) { + macroDragScroll=scrollMax; + } + ImGui::BeginDisabled(scrollMax<1); ImGui::SetNextItemWidth(availableWidth); - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) { + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,scrollMax,"")) { if (macroDragScroll<0) macroDragScroll=0; - if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit; + if (macroDragScroll>scrollMax) macroDragScroll=scrollMax; } + ImGui::EndDisabled(); // draw macros for (FurnaceGUIMacroDesc& i: macros) { @@ -1838,11 +1846,13 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::TableNextColumn(); + ImGui::BeginDisabled(scrollMax<1); ImGui::SetNextItemWidth(availableWidth); - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) { + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,scrollMax,"")) { if (macroDragScroll<0) macroDragScroll=0; - if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit; + if (macroDragScroll>scrollMax) macroDragScroll=scrollMax; } + ImGui::EndDisabled(); ImGui::EndTable(); } break; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 43adfc85a..50a36a9e8 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3334,6 +3334,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGui::StyleColorsDark(&sty); } + if (dpiScale<0.1) dpiScale=0.1; + setupLabel(settings.noteOffLabel.c_str(),noteOffLabel,3); setupLabel(settings.noteRelLabel.c_str(),noteRelLabel,3); setupLabel(settings.macroRelLabel.c_str(),macroRelLabel,3); @@ -3539,21 +3541,21 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { fc1.MergeMode=true; if (settings.mainFont==6) { // custom font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.mainFont==5) { // system font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3561,21 +3563,21 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } } } else { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } // two fallback fonts - mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); - mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),&fc1,fontRange); + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),&fc1,fontRange); ImFontConfig fc; fc.MergeMode=true; fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; static const ImWchar fontRangeIcon[]={ICON_MIN_FA,ICON_MAX_FA,0}; - if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) { + if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,MAX(1,e->getConfInt("iconSize",16)*dpiScale),&fc,fontRangeIcon))==NULL) { logE("could not load icon font!"); } @@ -3584,21 +3586,21 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { patFont=mainFont; } else { if (settings.patFont==6) { // custom font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean."); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.patFont==5) { // system font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean."); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3606,7 +3608,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } } } else { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font!"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3615,7 +3617,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { // 0x39B = Λ static const ImWchar bigFontRange[]={0x20,0xFF,0x39b,0x39b,0}; - if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale,NULL,bigFontRange))==NULL) { + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,MAX(1,40*dpiScale),NULL,bigFontRange))==NULL) { logE("could not load big UI font!"); } diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index fc25d63e3..0bd94fdc4 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -29,7 +29,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { nextWindow=GUI_WINDOW_NOTHING; } if (!songInfoOpen && !asChild) return; - bool began=asChild?ImGui::BeginChild("Song Information"):ImGui::Begin("Song Information",&songInfoOpen,globalWinFlags); + bool began=asChild?ImGui::BeginChild("Song Info##Song Information"):ImGui::Begin("Song Info##Song Information",&songInfoOpen,globalWinFlags); if (began) { if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); @@ -100,138 +100,16 @@ void FurnaceGUI::drawSongInfo(bool asChild) { ImGui::EndTable(); } - if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { + if (ImGui::BeginTable("OtherProps",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(); - ImGui::Text("TimeBase"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - 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::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(); - } - 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(); - } - - 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::TableNextColumn(); - ImGui::Text("Highlight"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Pattern Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int patLen=e->curSubSong->patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED - if (patLen<1) patLen=1; - if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; - e->curSubSong->patLen=patLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Song Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int ordLen=e->curSubSong->ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED - if (ordLen<1) ordLen=1; - if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS; - e->curSubSong->ordersLen=ordLen; - if (curOrder>=ordLen) { - setOrder(ordLen-1); - } - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { - tempoView=!tempoView; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - 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; - if (setHz<1) setHz=1; - if (setHz>999) setHz=999; - e->setSongRate(setHz,setHz<52); - } - if (tempoView) { - ImGui::TableNextColumn(); - ImGui::Text("= %gHz",e->curSubSong->hz); - } else { - if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { - ImGui::TableNextColumn(); - ImGui::Text("PAL"); - } - if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { - ImGui::TableNextColumn(); - ImGui::Text("NTSC"); - } - } ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Tuning (A-4)"); ImGui::TableNextColumn(); float tune=e->song.tuning; + float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED if (tune<220.0f) tune=220.0f; diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp new file mode 100644 index 000000000..1d09331b4 --- /dev/null +++ b/src/gui/speed.cpp @@ -0,0 +1,179 @@ +/** + * 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 "intConst.h" + +void FurnaceGUI::drawSpeed(bool asChild) { + if (nextWindow==GUI_WINDOW_SPEED) { + speedOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!speedOpen && !asChild) return; + bool began=asChild?ImGui::BeginChild("Speed"):ImGui::Begin("Speed",&speedOpen,globalWinFlags); + if (began) { + if (ImGui::BeginTable("Props",3,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")) { + tempoView=!tempoView; + } + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + 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; + if (setHz<1) setHz=1; + if (setHz>999) setHz=999; + e->setSongRate(setHz,setHz<52); + } + if (tempoView) { + ImGui::TableNextColumn(); + ImGui::Text("= %gHz",e->curSubSong->hz); + } else { + if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { + ImGui::TableNextColumn(); + ImGui::Text("PAL"); + } + if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { + ImGui::TableNextColumn(); + 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(); + } + 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(); + } + + 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::TableNextColumn(); + ImGui::Text("TimeBase"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + 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::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Highlight"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::EndTable(); + } + + ImGui::Separator(); + + if (ImGui::BeginTable("Props2",3,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(); + ImGui::Text("Pattern Length"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + int patLen=e->curSubSong->patLen; + if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED + if (patLen<1) patLen=1; + if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; + e->curSubSong->patLen=patLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Song Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int ordLen=e->curSubSong->ordersLen; + if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED + if (ordLen<1) ordLen=1; + if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS; + e->curSubSong->ordersLen=ordLen; + if (curOrder>=ordLen) { + setOrder(ordLen-1); + } + } + + ImGui::EndTable(); + } + } + if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SPEED; + if (asChild) { + ImGui::EndChild(); + } else { + ImGui::End(); + } +}