diff --git a/CMakeLists.txt b/CMakeLists.txt index d6816c696..5a3f8e641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -782,6 +782,7 @@ src/gui/doAction.cpp src/gui/editing.cpp src/gui/editControls.cpp src/gui/effectList.cpp +src/gui/exportOptions.cpp src/gui/findReplace.cpp src/gui/fmPreview.cpp src/gui/gradient.cpp diff --git a/TODO.md b/TODO.md index 06951982a..f1918edef 100644 --- a/TODO.md +++ b/TODO.md @@ -1,15 +1,12 @@ # to-do -- add cheat code to insert bruno time (blank) - -# THE REAL TO-DO LIST - +- finish color import improvements (settings refactor) +- new undo stuff +- fix some bugs - finish auto-clone once you have done all of this (maybe not the first one), release 0.6.1 -Furnace is like alcohol... - # and then - new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing diff --git a/demos/misc/fragments_turbosound.fur b/demos/misc/fragments_turbosound.fur new file mode 100644 index 000000000..61064a7fc Binary files /dev/null and b/demos/misc/fragments_turbosound.fur differ diff --git a/demos/nes/christmas-fever.fur b/demos/nes/christmas-fever.fur new file mode 100644 index 000000000..874530242 Binary files /dev/null and b/demos/nes/christmas-fever.fur differ diff --git a/doc/2-interface/MIDI-value-input-1.png b/doc/2-interface/MIDI-value-input-1.png new file mode 100644 index 000000000..4e5df7be8 Binary files /dev/null and b/doc/2-interface/MIDI-value-input-1.png differ diff --git a/doc/2-interface/MIDI-value-input-2.png b/doc/2-interface/MIDI-value-input-2.png new file mode 100644 index 000000000..5173a2ded Binary files /dev/null and b/doc/2-interface/MIDI-value-input-2.png differ diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index 3571e7dd6..f65f23fcb 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -123,22 +123,21 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Note input**: enables note input. disable if you intend to use this device only for binding actions. - **Velocity input**: enables velocity input when entering notes in the pattern. - **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position. +- **Program change pass-through**: when enabled, program change events are sent to each channel as instrument change commands. + - this option is only available when the previous one is enabled. - **Map Yamaha FM voice data to instruments**: when enabled, Furnace will listen for any transmitted Yamaha SysEx patches. - this option is only useful if you have a Yamaha FM synthesizer (e.g. TX81Z). - selecting a voice or using the "Voice Transmit?" option will send a patch, and Furnace will create a new instrument with its data. - this may also be triggered by clicking on "Receive from TX81Z" in the instrument editor (OPZ only). - **Program change is instrument selection**: changes the current instrument when a program change event is received. + - this option is not available when "Program change pass-through" is enabled. - **Value input style**: changes the way values are entered when the pattern cursor is not in the Note column. the following styles are available: - **Disabled/custom**: no value input through MIDI. - **Two octaves (0 is C-4, F is D#5)**: maps keys in two octaves to single nibble input. the layout is: - - ` - octave n -- octave n+1 -` - - ` 1 3 6 8 A D F # # # ` - - `0 2 4 5 7 9 B C E # # # # #` + ![two octaves layout 1](MIDI-value-input-1.png) - **Raw (note number is value)**: the note number becomes the input value. not useful if you want to input anything above 7F. - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**: maps keys in two octaves, but with a different layout: - - ` - octave n -- octave n+1 -` - - ` A B C D E F # # # # ` - - `0 1 2 3 4 5 6 7 8 9 # # # #` + ![two octaves layout 2](MIDI-value-input-2.png) - **Use dual control change (one for each nibble)**: maps two control change events to the nibbles of a value. - **CC of upper nibble**: select the CC number that will change the upper nibble. - **CC of lower nibble**: select the CC number that will change the lower nibble. @@ -149,6 +148,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Control**: select the CC number that will change the value. - **Per-column control change**: when enabled, you can map several control change events to a channel's columns. - **Volume curve**: adjust the velocity to volume curve. + - the default is 2.0, which matches General MIDI standard. - **Actions**: this allows you to bind note input and control change events to actions. - **`+`** button: adds a new action. - window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device. @@ -498,7 +498,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Rounded window corners** - **Rounded buttons** - **Rounded menu corners** -- **Borders around widgets**: draws thin borders on buttons, checkboxes, text widgets, and the like. +- **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like. @@ -509,6 +509,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Import** - **Export** - **Reset defaults** +- **Guru mode**: exposes all color options (instead of accent colors). - **General** - **Color scheme type:** - **Dark** diff --git a/doc/7-systems/n163.md b/doc/7-systems/n163.md index a810f8106..78cc2562c 100644 --- a/doc/7-systems/n163.md +++ b/doc/7-systems/n163.md @@ -6,7 +6,7 @@ it has 256 nibbles (128 bytes) of internal RAM which is shared between channel s wavetables are variable in size and may be allocated anywhere in RAM. at least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. -Namco 163 uses time-division multiplexing for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. +Namco 163 uses time-division multiplexing (TDM) for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. ## waveform load position versus waveform position @@ -39,6 +39,13 @@ if the waveform changes (e.g. ins change, wave macro or wave synth), or the **lo - make sure to use `21xx` first! - `21xx`: **set position for 20xx.** +## chip options + +- **Initial channel limit**: sets the number of channels that will be active. higher values reduce volume and make TDM artifacts more noticeable. +- **Disable hissing**: remove TDM artifacts by mixing. sacrifices some accuracy! +- **Scale frequency to wave length**: automatically adjusts note frequency to account for differing waveform lengths. + - if disabled, note frequencies ignore waveveform length. this is how FamiTracker behaves. + ## info this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor. diff --git a/doc/7-systems/opll.md b/doc/7-systems/opll.md index 2cf068c13..4de890b22 100644 --- a/doc/7-systems/opll.md +++ b/doc/7-systems/opll.md @@ -15,8 +15,8 @@ the YM2413 is equipped with the following features: - a drum/percussion mode, replacing the last 3 voices with 5 rhythm channels, with drum mode tones hard-defined in the chip itself, like FM instruments. only pitch might be altered. - drum mode works like following: FM channel 7 is for Kick Drum, which is a normal FM channel but routed through mixer twice for 2× volume, like all drum sounds. FM channel 8 splits to Snare, Drum, and Hi-Hat. Snare Drum is the carrier and it works with a special 1 bit noise generator combined with a square wave, all possible by overriding phase-generator with some different synthesis method. Hi-Hat is the modulator and it works with the noise generator and also the special synthesis. channel 9 splits to Top-Cymbal and Tom-Tom, Top-Cymbal is the carrier and only has the special synthesis, while Tom-Tom is basically a 1op wave. - special synthesis mentioned already is: 5 square waves are gathered from 4×, 64× and 128× the pitch of channel 8 and 16× and 64× the pitch of channel 9 and they go through a process where 2 HH bits OR'd together, then 1 HH and 1 TC bit OR'd, then the two TC bits OR'd together, and those 3 results get XOR'd. -- 1 user-definable patch (this patch can be changed throughout the course of the song) -- 15 pre-defined patches which can all be used at the same time +- **1 user-definable patch (this patch can be changed throughout the course of the song)** +- **15 pre-defined patches which can all be used at the same time** - support for ADSR on both the modulator and the carrier - sine and half-sine based FM synthesis - 9 octave note control diff --git a/src/engine/configEngine.cpp b/src/engine/configEngine.cpp index 123a19a81..f226d1364 100644 --- a/src/engine/configEngine.cpp +++ b/src/engine/configEngine.cpp @@ -167,4 +167,8 @@ void DivEngine::setConf(String key, String value) { bool DivEngine::hasConf(String key) { return conf.has(key); -} \ No newline at end of file +} + +DivConfig& DivEngine::getConfObject() { + return conf; +} diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 6cbf2fe88..1ebab4059 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -83,7 +83,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0x82: return "82xx: Set panning (right channel)"; case 0x88: - return "88xx: Set panning (rear channels; x: left; y: right)"; + return "88xy: Set panning (rear channels; x: left; y: right)"; break; case 0x89: return "89xx: Set panning (rear left channel)"; @@ -2188,6 +2188,13 @@ int DivEngine::getMaxVolumeChan(int ch) { return chan[ch].volMax>>8; } +int DivEngine::mapVelocity(int ch, float vel) { + if (ch<0) return 0; + if (ch>=chans) return 0; + if (disCont[dispatchOfChan[ch]].dispatch==NULL) return 0; + return disCont[dispatchOfChan[ch]].dispatch->mapVelocity(dispatchChanOfChan[ch],vel); +} + unsigned char DivEngine::getOrder() { return prevOrder; } @@ -3391,6 +3398,10 @@ void DivEngine::setMidiDirect(bool value) { midiIsDirect=value; } +void DivEngine::setMidiDirectProgram(bool value) { + midiIsDirectProgram=value; +} + void DivEngine::setMidiVolExp(float value) { midiVolExp=value; } @@ -3459,6 +3470,12 @@ void DivEngine::rescanAudioDevices() { audioDevs.clear(); if (output!=NULL) { audioDevs=output->listAudioDevices(); + } +} + +void DivEngine::rescanMidiDevices() { + if (output!=NULL) { + logV("re-scanning midi..."); if (output->midiIn!=NULL) { midiIns=output->midiIn->listDevices(); } diff --git a/src/engine/engine.h b/src/engine/engine.h index fe0cd0991..a5b73b2ba 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -54,8 +54,8 @@ class DivWorkPool; #define DIV_UNSTABLE -#define DIV_VERSION "dev189" -#define DIV_ENGINE_VERSION 189 +#define DIV_VERSION "dev190" +#define DIV_ENGINE_VERSION 190 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -176,16 +176,16 @@ struct DivNoteEvent { signed char channel; unsigned char ins; signed char note, volume; - bool on, nop, pad1, pad2; - DivNoteEvent(int c, int i, int n, int v, bool o): + bool on, nop, insChange, fromMIDI; + DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false): channel(c), ins(i), note(n), volume(v), on(o), nop(false), - pad1(false), - pad2(false) {} + insChange(ic), + fromMIDI(fm) {} DivNoteEvent(): channel(-1), ins(0), @@ -193,8 +193,8 @@ struct DivNoteEvent { volume(-1), on(false), nop(true), - pad1(false), - pad2(false) {} + insChange(false), + fromMIDI(false) {} }; struct DivDispatchContainer { @@ -415,6 +415,7 @@ class DivEngine { bool firstTick; bool skipping; bool midiIsDirect; + bool midiIsDirectProgram; bool lowLatency; bool systemsRegistered; bool hasLoadedSomething; @@ -701,6 +702,9 @@ class DivEngine { double getConfDouble(String key, double fallback); String getConfString(String key, String fallback); + // get config object + DivConfig& getConfObject(); + // set a config value void setConf(String key, bool value); void setConf(String key, int value); @@ -851,6 +855,9 @@ class DivEngine { // get channel max volume int getMaxVolumeChan(int chan); + // map MIDI velocity to volume + int mapVelocity(int ch, float vel); + // get current order unsigned char getOrder(); @@ -1070,6 +1077,9 @@ class DivEngine { // rescan audio devices void rescanAudioDevices(); + /** rescan midi devices */ + void rescanMidiDevices(); + // set the console mode. void setConsoleMode(bool enable); @@ -1185,6 +1195,9 @@ class DivEngine { // set MIDI direct channel map void setMidiDirect(bool value); + // set MIDI direct program change + void setMidiDirectProgram(bool value); + // set MIDI volume curve exponent void setMidiVolExp(float value); @@ -1257,6 +1270,7 @@ class DivEngine { firstTick(false), skipping(false), midiIsDirect(false), + midiIsDirectProgram(false), lowLatency(false), systemsRegistered(false), hasLoadedSomething(false), diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 7622662a7..8cfbc292e 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -6511,7 +6511,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) { if (ins->type==DIV_INS_GB) { w->writeText("- Game Boy parameters:\n"); w->writeText(fmt::sprintf(" - volume: %d\n",ins->gb.envVol)); - w->writeText(fmt::sprintf(" - direction: %d\n",gbEnvDir[ins->gb.envDir?1:0])); + w->writeText(fmt::sprintf(" - direction: %s\n",gbEnvDir[ins->gb.envDir?1:0])); w->writeText(fmt::sprintf(" - length: %d\n",ins->gb.envLen)); w->writeText(fmt::sprintf(" - sound length: %d\n",ins->gb.soundLen)); w->writeText(fmt::sprintf(" - use software envelope: %s\n",trueFalse[ins->gb.softEnv?1:0])); diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 32ea4c002..e7d06c7f5 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -186,6 +186,17 @@ class DivPlatformOPN: public DivPlatformFMBase { void setCombo(bool combo) { useCombo=combo; } + virtual int mapVelocity(int ch, float vel) { + if (ch==csmChan) return vel*127.0; + if (ch==adpcmBChanOffs) return vel*255.0; + if (ch>=adpcmAChanOffs) { + if (vel==0) return 0; + if (vel>=1.0) return 31; + return CLAMP(round(32.0-(56.0-log2(vel*127.0)*8.0)),0,31); + } + if (ch>=psgChanOffs) return round(15.0*pow(vel,0.33)); + return DivPlatformFMBase::mapVelocity(ch,vel); + } }; #endif diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index 3a12f96e6..f20292a0d 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -132,7 +132,7 @@ class DivPlatformFMBase: public DivDispatch { // -36: 2: 48 // -42: 1: 56 if (vel==0) return 0; - if (vel==127) return 127; + if (vel>=1.0) return 127; return CLAMP(round(128.0-(56.0-log2(vel*127.0)*8.0)),0,127); } diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 633bf2886..c90404eae 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1300,6 +1300,12 @@ DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformGenesis::mapVelocity(int ch, float vel) { + if (ch==csmChan) return DivPlatformOPN::mapVelocity(ch,vel); + if (ch>5) return DivPlatformOPN::mapVelocity(5,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + unsigned char* DivPlatformGenesis::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 82d4301b9..a12e6625d 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -112,6 +112,7 @@ class DivPlatformGenesis: public DivPlatformOPN { virtual unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + virtual int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index aab02e1e1..0097f763f 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -818,6 +818,12 @@ DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) { return NULL; } +int DivPlatformGenesisExt::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformGenesis::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformGenesis::mapVelocity(extChanOffs,vel); + return DivPlatformGenesis::mapVelocity(ch,vel); +} + void DivPlatformGenesisExt::reset() { DivPlatformGenesis::reset(); diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 63112c069..d6ab86924 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -36,6 +36,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 0aa4bbd94..223216f66 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -199,7 +199,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) { for (int i=0; i>4; + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,chan[i].std.vol.val,15); } if (chan[i].std.duty.had) { chan[i].noise=chan[i].std.duty.val; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 8eb95fc28..c68271743 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -2104,6 +2104,21 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformOPL::mapVelocity(int ch, float vel) { + if (ch==adpcmChan) return vel*255.0; + // -0.75dB per step + // -6: 64: 8 + // -12: 32: 16 + // -18: 16: 24 + // -24: 8: 32 + // -30: 4: 40 + // -36: 2: 48 + // -42: 1: 56 + if (vel==0) return 0; + if (vel>=1.0) return 63; + return CLAMP(round(64.0-(56.0-log2(vel*127.0)*8.0)),0,63); +} + unsigned char* DivPlatformOPL::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index c4346a21e..bef02db5e 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -150,6 +150,7 @@ class DivPlatformOPL: public DivDispatch { unsigned short getPan(int chan); DivChannelPair getPaired(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 3f1621c02..9228568ef 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -962,6 +962,13 @@ DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { return oscBuf[ch]; } +int DivPlatformOPLL::mapVelocity(int ch, float vel) { + // -3dB per step + if (vel==0) return 0; + if (vel>=1.0) return 15; + return CLAMP(round(16.0-(14.0-log2(vel*127.0)*2.0)),0,15); +} + unsigned char* DivPlatformOPLL::getRegisterPool() { return regPool; } diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index e8bd627a4..70ece9c1e 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -95,6 +95,7 @@ class DivPlatformOPLL: public DivDispatch { void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index adb312ad8..bcb84e19b 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -692,6 +692,12 @@ DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2203Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2203Ext::reset() { DivPlatformYM2203::reset(); diff --git a/src/engine/platform/ym2203ext.h b/src/engine/platform/ym2203ext.h index 731e2a202..f0e468158 100644 --- a/src/engine/platform/ym2203ext.h +++ b/src/engine/platform/ym2203ext.h @@ -34,6 +34,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 50424d4eb..589bd542a 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -767,6 +767,12 @@ DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2608Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2608Ext::reset() { DivPlatformYM2608::reset(); diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index 0c9c1a418..159a6c807 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -35,6 +35,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 545a80e98..c0b0282b2 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2610BExt::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2610BExt::reset() { DivPlatformYM2610B::reset(); diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index 024119cff..e7feaf3c2 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -35,6 +35,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 39e341675..063fd5c36 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) { return NULL; } +int DivPlatformYM2610Ext::mapVelocity(int ch, float vel) { + if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel); + if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel); + return DivPlatformOPN::mapVelocity(ch,vel); +} + void DivPlatformYM2610Ext::reset() { DivPlatformYM2610::reset(); diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index f860a36cd..666f0d40f 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -35,6 +35,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2e22d074f..efede06a0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1376,8 +1376,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { pendingNotes.pop_front(); continue; } + if (note.insChange) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0)); + pendingNotes.pop_front(); + continue; + } if (note.on) { - dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); + if (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); + } if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { float curvedVol=pow((float)note.volume/127.0f,midiVolExp); int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); @@ -1838,7 +1845,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi case TA_MIDI_NOTE_OFF: { if (chan<0 || chan>=chans) break; if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } @@ -1853,13 +1860,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } } else { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true)); + pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true)); } else { autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); } @@ -1867,7 +1874,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } case TA_MIDI_PROGRAM: { - // TODO: change instrument event thingy + if (midiIsDirect && midiIsDirectProgram) { + pendingNotes.push_back(DivNoteEvent(chan,msg.data[0],0,0,false,true,true)); + } break; } } diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 527848a63..b45739dcb 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -76,7 +76,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { const char* insType="Bug!"; if (i>=0 && isong.insLen) { DivInstrument* ins=e->song.ins[i]; - insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type][0]; + insType=(ins->type>=DIV_INS_MAX)?"Unknown":insTypes[ins->type][0]; const char** insIcon=NULL; if (ins->type>=DIV_INS_MAX) { diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 3acc4bc59..c04f14118 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -65,6 +65,10 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAVE_AS: openFileDialog(GUI_FILE_SAVE); break; + case GUI_ACTION_EXPORT: + curExportType=GUI_EXPORT_NONE; + displayExport=true; + break; case GUI_ACTION_UNDO: if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { doUndoSample(); diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index b9b8a7409..1ce85020d 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -519,24 +519,8 @@ void FurnaceGUI::drawMobileControls() { openFileDialog(GUI_FILE_SAVE_DMF_LEGACY); } ImGui::SameLine(); - if (ImGui::Button("Export Audio")) { - openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); - } - ImGui::SameLine(); - if (ImGui::Button("Export VGM")) { - openFileDialog(GUI_FILE_EXPORT_VGM); - } - - if (ImGui::Button("CmdStream")) { - openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY); - } - ImGui::SameLine(); - if (ImGui::Button("CmdStream Text")) { - openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); - } - ImGui::SameLine(); - if (ImGui::Button("Text")) { - openFileDialog(GUI_FILE_EXPORT_TEXT); + if (ImGui::Button("Export")) { + doAction(GUI_ACTION_EXPORT); } if (ImGui::Button("Restore Backup")) { diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index d27c55a13..50c92aadf 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -771,10 +771,10 @@ unsigned int convertEffectMPT_S3M(unsigned char symbol, unsigned int val) { return (0x80<<8)|((val&0xf)<<4); break; case 0xC: - return (0xFC<<8)|(val&0xf); + return (0xEC<<8)|(val&0xf); break; case 0xD: - return (0xFD<<8)|(val&0xf); + return (0xED<<8)|(val&0xf); break; default: break; @@ -1117,7 +1117,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String if (invalidData) { - logW("invalid OpenMPT clipboard data! failed at line %d char %d",i,charPos); + logW("invalid clipboard data! failed at line %d char %d",i,charPos); logW("%s",line.c_str()); break; } diff --git a/src/gui/exportOptions.cpp b/src/gui/exportOptions.cpp new file mode 100644 index 000000000..165ba5f95 --- /dev/null +++ b/src/gui/exportOptions.cpp @@ -0,0 +1,357 @@ +/** + * 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 "guiConst.h" +#include "../fileutils.h" +#include "misc/cpp/imgui_stdlib.h" +#include + +void FurnaceGUI::drawExportAudio(bool onWindow) { + exitDisabledTimer=1; + + ImGui::RadioButton("one file",&audioExportType,0); + ImGui::RadioButton("multiple files (one per chip)",&audioExportType,1); + ImGui::RadioButton("multiple files (one per channel)",&audioExportType,2); + if (ImGui::InputInt("Loops",&exportLoops,1,2)) { + if (exportLoops<0) exportLoops=0; + } + if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) { + if (exportFadeOut<0.0) exportFadeOut=0.0; + } + + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + + if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) { + switch (audioExportType) { + case 0: + openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); + break; + case 1: + openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS); + break; + case 2: + openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL); + break; + } + ImGui::CloseCurrentPopup(); + } +} + +void FurnaceGUI::drawExportVGM(bool onWindow) { + exitDisabledTimer=1; + + ImGui::Text("settings:"); + if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) { + for (int i=0; i<7; i++) { + if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) { + vgmExportVersion=vgmVersions[i]; + } + } + ImGui::EndCombo(); + } + ImGui::Checkbox("loop",&vgmExportLoop); + if (vgmExportLoop && e->song.loopModality==2) { + ImGui::Text("loop trail:"); + ImGui::Indent(); + if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) { + vgmExportTrailingTicks=-1; + } + if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) { + vgmExportTrailingTicks=-2; + } + if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) { + vgmExportTrailingTicks=0; + } + if (vgmExportTrailingTicks>=0) { + ImGui::SameLine(); + if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) { + if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0; + } + } + ImGui::Unindent(); + } + ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "inserts data blocks on pattern changes.\n" + "useful if you are writing a playback routine.\n\n" + + "the format of a pattern change data block is:\n" + "67 66 FE ll ll ll ll 01 oo rr pp pp pp ...\n" + "- ll: length, a 32-bit little-endian number\n" + "- oo: order\n" + "- rr: initial row (a 0Dxx effect is able to select a different row)\n" + "- pp: pattern index (one per channel)\n\n" + + "pattern indexes are ordered as they appear in the song." + ); + } + ImGui::Checkbox("direct stream mode",&vgmExportDirectStream); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "required for DualPCM and MSM6258 export.\n\n" + "allows for volume/direction changes when playing samples,\n" + "at the cost of a massive increase in file size." + ); + } + ImGui::Text("chips to export:"); + bool hasOneAtLeast=false; + for (int i=0; isong.systemLen; i++) { + int minVersion=e->minVGMVersion(e->song.system[i]); + ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0); + ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]); + ImGui::EndDisabled(); + if (minVersion>vgmExportVersion) { + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("this chip is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff); + } + } else if (minVersion==0) { + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("this chip is not supported by the VGM format!"); + } + } else { + if (willExport[i]) hasOneAtLeast=true; + } + } + ImGui::Text("select the chip you wish to export, but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1); + if (hasOneAtLeast) { + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) { + openFileDialog(GUI_FILE_EXPORT_VGM); + ImGui::CloseCurrentPopup(); + } + } else { + ImGui::Text("nothing to export"); + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(400.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + } + } +} + +void FurnaceGUI::drawExportZSM(bool onWindow) { + exitDisabledTimer=1; + + ImGui::Text("Commander X16 Zsound Music File"); + if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) { + if (zsmExportTickRate<1) zsmExportTickRate=1; + if (zsmExportTickRate>44100) zsmExportTickRate=44100; + } + ImGui::Checkbox("loop",&zsmExportLoop); + ImGui::SameLine(); + ImGui::Checkbox("optimize size",&zsmExportOptimize); + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) { + openFileDialog(GUI_FILE_EXPORT_ZSM); + ImGui::CloseCurrentPopup(); + } +} + +void FurnaceGUI::drawExportAmigaVal(bool onWindow) { + exitDisabledTimer=1; + + ImGui::Text( + "this is NOT ROM export! only use for making sure the\n" + "Furnace Amiga emulator is working properly by\n" + "comparing it with real Amiga output." + ); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Directory"); + ImGui::SameLine(); + ImGui::InputText("##AVDPath",&workingDirROMExport); + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + if (ImGui::Button("Bake Data",ImVec2(200.0f*dpiScale,0))) { + std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION); + if (workingDirROMExport.size()>0) { + if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR; + } + for (DivROMExportOutput& i: out) { + String path=workingDirROMExport+i.name; + FILE* outFile=ps_fopen(path.c_str(),"wb"); + if (outFile!=NULL) { + fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile); + fclose(outFile); + } + i.data->finish(); + delete i.data; + } + showError(fmt::sprintf("Done! Baked %d files.",(int)out.size())); + ImGui::CloseCurrentPopup(); + } +} + +void FurnaceGUI::drawExportText(bool onWindow) { + exitDisabledTimer=1; + + ImGui::Text( + "this option exports the song to a text file.\n" + ); + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) { + openFileDialog(GUI_FILE_EXPORT_TEXT); + ImGui::CloseCurrentPopup(); + } +} + +void FurnaceGUI::drawExportCommand(bool onWindow) { + exitDisabledTimer=1; + + ImGui::Text( + "this option exports a text or binary file which\n" + "contains a dump of the internal command stream\n" + "produced when playing the song.\n\n" + + "technical/development use only!" + ); + if (onWindow) { + ImGui::Separator(); + if (ImGui::Button("Cancel",ImVec2(133.3f*dpiScale,0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + } + if (ImGui::Button("Export (binary)",ImVec2(133.3f*dpiScale,0))) { + openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Export (text)",ImVec2(133.3f*dpiScale,0))) { + openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); + ImGui::CloseCurrentPopup(); + } +} + +void FurnaceGUI::drawExport() { + if (settings.exportOptionsLayout==1 || curExportType==GUI_EXPORT_NONE) { + if (ImGui::BeginTabBar("ExportTypes")) { + if (ImGui::BeginTabItem("Audio")) { + drawExportAudio(true); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("VGM")) { + drawExportVGM(true); + ImGui::EndTabItem(); + } + int numZSMCompat=0; + for (int i=0; isong.systemLen; i++) { + if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++; + } + if (numZSMCompat>0) { + if (ImGui::BeginTabItem("ZSM")) { + drawExportZSM(true); + ImGui::EndTabItem(); + } + } + int numAmiga=0; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; + } + if (numAmiga && settings.iCannotWait) { + if (ImGui::BeginTabItem("Amiga Validation")) { + drawExportAmigaVal(true); + ImGui::EndTabItem(); + } + } + if (ImGui::BeginTabItem("Text")) { + drawExportText(true); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Command Stream")) { + drawExportCommand(true); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + } else switch (curExportType) { + case GUI_EXPORT_AUDIO: + drawExportAudio(true); + break; + case GUI_EXPORT_VGM: + drawExportVGM(true); + break; + case GUI_EXPORT_ZSM: + drawExportZSM(true); + break; + case GUI_EXPORT_AMIGA_VAL: + drawExportAmigaVal(true); + break; + case GUI_EXPORT_TEXT: + drawExportText(true); + break; + case GUI_EXPORT_CMD_STREAM: + drawExportCommand(true); + break; + default: + ImGui::Text("congratulations! you've unlocked a secret panel."); + if (ImGui::Button("Toggle hidden systems")) { + settings.hiddenSystems=!settings.hiddenSystems; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Toggle all instrument types")) { + settings.displayAllInsTypes=!settings.displayAllInsTypes; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Set pitch linearity to Partial")) { + e->song.linearPitch=1; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Enable multi-threading settings")) { + settings.showPool=1; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Set fat to max")) { + ImGuiStyle& sty=ImGui::GetStyle(); + sty.FramePadding=ImVec2(20.0f*dpiScale,20.0f*dpiScale); + sty.ItemSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale); + sty.ItemInnerSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale); + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Set muscle and fat to zero")) { + ImGuiStyle& sty=ImGui::GetStyle(); + sty.FramePadding=ImVec2(0,0); + sty.ItemSpacing=ImVec2(0,0); + sty.ItemInnerSpacing=ImVec2(0,0); + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("Tell tildearrow this must be a mistake")) { + showError("yeah, it's a bug. write a bug report in the GitHub page and tell me how did you get here."); + ImGui::CloseCurrentPopup(); + } + break; + } +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a9efea15b..b8da63237 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1216,7 +1216,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { if (latchVol!=-1) { pat->data[cursor.y][3]=MIN(maxVol,latchVol); } else if (vol!=-1) { - pat->data[cursor.y][3]=(vol*maxVol)/127; + pat->data[cursor.y][3]=e->mapVelocity(cursor.xCoarse,pow((float)vol/127.0f,midiMap.volExp)); } if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect; if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal; @@ -1319,6 +1319,32 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { } } +void FurnaceGUI::orderInput(int num) { + if (orderCursor>=0 && orderCursorgetTotalChannelCount()) { + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->lockSave([this,num]() { + if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0; + e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num); + }); + MARK_MODIFIED; + curNibble=!curNibble; + if (orderEditMode==2 || orderEditMode==3) { + if (!curNibble) { + if (orderEditMode==2) { + orderCursor++; + if (orderCursor>=e->getTotalChannelCount()) orderCursor=0; + } else if (orderEditMode==3) { + if (curOrdercurSubSong->ordersLen-1) { + setOrder(curOrder+1); + } + } + } + } + e->walkSong(loopOrder,loopRow,loopEnd); + makeUndo(GUI_UNDO_CHANGE_ORDER); + } +} + #define changeLatch(x) \ if (x<0) x=0; \ if (!latchNibble && !settings.pushNibble) x=0; \ @@ -1529,29 +1555,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { auto it=valueKeys.find(ev.key.keysym.sym); if (it!=valueKeys.cend()) { int num=it->second; - if (orderCursor>=0 && orderCursorgetTotalChannelCount()) { - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->lockSave([this,num]() { - if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0; - e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num); - }); - MARK_MODIFIED; - curNibble=!curNibble; - if (orderEditMode==2 || orderEditMode==3) { - if (!curNibble) { - if (orderEditMode==2) { - orderCursor++; - if (orderCursor>=e->getTotalChannelCount()) orderCursor=0; - } else if (orderEditMode==3) { - if (curOrdercurSubSong->ordersLen-1) { - setOrder(curOrder+1); - } - } - } - } - e->walkSong(loopOrder,loopRow,loopEnd); - makeUndo(GUI_UNDO_CHANGE_ORDER); - } + orderInput(num); } } break; @@ -2246,6 +2250,8 @@ int FurnaceGUI::load(String path) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } pushRecentFile(path); + // walk song + e->walkSong(loopOrder,loopRow,loopEnd); // do not auto-play a backup if (path.find(backupPath)!=0) { if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) { @@ -3776,7 +3782,7 @@ bool FurnaceGUI::loop() { noteInput( msg.data[0]-12, 0, - midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1 + midiMap.volInput?msg.data[1]:-1 ); } } else { @@ -3803,7 +3809,7 @@ bool FurnaceGUI::loop() { } break; case TA_MIDI_PROGRAM: - if (midiMap.programChange) { + if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) { curIns=msg.data[0]; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; wavePreviewInit=true; @@ -4099,197 +4105,84 @@ bool FurnaceGUI::loop() { openFileDialog(GUI_FILE_SAVE_DMF_LEGACY); } ImGui::Separator(); - if (ImGui::BeginMenu("export audio...")) { - exitDisabledTimer=1; - if (ImGui::MenuItem("one file")) { - openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); + if (settings.exportOptionsLayout==0) { + if (ImGui::BeginMenu("export audio...")) { + drawExportAudio(); + ImGui::EndMenu(); } - if (ImGui::MenuItem("multiple files (one per chip)")) { - openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS); + if (ImGui::BeginMenu("export VGM...")) { + drawExportVGM(); + ImGui::EndMenu(); } - if (ImGui::MenuItem("multiple files (one per channel)")) { - openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL); - } - if (ImGui::InputInt("Loops",&exportLoops,1,2)) { - if (exportLoops<0) exportLoops=0; - } - if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) { - if (exportFadeOut<0.0) exportFadeOut=0.0; - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("export VGM...")) { - exitDisabledTimer=1; - ImGui::Text("settings:"); - if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) { - for (int i=0; i<7; i++) { - if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) { - vgmExportVersion=vgmVersions[i]; - } - } - ImGui::EndCombo(); - } - ImGui::Checkbox("loop",&vgmExportLoop); - if (vgmExportLoop && e->song.loopModality==2) { - ImGui::Text("loop trail:"); - ImGui::Indent(); - if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) { - vgmExportTrailingTicks=-1; - } - if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) { - vgmExportTrailingTicks=-2; - } - if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) { - vgmExportTrailingTicks=0; - } - if (vgmExportTrailingTicks>=0) { - ImGui::SameLine(); - if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) { - if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0; - } - } - ImGui::Unindent(); - } - ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "inserts data blocks on pattern changes.\n" - "useful if you are writing a playback routine.\n\n" - - "the format of a pattern change data block is:\n" - "67 66 FE ll ll ll ll 01 oo rr pp pp pp ...\n" - "- ll: length, a 32-bit little-endian number\n" - "- oo: order\n" - "- rr: initial row (a 0Dxx effect is able to select a different row)\n" - "- pp: pattern index (one per channel)\n\n" - - "pattern indexes are ordered as they appear in the song." - ); - } - ImGui::Checkbox("direct stream mode",&vgmExportDirectStream); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "required for DualPCM and MSM6258 export.\n\n" - "allows for volume/direction changes when playing samples,\n" - "at the cost of a massive increase in file size." - ); - } - ImGui::Text("chips to export:"); - bool hasOneAtLeast=false; + int numZSMCompat=0; for (int i=0; isong.systemLen; i++) { - int minVersion=e->minVGMVersion(e->song.system[i]); - ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0); - ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]); - ImGui::EndDisabled(); - if (minVersion>vgmExportVersion) { - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { - ImGui::SetTooltip("this chip is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff); - } - } else if (minVersion==0) { - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { - ImGui::SetTooltip("this chip is not supported by the VGM format!"); - } - } else { - if (willExport[i]) hasOneAtLeast=true; + if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++; + } + if (numZSMCompat>0) { + if (ImGui::BeginMenu("export ZSM...")) { + drawExportZSM(); + ImGui::EndMenu(); } } - ImGui::Text("select the chip you wish to export,"); - ImGui::Text("but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1); - if (hasOneAtLeast) { - if (ImGui::MenuItem("click to export")) { - openFileDialog(GUI_FILE_EXPORT_VGM); - } - } else { - ImGui::Text("nothing to export"); + int numAmiga=0; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; } - ImGui::EndMenu(); - } - int numZSMCompat=0; - for (int i=0; isong.systemLen; i++) { - if ((e->song.system[i] == DIV_SYSTEM_VERA) || (e->song.system[i] == DIV_SYSTEM_YM2151)) numZSMCompat++; - } - if (numZSMCompat > 0) { - if (ImGui::BeginMenu("export ZSM...")) { - exitDisabledTimer=1; - ImGui::Text("Commander X16 Zsound Music File"); - if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,10)) { - if (zsmExportTickRate<1) zsmExportTickRate=1; - if (zsmExportTickRate>44100) zsmExportTickRate=44100; - } - ImGui::Checkbox("loop",&zsmExportLoop); - ImGui::SameLine(); - ImGui::Checkbox("optimize size",&zsmExportOptimize); - ImGui::SameLine(); - if (ImGui::Button("Begin Export")) { - openFileDialog(GUI_FILE_EXPORT_ZSM); - ImGui::CloseCurrentPopup(); + if (numAmiga && settings.iCannotWait) { + if (ImGui::BeginMenu("export Amiga validation data...")) { + drawExportAmigaVal(); + ImGui::EndMenu(); } + } + if (ImGui::BeginMenu("export text...")) { + drawExportText(); ImGui::EndMenu(); } - } - int numAmiga=0; - for (int i=0; isong.systemLen; i++) { - if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; - } - if (numAmiga && settings.iCannotWait) { - if (ImGui::BeginMenu("export Amiga validation data...")) { - exitDisabledTimer=1; - ImGui::Text( - "this is NOT ROM export! only use for making sure the\n" - "Furnace Amiga emulator is working properly by\n" - "comparing it with real Amiga output." - ); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Directory"); - ImGui::SameLine(); - ImGui::InputText("##AVDPath",&workingDirROMExport); - if (ImGui::Button("Bake Data")) { - std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION); - if (workingDirROMExport.size()>0) { - if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR; - } - for (DivROMExportOutput& i: out) { - String path=workingDirROMExport+i.name; - FILE* outFile=ps_fopen(path.c_str(),"wb"); - if (outFile!=NULL) { - fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile); - fclose(outFile); - } - i.data->finish(); - delete i.data; - } - showError(fmt::sprintf("Done! Baked %d files.",(int)out.size())); - ImGui::CloseCurrentPopup(); - } + if (ImGui::BeginMenu("export command stream...")) { + drawExportCommand(); ImGui::EndMenu(); } - } - if (ImGui::BeginMenu("export text...")) { - exitDisabledTimer=1; - ImGui::Text( - "this option exports the song to a text file.\n" - ); - if (ImGui::Button("export")) { - openFileDialog(GUI_FILE_EXPORT_TEXT); + } else if (settings.exportOptionsLayout==2) { + if (ImGui::MenuItem("export audio...")) { + curExportType=GUI_EXPORT_AUDIO; + displayExport=true; } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("export command stream...")) { - exitDisabledTimer=1; - ImGui::Text( - "this option exports a text or binary file which\n" - "contains a dump of the internal command stream\n" - "produced when playing the song.\n\n" - - "technical/development use only!" - ); - if (ImGui::Button("export (binary)")) { - openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY); + if (ImGui::MenuItem("export VGM...")) { + curExportType=GUI_EXPORT_VGM; + displayExport=true; } - if (ImGui::Button("export (text)")) { - openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); + int numZSMCompat=0; + for (int i=0; isong.systemLen; i++) { + if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++; + } + if (numZSMCompat>0) { + if (ImGui::MenuItem("export ZSM...")) { + curExportType=GUI_EXPORT_ZSM; + displayExport=true; + } + } + int numAmiga=0; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; + } + if (numAmiga && settings.iCannotWait) { + if (ImGui::MenuItem("export Amiga validation data...")) { + curExportType=GUI_EXPORT_AMIGA_VAL; + displayExport=true; + } + } + if (ImGui::MenuItem("export text...")) { + curExportType=GUI_EXPORT_TEXT; + displayExport=true; + } + if (ImGui::MenuItem("export command stream...")) { + curExportType=GUI_EXPORT_CMD_STREAM; + displayExport=true; + } + } else { + if (ImGui::MenuItem("export...",BIND_FOR(GUI_ACTION_EXPORT))) { + displayExport=true; } - ImGui::EndMenu(); } ImGui::Separator(); if (!settings.classicChipOptions) { @@ -4629,6 +4522,7 @@ bool FurnaceGUI::loop() { ordersOpen=true; curWindow=GUI_WINDOW_ORDERS; MEASURE(orders,drawOrders()); + MEASURE(piano,drawPiano()); break; case GUI_SCENE_INSTRUMENT: insEditOpen=true; @@ -5485,6 +5379,11 @@ bool FurnaceGUI::loop() { } } + if (displayExport) { + displayExport=false; + ImGui::OpenPopup("Export"); + } + if (displayEditString) { ImGui::OpenPopup("EditString"); } @@ -5526,6 +5425,12 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + if (ImGui::BeginPopupModal("Export",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5)); + drawExport(); + ImGui::EndPopup(); + } + centerNextWindow("Error",canvasW,canvasH); if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s",errorString.c_str()); @@ -7018,6 +6923,7 @@ bool FurnaceGUI::init() { return -2; } + if (midiMap.directChannel && midiMap.directProgram) return -1; return curIns; }); @@ -7726,7 +7632,9 @@ FurnaceGUI::FurnaceGUI(): introSkipDo(false), introStopped(false), curTutorial(-1), - curTutorialStep(0) { + curTutorialStep(0), + audioExportType(0), + curExportType(GUI_EXPORT_NONE) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; diff --git a/src/gui/gui.h b/src/gui/gui.h index b10adf188..162ebb0ac 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -101,6 +101,7 @@ enum FurnaceGUIColors { GUI_COLOR_MODAL_BACKDROP, GUI_COLOR_HEADER, GUI_COLOR_TEXT, + GUI_COLOR_TEXT_DISABLED, GUI_COLOR_ACCENT_PRIMARY, GUI_COLOR_ACCENT_SECONDARY, GUI_COLOR_TITLE_INACTIVE, @@ -124,6 +125,36 @@ enum FurnaceGUIColors { GUI_COLOR_NAV_HIGHLIGHT, GUI_COLOR_NAV_WIN_HIGHLIGHT, GUI_COLOR_NAV_WIN_BACKDROP, + GUI_COLOR_PLOT_LINES, + GUI_COLOR_PLOT_LINES_HOVER, + GUI_COLOR_PLOT_HISTOGRAM, + GUI_COLOR_PLOT_HISTOGRAM_HOVER, + + GUI_COLOR_BUTTON, + GUI_COLOR_BUTTON_HOVER, + GUI_COLOR_BUTTON_ACTIVE, + GUI_COLOR_TAB, + GUI_COLOR_TAB_HOVER, + GUI_COLOR_TAB_ACTIVE, + GUI_COLOR_TAB_UNFOCUSED, + GUI_COLOR_TAB_UNFOCUSED_ACTIVE, + GUI_COLOR_IMGUI_HEADER, + GUI_COLOR_IMGUI_HEADER_HOVER, + GUI_COLOR_IMGUI_HEADER_ACTIVE, + GUI_COLOR_RESIZE_GRIP, + GUI_COLOR_RESIZE_GRIP_HOVER, + GUI_COLOR_RESIZE_GRIP_ACTIVE, + GUI_COLOR_WIDGET_BACKGROUND, + GUI_COLOR_WIDGET_BACKGROUND_HOVER, + GUI_COLOR_WIDGET_BACKGROUND_ACTIVE, + GUI_COLOR_SLIDER_GRAB, + GUI_COLOR_SLIDER_GRAB_ACTIVE, + GUI_COLOR_TITLE_BACKGROUND_ACTIVE, + GUI_COLOR_CHECK_MARK, + GUI_COLOR_TEXT_SELECTION, + GUI_COLOR_TABLE_ROW_EVEN, + GUI_COLOR_TABLE_ROW_ODD, + GUI_COLOR_TOGGLE_OFF, GUI_COLOR_TOGGLE_ON, GUI_COLOR_EDITING, @@ -438,6 +469,21 @@ enum FurnaceGUIMobileScenes { GUI_SCENE_OTHER, }; +enum FurnaceGUISettingGroups: unsigned int { + GUI_SETTINGS_GENERAL=1, + GUI_SETTINGS_AUDIO=2, + GUI_SETTINGS_MIDI=4, + GUI_SETTINGS_KEYBOARD=8, + GUI_SETTINGS_BEHAVIOR=16, + GUI_SETTINGS_FONT=32, + GUI_SETTINGS_APPEARANCE=64, + GUI_SETTINGS_LAYOUTS=128, + GUI_SETTINGS_COLOR=256, + GUI_SETTINGS_EMULATION=512, + + GUI_SETTINGS_ALL=0xffffffff +}; + enum FurnaceGUIFileDialogs { GUI_FILE_OPEN, GUI_FILE_OPEN_BACKUP, @@ -505,6 +551,17 @@ enum FurnaceGUIWarnings { GUI_WARN_GENERIC }; +enum FurnaceGUIExportTypes { + GUI_EXPORT_NONE=-1, + + GUI_EXPORT_AUDIO=0, + GUI_EXPORT_VGM, + GUI_EXPORT_ZSM, + GUI_EXPORT_CMD_STREAM, + GUI_EXPORT_AMIGA_VAL, + GUI_EXPORT_TEXT +}; + enum FurnaceGUIFMAlgs { FM_ALGS_4OP, FM_ALGS_2OP_OPL, @@ -518,6 +575,7 @@ enum FurnaceGUIActions { GUI_ACTION_OPEN_BACKUP, GUI_ACTION_SAVE, GUI_ACTION_SAVE_AS, + GUI_ACTION_EXPORT, GUI_ACTION_UNDO, GUI_ACTION_REDO, GUI_ACTION_PLAY_TOGGLE, @@ -828,6 +886,14 @@ struct SelectionPoint { xCoarse(0), xFine(0), y(0) {} }; +struct UndoRegion { + struct UndoRegionPoint { + int ord, x, y; + UndoRegionPoint(): + ord(0), x(0), y(0) {} + } begin, end; +}; + enum ActionType { GUI_UNDO_CHANGE_ORDER, GUI_UNDO_PATTERN_EDIT, @@ -937,7 +1003,7 @@ struct MIDIMap { int**** map; std::vector binds; - bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse; + bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse, directProgram; // 0: disabled // // 1: C- C# D- D# E- F- F# G- G# A- A# B- @@ -1000,6 +1066,7 @@ struct MIDIMap { midiClock(false), midiTimeCode(false), yamahaFMResponse(false), + directProgram(false), valueInputStyle(1), valueInputControlMSB(0), valueInputControlLSB(0), @@ -1388,7 +1455,7 @@ class FurnaceGUI { bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList; bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; - bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange; + bool displayNew, displayExport, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange; bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool mobileEdit; bool killGraphics; @@ -1644,6 +1711,7 @@ class FurnaceGUI { int centerPopup; int insIconsStyle; int classicChipOptions; + int exportOptionsLayout; int wasapiEx; int chanOscThreads; int renderPoolThreads; @@ -1656,6 +1724,7 @@ class FurnaceGUI { int fontAutoHint; int fontAntiAlias; int selectAssetOnLoad; + int basicColors; unsigned int maxUndoSteps; String mainFontPath; String headFontPath; @@ -1839,11 +1908,12 @@ class FurnaceGUI { centerPopup(1), insIconsStyle(1), classicChipOptions(0), + exportOptionsLayout(1), wasapiEx(0), chanOscThreads(0), renderPoolThreads(0), showPool(0), - writeInsNames(1), + writeInsNames(0), readInsNames(1), fontBackend(1), fontHinting(0), @@ -1851,6 +1921,7 @@ class FurnaceGUI { fontAutoHint(1), fontAntiAlias(1), selectAssetOnLoad(1), + basicColors(1), maxUndoSteps(100), mainFontPath(""), headFontPath(""), @@ -2245,7 +2316,7 @@ class FurnaceGUI { int pianoOffset, pianoOffsetEdit; int pianoView, pianoInputPadMode; - //effect sorting + // effect sorting bool effectsShow[10]; // TX81Z @@ -2285,6 +2356,17 @@ class FurnaceGUI { // tutorial int curTutorial, curTutorialStep; + // export options + int audioExportType; + FurnaceGUIExportTypes curExportType; + + void drawExportAudio(bool onWindow=false); + void drawExportVGM(bool onWindow=false); + void drawExportZSM(bool onWindow=false); + void drawExportAmigaVal(bool onWindow=false); + void drawExportText(bool onWindow=false); + void drawExportCommand(bool onWindow=false); + void drawSSGEnv(unsigned char type, const ImVec2& size); void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); @@ -2398,6 +2480,7 @@ class FurnaceGUI { void drawSettings(); void drawDebug(); void drawNewSong(); + void drawExport(); void drawLog(); void drawEffectList(); void drawSubSongs(bool asChild=false); @@ -2423,6 +2506,9 @@ class FurnaceGUI { void resetColors(); void resetKeybinds(); + void readConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL); + void writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL); + void syncSettings(); void commitSettings(); void syncTutorial(); @@ -2473,6 +2559,7 @@ class FurnaceGUI { DivSystem systemPicker(); void noteInput(int num, int key, int vol=-1); void valueInput(int num, bool direct=false, int target=-1); + void orderInput(int num); void doGenerateWave(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index b467cd57a..c5fc02311 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -538,6 +538,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("OPEN_BACKUP", "Restore backup", 0), D("SAVE", "Save file", FURKMOD_CMD|SDLK_s), D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s), + D("EXPORT", "Export", 0), D("UNDO", "Undo", FURKMOD_CMD|SDLK_z), #ifdef __APPLE__ D("REDO", "Redo", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_z), @@ -795,6 +796,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)), D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)), D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_TEXT_DISABLED,"",ImVec4(0.5f,0.5f,0.5f,1.0f)), D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)), @@ -818,6 +820,36 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)), D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)), + D(GUI_COLOR_PLOT_LINES,"",ImVec4(0.61f,0.61f,0.61f,1.0f)), + D(GUI_COLOR_PLOT_LINES_HOVER,"",ImVec4(1.00f,0.43f,0.35f,1.00f)), + D(GUI_COLOR_PLOT_HISTOGRAM,"",ImVec4(0.0f,0.9f,1.0f,1.0f)), + D(GUI_COLOR_PLOT_HISTOGRAM_HOVER,"",ImVec4(0.0f,0.9f,1.0f,1.0f)), + + D(GUI_COLOR_BUTTON,"",ImVec4(0.085f,0.216f,0.343f,1.0f)), + D(GUI_COLOR_BUTTON_HOVER,"",ImVec4(0.075f,0.287f,0.49f,1.0f)), + D(GUI_COLOR_BUTTON_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_TAB,"",ImVec4(0.085f,0.216f,0.343f,1.0f)), + D(GUI_COLOR_TAB_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)), + D(GUI_COLOR_TAB_ACTIVE,"",ImVec4(0.25f,0.47f,0.735f,1.0f)), + D(GUI_COLOR_TAB_UNFOCUSED,"",ImVec4(0.085f,0.216f,0.343f,1.0f)), + D(GUI_COLOR_TAB_UNFOCUSED_ACTIVE,"",ImVec4(0.075f,0.287f,0.49f,1.0f)), + D(GUI_COLOR_IMGUI_HEADER,"",ImVec4(0.083f,0.156f,0.245f,1.0f)), + D(GUI_COLOR_IMGUI_HEADER_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)), + D(GUI_COLOR_IMGUI_HEADER_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), + D(GUI_COLOR_RESIZE_GRIP,"",ImVec4(0.083f,0.156f,0.245f,1.0f)), + D(GUI_COLOR_RESIZE_GRIP_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)), + D(GUI_COLOR_RESIZE_GRIP_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), + D(GUI_COLOR_WIDGET_BACKGROUND,"",ImVec4(0.083f,0.156f,0.245f,1.0f)), + D(GUI_COLOR_WIDGET_BACKGROUND_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)), + D(GUI_COLOR_WIDGET_BACKGROUND_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), + D(GUI_COLOR_SLIDER_GRAB,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_SLIDER_GRAB_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_TITLE_BACKGROUND_ACTIVE,"",ImVec4(0.085f,0.216f,0.343f,1.0f)), + D(GUI_COLOR_CHECK_MARK,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_TEXT_SELECTION,"",ImVec4(0.165f,0.313f,0.49f,1.0f)), + D(GUI_COLOR_TABLE_ROW_EVEN,"",ImVec4(0.0f,0.0f,0.0f,0.0f)), + D(GUI_COLOR_TABLE_ROW_ODD,"",ImVec4(1.0f,1.0f,1.0f,0.06f)), + D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)), D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)), D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f80757b67..40515099e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2967,7 +2967,11 @@ void FurnaceGUI::drawInsEdit() { updateFMPreview=false; } if (settings.insEditColorize) { - pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); + if (ins->type>=DIV_INS_MAX) { + pushAccentColors(uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],ImVec4(0.0f,0.0f,0.0f,0.0f)); + } else { + pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); + } } if (ImGui::BeginTable("InsProp",3)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); @@ -3028,14 +3032,8 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("Type"); ImGui::TableNextColumn(); - if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM; int insType=ins->type; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - /* - if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) { - ins->type=(DivInstrumentType)insType; - } - */ bool warnType=true; for (DivInstrumentType i: e->getPossibleInsTypes()) { if (i==insType) { @@ -3044,7 +3042,7 @@ void FurnaceGUI::drawInsEdit() { } pushWarningColor(warnType,warnType && failedNoteOn); - if (ImGui::BeginCombo("##Type",insTypes[insType][0])) { + if (ImGui::BeginCombo("##Type",(insType>=DIV_INS_MAX)?"Unknown":insTypes[insType][0])) { std::vector insTypeList; if (settings.displayAllInsTypes) { for (int i=0; insTypes[i][0]; i++) { @@ -6689,7 +6687,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } } - if (ImGui::BeginTabItem("Macros")) { + if (ins->typetype==DIV_INS_VRC6) { insTabSample(ins); } + if (ins->type>=DIV_INS_MAX) { + if (ImGui::BeginTabItem("Error")) { + ImGui::Text("invalid instrument type! change it first."); + ImGui::EndTabItem(); + } + } ImGui::EndTabBar(); } if (settings.insEditColorize) { diff --git a/src/gui/midiMap.cpp b/src/gui/midiMap.cpp index 602347107..fac0399b8 100644 --- a/src/gui/midiMap.cpp +++ b/src/gui/midiMap.cpp @@ -139,6 +139,7 @@ bool MIDIMap::read(String path) { UNDERSTAND_OPTION(midiClock) else UNDERSTAND_OPTION(midiTimeCode) else UNDERSTAND_OPTION(yamahaFMResponse) else + UNDERSTAND_OPTION(directProgram) else UNDERSTAND_OPTION(valueInputStyle) else UNDERSTAND_OPTION(valueInputControlMSB) else UNDERSTAND_OPTION(valueInputControlLSB) else @@ -205,6 +206,7 @@ bool MIDIMap::write(String path) { WRITE_OPTION(midiClock); WRITE_OPTION(midiTimeCode); WRITE_OPTION(yamahaFMResponse); + WRITE_OPTION(directProgram); WRITE_OPTION(valueInputStyle); WRITE_OPTION(valueInputControlMSB); WRITE_OPTION(valueInputControlLSB); diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 43dba3d79..4b0ae27d0 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -181,6 +181,10 @@ void FurnaceGUI::drawOrderButtons() { } NEXT_BUTTON; + if (orderEditMode==0 && mobileUI) { + orderEditMode=1; + } + const char* orderEditModeLabel="?##OrderEditMode"; if (orderEditMode==3) { orderEditModeLabel=ICON_FA_ARROWS_V "##OrderEditMode"; @@ -193,7 +197,7 @@ void FurnaceGUI::drawOrderButtons() { } if (ImGui::Button(orderEditModeLabel)) { handleUnimportant orderEditMode++; - if (orderEditMode>3) orderEditMode=0; + if (orderEditMode>3) orderEditMode=mobileUI?1:0; curNibble=false; } if (ImGui::IsItemHovered()) { @@ -219,7 +223,7 @@ void FurnaceGUI::drawOrders() { if (!ordersOpen) return; if (mobileUI) { patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f)); - patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH)); + patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)-(pianoOpen?(0.4*canvasW):0.0f)):ImVec2(canvasW-(0.16*canvasH),canvasH-(pianoOpen?(0.3*canvasH):0.0f))); ImGui::SetNextWindowPos(patWindowPos); ImGui::SetNextWindowSize(patWindowSize); } else { diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index a167b1b89..4e20142c7 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -42,7 +42,11 @@ const bool isTopKey[12]={ #define VALUE_DIGIT(x,label) \ if (ImGui::Button(label,buttonSize)) { \ - valueInput(x,false); \ + if (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0) { \ + orderInput(x); \ + } else { \ + valueInput(x,false); \ + } \ } void FurnaceGUI::drawPiano() { @@ -166,7 +170,7 @@ void FurnaceGUI::drawPiano() { } ImGui::TableNextColumn(); - if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) { + if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && ((cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) { ImVec2 buttonSize=ImGui::GetContentRegionAvail(); if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) { ImGui::TableNextRow(); @@ -443,9 +447,9 @@ void FurnaceGUI::drawPiano() { ImGui::End(); // draw input pad if necessary - if ((curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { + if ((curWindow==GUI_WINDOW_ORDERS || curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && (cursor.xFine>0 || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) { - ImGui::BeginDisabled(cursor.xFine==0); + ImGui::BeginDisabled(cursor.xFine==0 && !(curWindow==GUI_WINDOW_ORDERS && orderEditMode>0)); if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index dbf1a9661..3f4bd9de7 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1129,6 +1129,13 @@ void FurnaceGUI::drawSettings() { ImGui::EndCombo(); } + ImGui::SameLine(); + if (ImGui::Button("Re-scan MIDI devices")) { + e->rescanMidiDevices(); + audioEngineChanged=true; + settingsChanged=false; + } + if (hasToReloadMidi) { midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); midiMap.compile(); @@ -1141,10 +1148,19 @@ void FurnaceGUI::drawSettings() { //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) { e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); settingsChanged=true; } + if (midiMap.directChannel) { + if (ImGui::Checkbox("Program change pass-through",&midiMap.directProgram)) { + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); + settingsChanged=true; + } + } if (ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true; - if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + if (!(midiMap.directChannel && midiMap.directProgram)) { + if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + } //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); if (ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7)) settingsChanged=true; @@ -1663,6 +1679,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP); UI_KEYBIND_CONFIG(GUI_ACTION_SAVE); UI_KEYBIND_CONFIG(GUI_ACTION_SAVE_AS); + UI_KEYBIND_CONFIG(GUI_ACTION_EXPORT); UI_KEYBIND_CONFIG(GUI_ACTION_UNDO); UI_KEYBIND_CONFIG(GUI_ACTION_REDO); UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_TOGGLE); @@ -2650,6 +2667,22 @@ void FurnaceGUI::drawSettings() { } ImGui::Unindent(); + ImGui::Text("Export options layout:"); + ImGui::Indent(); + if (ImGui::RadioButton("Sub-menus in File menu##eol0",settings.exportOptionsLayout==0)) { + settings.exportOptionsLayout=0; + settingsChanged=true; + } + if (ImGui::RadioButton("Modal window with tabs##eol1",settings.exportOptionsLayout==1)) { + settings.exportOptionsLayout=1; + settingsChanged=true; + } + if (ImGui::RadioButton("Modal windows with options in File menu##eol2",settings.exportOptionsLayout==2)) { + settings.exportOptionsLayout=2; + settingsChanged=true; + } + ImGui::Unindent(); + bool capitalMenuBarB=settings.capitalMenuBar; if (ImGui::Checkbox("Capitalize menu bar",&capitalMenuBarB)) { settings.capitalMenuBar=capitalMenuBarB; @@ -3161,26 +3194,84 @@ void FurnaceGUI::drawSettings() { if (ImGui::Button("Reset defaults")) { showWarning("Are you sure you want to reset the color scheme?",GUI_WARN_RESET_COLORS); } - if (ImGui::TreeNode("General")) { - ImGui::Text("Color scheme type:"); - ImGui::Indent(); - if (ImGui::RadioButton("Dark##gcb0",settings.guiColorsBase==0)) { - settings.guiColorsBase=0; - applyUISettings(false); - settingsChanged=true; + bool basicColorsB=!settings.basicColors; + if (ImGui::Checkbox("Guru mode",&basicColorsB)) { + settings.basicColors=!basicColorsB; + applyUISettings(false); + settingsChanged=true; + } + if (settings.basicColors) { + if (ImGui::TreeNode("Interface")) { + if (ImGui::SliderInt("Frame shading",&settings.guiColorsShading,0,100,"%d%%")) { + if (settings.guiColorsShading<0) settings.guiColorsShading=0; + if (settings.guiColorsShading>100) settings.guiColorsShading=100; + applyUISettings(false); + settingsChanged=true; + } + ImGui::Text("Color scheme type:"); + ImGui::Indent(); + if (ImGui::RadioButton("Dark##gcb0",settings.guiColorsBase==0)) { + settings.guiColorsBase=0; + applyUISettings(false); + settingsChanged=true; + } + if (ImGui::RadioButton("Light##gcb1",settings.guiColorsBase==1)) { + settings.guiColorsBase=1; + applyUISettings(false); + settingsChanged=true; + } + ImGui::Unindent(); + + ImGui::Text("Accent colors:"); + ImGui::Indent(); + UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary"); + UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary"); + ImGui::Unindent(); + + ImGui::TreePop(); } - if (ImGui::RadioButton("Light##gcb1",settings.guiColorsBase==1)) { - settings.guiColorsBase=1; - applyUISettings(false); - settingsChanged=true; - } - ImGui::Unindent(); - if (ImGui::SliderInt("Frame shading",&settings.guiColorsShading,0,100,"%d%%")) { - if (settings.guiColorsShading<0) settings.guiColorsShading=0; - if (settings.guiColorsShading>100) settings.guiColorsShading=100; - applyUISettings(false); - settingsChanged=true; + } else { + if (ImGui::TreeNode("Interface")) { + if (ImGui::SliderInt("Frame shading",&settings.guiColorsShading,0,100,"%d%%")) { + if (settings.guiColorsShading<0) settings.guiColorsShading=0; + if (settings.guiColorsShading>100) settings.guiColorsShading=100; + applyUISettings(false); + settingsChanged=true; + } + + UI_COLOR_CONFIG(GUI_COLOR_BUTTON,"Button"); + UI_COLOR_CONFIG(GUI_COLOR_BUTTON_HOVER,"Button (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_BUTTON_ACTIVE,"Button (active)"); + UI_COLOR_CONFIG(GUI_COLOR_TAB,"Tab"); + UI_COLOR_CONFIG(GUI_COLOR_TAB_HOVER,"Tab (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_TAB_ACTIVE,"Tab (active)"); + UI_COLOR_CONFIG(GUI_COLOR_TAB_UNFOCUSED,"Tab (unfocused)"); + UI_COLOR_CONFIG(GUI_COLOR_TAB_UNFOCUSED_ACTIVE,"Tab (unfocused and active)"); + UI_COLOR_CONFIG(GUI_COLOR_IMGUI_HEADER,"ImGui header"); + UI_COLOR_CONFIG(GUI_COLOR_IMGUI_HEADER_HOVER,"ImGui header (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_IMGUI_HEADER_ACTIVE,"ImGui header (active)"); + UI_COLOR_CONFIG(GUI_COLOR_RESIZE_GRIP,"Resize grip"); + UI_COLOR_CONFIG(GUI_COLOR_RESIZE_GRIP_HOVER,"Resize grip (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_RESIZE_GRIP_ACTIVE,"Resize grip (active)"); + UI_COLOR_CONFIG(GUI_COLOR_WIDGET_BACKGROUND,"Widget background"); + UI_COLOR_CONFIG(GUI_COLOR_WIDGET_BACKGROUND_HOVER,"Widget background (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_WIDGET_BACKGROUND_ACTIVE,"Widget background (active)"); + UI_COLOR_CONFIG(GUI_COLOR_SLIDER_GRAB,"Slider grab"); + UI_COLOR_CONFIG(GUI_COLOR_SLIDER_GRAB_ACTIVE,"Slider grab (active)"); + UI_COLOR_CONFIG(GUI_COLOR_TITLE_BACKGROUND_ACTIVE,"Title background (active)"); + UI_COLOR_CONFIG(GUI_COLOR_CHECK_MARK,"Checkbox/radio button mark"); + UI_COLOR_CONFIG(GUI_COLOR_TEXT_SELECTION,"Text selection"); + UI_COLOR_CONFIG(GUI_COLOR_PLOT_LINES,"Line plot"); + UI_COLOR_CONFIG(GUI_COLOR_PLOT_LINES_HOVER,"Line plot (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_PLOT_HISTOGRAM,"Histogram plot"); + UI_COLOR_CONFIG(GUI_COLOR_PLOT_HISTOGRAM_HOVER,"Histogram plot (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_TABLE_ROW_EVEN,"Table row (even)"); + UI_COLOR_CONFIG(GUI_COLOR_TABLE_ROW_ODD,"Table row (odd)"); + + ImGui::TreePop(); } + } + if (ImGui::TreeNode("Interface (other)")) { UI_COLOR_CONFIG(GUI_COLOR_BACKGROUND,"Background"); UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND,"Window background"); UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_CHILD,"Sub-window background"); @@ -3188,8 +3279,7 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_MODAL_BACKDROP,"Modal backdrop"); UI_COLOR_CONFIG(GUI_COLOR_HEADER,"Header"); UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text"); - UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary"); - UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary"); + UI_COLOR_CONFIG(GUI_COLOR_TEXT_DISABLED,"Text (disabled)"); UI_COLOR_CONFIG(GUI_COLOR_TITLE_INACTIVE,"Title bar (inactive)"); UI_COLOR_CONFIG(GUI_COLOR_TITLE_COLLAPSED,"Title bar (collapsed)"); UI_COLOR_CONFIG(GUI_COLOR_MENU_BAR,"Menu bar"); @@ -3210,11 +3300,11 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_DRAG_DROP_TARGET,"Drag and drop target"); UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_HIGHLIGHT,"Window switcher (highlight)"); UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_BACKDROP,"Window switcher backdrop"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Miscellaneous")) { UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on"); UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off"); - UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing"); - UI_COLOR_CONFIG(GUI_COLOR_EDITING_CLONE,"Editing (will clone)"); - UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop"); UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status"); UI_COLOR_CONFIG(GUI_COLOR_DESTRUCTIVE,"Destructive hint"); UI_COLOR_CONFIG(GUI_COLOR_WARNING,"Warning hint"); @@ -3275,6 +3365,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::TreeNode("Orders")) { UI_COLOR_CONFIG(GUI_COLOR_ORDER_ROW_INDEX,"Order number"); UI_COLOR_CONFIG(GUI_COLOR_ORDER_ACTIVE,"Playing order background"); + UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop"); 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"); @@ -3387,6 +3478,8 @@ void FurnaceGUI::drawSettings() { } if (ImGui::TreeNode("Pattern")) { UI_COLOR_CONFIG(GUI_COLOR_PATTERN_PLAY_HEAD,"Playhead"); + UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing"); + UI_COLOR_CONFIG(GUI_COLOR_EDITING_CLONE,"Editing (will clone)"); UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR,"Cursor"); UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_HOVER,"Cursor (hovered)"); UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_ACTIVE,"Cursor (clicked)"); @@ -3609,207 +3702,327 @@ void FurnaceGUI::drawSettings() { x=maxV; \ } -void FurnaceGUI::syncSettings() { - settings.mainFontSize=e->getConfInt("mainFontSize",18); - settings.headFontSize=e->getConfInt("headFontSize",27); - settings.patFontSize=e->getConfInt("patFontSize",18); - settings.iconSize=e->getConfInt("iconSize",16); - settings.audioEngine=(e->getConfString("audioEngine","SDL")=="SDL")?1:0; - if (e->getConfString("audioEngine","SDL")=="JACK") { - settings.audioEngine=DIV_AUDIO_JACK; - } else if (e->getConfString("audioEngine","SDL")=="PortAudio") { - settings.audioEngine=DIV_AUDIO_PORTAUDIO; - } else { - settings.audioEngine=DIV_AUDIO_SDL; +void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { + if (groups&GUI_SETTINGS_GENERAL) { + settings.renderDriver=conf.getString("renderDriver",""); + settings.noDMFCompat=conf.getInt("noDMFCompat",0); + + settings.dpiScale=conf.getFloat("dpiScale",0.0f); + + settings.initialSysName=conf.getString("initialSysName",""); + + // initial system + String initialSys2=conf.getString("initialSys2",""); + bool oldVol=conf.getInt("configVersion",DIV_ENGINE_VERSION)<135; + if (initialSys2.empty()) { + initialSys2=e->decodeSysDesc(conf.getString("initialSys","")); + oldVol=false; + } + settings.initialSys.clear(); + settings.initialSys.loadFromBase64(initialSys2.c_str()); + if (settings.initialSys.getInt("id0",0)==0) { + settings.initialSys.clear(); + settings.initialSys.set("id0",e->systemToFileFur(DIV_SYSTEM_YM2612)); + settings.initialSys.set("vol0",1.0f); + settings.initialSys.set("pan0",0.0f); + settings.initialSys.set("fr0",0.0f); + settings.initialSys.set("flags0",""); + settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS)); + settings.initialSys.set("vol1",0.5f); + settings.initialSys.set("pan1",0); + settings.initialSys.set("fr1",0); + settings.initialSys.set("flags1",""); + } else { + if (oldVol) { + for (int i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) { + float newVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),64); + float newPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0); + newVol/=64.0f; + newPan/=127.0f; + settings.initialSys.set(fmt::sprintf("vol%d",i),newVol); + settings.initialSys.set(fmt::sprintf("pan%d",i),newPan); + } + conf.set("initialSys2",settings.initialSys.toBase64()); + conf.set("configVersion",DIV_ENGINE_VERSION); + } + } + + settings.noThreadedInput=conf.getInt("noThreadedInput",0); + settings.powerSave=conf.getInt("powerSave",POWER_SAVE_DEFAULT); + settings.eventDelay=conf.getInt("eventDelay",0); + + settings.renderBackend=conf.getString("renderBackend",GUI_BACKEND_DEFAULT_NAME); + settings.renderClearPos=conf.getInt("renderClearPos",0); + + settings.chanOscThreads=conf.getInt("chanOscThreads",0); + settings.renderPoolThreads=conf.getInt("renderPoolThreads",0); + settings.showPool=conf.getInt("showPool",0); + settings.writeInsNames=conf.getInt("writeInsNames",0); + settings.readInsNames=conf.getInt("readInsNames",1); + settings.defaultAuthorName=conf.getString("defaultAuthorName",""); + + settings.hiddenSystems=conf.getInt("hiddenSystems",0); + settings.allowEditDocking=conf.getInt("allowEditDocking",1); + settings.sysFileDialog=conf.getInt("sysFileDialog",SYS_FILE_DIALOG_DEFAULT); + settings.displayAllInsTypes=conf.getInt("displayAllInsTypes",0); + settings.displayPartial=conf.getInt("displayPartial",0); + + settings.blankIns=conf.getInt("blankIns",0); + + settings.saveWindowPos=conf.getInt("saveWindowPos",1); + + settings.saveUnusedPatterns=conf.getInt("saveUnusedPatterns",0); + settings.maxRecentFile=conf.getInt("maxRecentFile",10); + + settings.persistFadeOut=conf.getInt("persistFadeOut",1); + settings.exportLoops=conf.getInt("exportLoops",0); + settings.exportFadeOut=conf.getDouble("exportFadeOut",0.0); + + settings.doubleClickTime=conf.getFloat("doubleClickTime",0.3f); + settings.disableFadeIn=conf.getInt("disableFadeIn",0); + settings.alwaysPlayIntro=conf.getInt("alwaysPlayIntro",0); + settings.iCannotWait=conf.getInt("iCannotWait",0); + + settings.compress=conf.getInt("compress",1); + settings.newPatternFormat=conf.getInt("newPatternFormat",1); + settings.newSongBehavior=conf.getInt("newSongBehavior",0); + settings.playOnLoad=conf.getInt("playOnLoad",0); + settings.centerPopup=conf.getInt("centerPopup",1); + } + + if (groups&GUI_SETTINGS_AUDIO) { + settings.audioEngine=(conf.getString("audioEngine","SDL")=="SDL")?1:0; + if (conf.getString("audioEngine","SDL")=="JACK") { + settings.audioEngine=DIV_AUDIO_JACK; + } else if (conf.getString("audioEngine","SDL")=="PortAudio") { + settings.audioEngine=DIV_AUDIO_PORTAUDIO; + } else { + settings.audioEngine=DIV_AUDIO_SDL; + } + settings.audioDevice=conf.getString("audioDevice",""); + settings.sdlAudioDriver=conf.getString("sdlAudioDriver",""); + settings.audioQuality=conf.getInt("audioQuality",0); + settings.audioHiPass=conf.getInt("audioHiPass",1); + settings.audioBufSize=conf.getInt("audioBufSize",1024); + settings.audioRate=conf.getInt("audioRate",44100); + settings.audioChans=conf.getInt("audioChans",2); + + settings.lowLatency=conf.getInt("lowLatency",0); + + settings.metroVol=conf.getInt("metroVol",100); + settings.sampleVol=conf.getInt("sampleVol",50); + + settings.wasapiEx=conf.getInt("wasapiEx",0); + + settings.clampSamples=conf.getInt("clampSamples",0); + settings.forceMono=conf.getInt("forceMono",0); + } + + if (groups&GUI_SETTINGS_MIDI) { + settings.midiInDevice=conf.getString("midiInDevice",""); + settings.midiOutDevice=conf.getString("midiOutDevice",""); + settings.midiOutClock=conf.getInt("midiOutClock",0); + settings.midiOutTime=conf.getInt("midiOutTime",0); + settings.midiOutProgramChange=conf.getInt("midiOutProgramChange",0); + settings.midiOutMode=conf.getInt("midiOutMode",1); + settings.midiOutTimeRate=conf.getInt("midiOutTimeRate",0); + } + + if (groups&GUI_SETTINGS_KEYBOARD) { + // keybinds + for (int i=0; igetConfString("audioDevice",""); - settings.audioChans=e->getConfInt("audioChans",2); - settings.midiInDevice=e->getConfString("midiInDevice",""); - settings.midiOutDevice=e->getConfString("midiOutDevice",""); - settings.renderDriver=e->getConfString("renderDriver",""); - settings.sdlAudioDriver=e->getConfString("sdlAudioDriver",""); - settings.audioQuality=e->getConfInt("audioQuality",0); - settings.audioHiPass=e->getConfInt("audioHiPass",1); - settings.audioBufSize=e->getConfInt("audioBufSize",1024); - settings.audioRate=e->getConfInt("audioRate",44100); - settings.arcadeCore=e->getConfInt("arcadeCore",0); - settings.ym2612Core=e->getConfInt("ym2612Core",0); - settings.snCore=e->getConfInt("snCore",0); - settings.nesCore=e->getConfInt("nesCore",0); - settings.fdsCore=e->getConfInt("fdsCore",0); - settings.c64Core=e->getConfInt("c64Core",0); - settings.pokeyCore=e->getConfInt("pokeyCore",1); - settings.opnCore=e->getConfInt("opnCore",1); - settings.opl2Core=e->getConfInt("opl2Core",0); - settings.opl3Core=e->getConfInt("opl3Core",0); - settings.arcadeCoreRender=e->getConfInt("arcadeCoreRender",1); - settings.ym2612CoreRender=e->getConfInt("ym2612CoreRender",0); - settings.snCoreRender=e->getConfInt("snCoreRender",0); - settings.nesCoreRender=e->getConfInt("nesCoreRender",0); - settings.fdsCoreRender=e->getConfInt("fdsCoreRender",1); - settings.c64CoreRender=e->getConfInt("c64CoreRender",1); - settings.pokeyCoreRender=e->getConfInt("pokeyCoreRender",1); - settings.opnCoreRender=e->getConfInt("opnCoreRender",1); - settings.opl2CoreRender=e->getConfInt("opl2CoreRender",0); - settings.opl3CoreRender=e->getConfInt("opl3CoreRender",0); - settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); - settings.yrw801Path=e->getConfString("yrw801Path",""); - settings.tg100Path=e->getConfString("tg100Path",""); - settings.mu5Path=e->getConfString("mu5Path",""); - settings.mainFont=e->getConfInt("mainFont",0); - settings.headFont=e->getConfInt("headFont",0); - settings.patFont=e->getConfInt("patFont",0); - settings.mainFontPath=e->getConfString("mainFontPath",""); - settings.headFontPath=e->getConfString("headFontPath",""); - settings.patFontPath=e->getConfString("patFontPath",""); - settings.patRowsBase=e->getConfInt("patRowsBase",0); - settings.orderRowsBase=e->getConfInt("orderRowsBase",1); - settings.soloAction=e->getConfInt("soloAction",0); - settings.pullDeleteBehavior=e->getConfInt("pullDeleteBehavior",1); - settings.wrapHorizontal=e->getConfInt("wrapHorizontal",0); - settings.wrapVertical=e->getConfInt("wrapVertical",0); - settings.macroView=e->getConfInt("macroView",0); - settings.fmNames=e->getConfInt("fmNames",0); - settings.allowEditDocking=e->getConfInt("allowEditDocking",1); - settings.chipNames=e->getConfInt("chipNames",0); - settings.overflowHighlight=e->getConfInt("overflowHighlight",0); - settings.partyTime=e->getConfInt("partyTime",0); - settings.flatNotes=e->getConfInt("flatNotes",0); - settings.germanNotation=e->getConfInt("germanNotation",0); - settings.stepOnDelete=e->getConfInt("stepOnDelete",0); - settings.scrollStep=e->getConfInt("scrollStep",0); - settings.sysSeparators=e->getConfInt("sysSeparators",1); - settings.forceMono=e->getConfInt("forceMono",0); - settings.controlLayout=e->getConfInt("controlLayout",3); - settings.statusDisplay=e->getConfInt("statusDisplay",0); - settings.dpiScale=e->getConfFloat("dpiScale",0.0f); - settings.viewPrevPattern=e->getConfInt("viewPrevPattern",1); - settings.guiColorsBase=e->getConfInt("guiColorsBase",0); - settings.guiColorsShading=e->getConfInt("guiColorsShading",0); - settings.avoidRaisingPattern=e->getConfInt("avoidRaisingPattern",0); - settings.insFocusesPattern=e->getConfInt("insFocusesPattern",1); - settings.stepOnInsert=e->getConfInt("stepOnInsert",0); - settings.unifiedDataView=e->getConfInt("unifiedDataView",0); - settings.sysFileDialog=e->getConfInt("sysFileDialog",SYS_FILE_DIALOG_DEFAULT); - settings.roundedWindows=e->getConfInt("roundedWindows",1); - settings.roundedButtons=e->getConfInt("roundedButtons",1); - settings.roundedMenus=e->getConfInt("roundedMenus",0); - settings.loadJapanese=e->getConfInt("loadJapanese",0); - settings.loadChinese=e->getConfInt("loadChinese",0); - settings.loadChineseTraditional=e->getConfInt("loadChineseTraditional",0); - settings.loadKorean=e->getConfInt("loadKorean",0); - settings.fmLayout=e->getConfInt("fmLayout",4); - settings.sampleLayout=e->getConfInt("sampleLayout",0); - settings.waveLayout=e->getConfInt("waveLayout",0); - settings.susPosition=e->getConfInt("susPosition",0); - settings.effectCursorDir=e->getConfInt("effectCursorDir",1); - settings.cursorPastePos=e->getConfInt("cursorPastePos",1); - settings.titleBarInfo=e->getConfInt("titleBarInfo",1); - settings.titleBarSys=e->getConfInt("titleBarSys",1); - settings.frameBorders=e->getConfInt("frameBorders",0); - settings.effectDeletionAltersValue=e->getConfInt("effectDeletionAltersValue",1); - settings.oscRoundedCorners=e->getConfInt("oscRoundedCorners",1); - settings.oscTakesEntireWindow=e->getConfInt("oscTakesEntireWindow",0); - settings.oscBorder=e->getConfInt("oscBorder",1); - settings.oscEscapesBoundary=e->getConfInt("oscEscapesBoundary",0); - settings.oscMono=e->getConfInt("oscMono",1); - settings.oscAntiAlias=e->getConfInt("oscAntiAlias",1); - settings.separateFMColors=e->getConfInt("separateFMColors",0); - settings.insEditColorize=e->getConfInt("insEditColorize",0); - settings.metroVol=e->getConfInt("metroVol",100); - settings.sampleVol=e->getConfInt("sampleVol",50); - settings.pushNibble=e->getConfInt("pushNibble",0); - settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0); - settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0); - settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); - settings.lowLatency=e->getConfInt("lowLatency",0); - settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); - settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); - settings.absorbInsInput=e->getConfInt("absorbInsInput",0); - settings.eventDelay=e->getConfInt("eventDelay",0); - settings.moveWindowTitle=e->getConfInt("moveWindowTitle",1); - settings.hiddenSystems=e->getConfInt("hiddenSystems",0); - settings.horizontalDataView=e->getConfInt("horizontalDataView",0); - settings.noMultiSystem=e->getConfInt("noMultiSystem",0); - settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0); - settings.displayAllInsTypes=e->getConfInt("displayAllInsTypes",0); - settings.displayPartial=e->getConfInt("displayPartial",0); - settings.noteCellSpacing=e->getConfInt("noteCellSpacing",0); - settings.insCellSpacing=e->getConfInt("insCellSpacing",0); - settings.volCellSpacing=e->getConfInt("volCellSpacing",0); - settings.effectCellSpacing=e->getConfInt("effectCellSpacing",0); - settings.effectValCellSpacing=e->getConfInt("effectValCellSpacing",0); - settings.doubleClickColumn=e->getConfInt("doubleClickColumn",1); - settings.blankIns=e->getConfInt("blankIns",0); - settings.dragMovesSelection=e->getConfInt("dragMovesSelection",2); - settings.unsignedDetune=e->getConfInt("unsignedDetune",0); - settings.noThreadedInput=e->getConfInt("noThreadedInput",0); - settings.saveWindowPos=e->getConfInt("saveWindowPos",1); - settings.initialSysName=e->getConfString("initialSysName",""); - settings.clampSamples=e->getConfInt("clampSamples",0); - settings.noteOffLabel=e->getConfString("noteOffLabel","OFF"); - settings.noteRelLabel=e->getConfString("noteRelLabel","==="); - settings.macroRelLabel=e->getConfString("macroRelLabel","REL"); - settings.emptyLabel=e->getConfString("emptyLabel","..."); - settings.emptyLabel2=e->getConfString("emptyLabel2",".."); - settings.saveUnusedPatterns=e->getConfInt("saveUnusedPatterns",0); - settings.channelColors=e->getConfInt("channelColors",1); - settings.channelTextColors=e->getConfInt("channelTextColors",0); - settings.channelStyle=e->getConfInt("channelStyle",1); - settings.channelVolStyle=e->getConfInt("channelVolStyle",0); - settings.channelFeedbackStyle=e->getConfInt("channelFeedbackStyle",1); - settings.channelFont=e->getConfInt("channelFont",1); - settings.channelTextCenter=e->getConfInt("channelTextCenter",1); - settings.maxRecentFile=e->getConfInt("maxRecentFile",10); - settings.midiOutClock=e->getConfInt("midiOutClock",0); - settings.midiOutTime=e->getConfInt("midiOutTime",0); - settings.midiOutProgramChange=e->getConfInt("midiOutProgramChange",0); - settings.midiOutMode=e->getConfInt("midiOutMode",1); - settings.midiOutTimeRate=e->getConfInt("midiOutTimeRate",0); - settings.centerPattern=e->getConfInt("centerPattern",0); - settings.ordersCursor=e->getConfInt("ordersCursor",1); - settings.persistFadeOut=e->getConfInt("persistFadeOut",1); - settings.exportLoops=e->getConfInt("exportLoops",0); - settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0); - settings.macroLayout=e->getConfInt("macroLayout",0); - settings.doubleClickTime=e->getConfFloat("doubleClickTime",0.3f); - settings.oneDigitEffects=e->getConfInt("oneDigitEffects",0); - settings.disableFadeIn=e->getConfInt("disableFadeIn",0); - settings.alwaysPlayIntro=e->getConfInt("alwaysPlayIntro",0); - settings.cursorFollowsOrder=e->getConfInt("cursorFollowsOrder",1); - settings.iCannotWait=e->getConfInt("iCannotWait",0); - settings.orderButtonPos=e->getConfInt("orderButtonPos",2); - settings.compress=e->getConfInt("compress",1); - settings.newPatternFormat=e->getConfInt("newPatternFormat",1); - settings.renderBackend=e->getConfString("renderBackend",GUI_BACKEND_DEFAULT_NAME); - settings.renderClearPos=e->getConfInt("renderClearPos",0); - settings.insertBehavior=e->getConfInt("insertBehavior",1); - settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1); - settings.newSongBehavior=e->getConfInt("newSongBehavior",0); - settings.memUsageUnit=e->getConfInt("memUsageUnit",1); - settings.cursorFollowsWheel=e->getConfInt("cursorFollowsWheel",0); - settings.noDMFCompat=e->getConfInt("noDMFCompat",0); - settings.removeInsOff=e->getConfInt("removeInsOff",0); - settings.removeVolOff=e->getConfInt("removeVolOff",0); - settings.playOnLoad=e->getConfInt("playOnLoad",0); - settings.insTypeMenu=e->getConfInt("insTypeMenu",1); - settings.capitalMenuBar=e->getConfInt("capitalMenuBar",0); - settings.centerPopup=e->getConfInt("centerPopup",1); - settings.insIconsStyle=e->getConfInt("insIconsStyle",1); - settings.classicChipOptions=e->getConfInt("classicChipOptions",0); - settings.wasapiEx=e->getConfInt("wasapiEx",0); - settings.chanOscThreads=e->getConfInt("chanOscThreads",0); - settings.renderPoolThreads=e->getConfInt("renderPoolThreads",0); - settings.showPool=e->getConfInt("showPool",0); - settings.writeInsNames=e->getConfInt("writeInsNames",1); - settings.readInsNames=e->getConfInt("readInsNames",1); - settings.defaultAuthorName=e->getConfString("defaultAuthorName",""); - settings.fontBackend=e->getConfInt("fontBackend",FONT_BACKEND_DEFAULT); - settings.fontHinting=e->getConfInt("fontHinting",0); - settings.fontBitmap=e->getConfInt("fontBitmap",0); - settings.fontAutoHint=e->getConfInt("fontAutoHint",1); - settings.fontAntiAlias=e->getConfInt("fontAntiAlias",1); - settings.selectAssetOnLoad=e->getConfInt("selectAssetOnLoad",1); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.headFontSize,2,96); @@ -3850,7 +4063,6 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.pullDeleteBehavior,0,1); clampSetting(settings.wrapHorizontal,0,2); clampSetting(settings.wrapVertical,0,3); - clampSetting(settings.macroView,0,1); clampSetting(settings.fmNames,0,2); clampSetting(settings.allowEditDocking,0,1); clampSetting(settings.chipNames,0,1); @@ -3964,6 +4176,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.centerPopup,0,1); clampSetting(settings.insIconsStyle,0,2); clampSetting(settings.classicChipOptions,0,1); + clampSetting(settings.exportOptionsLayout,0,2); clampSetting(settings.wasapiEx,0,1); clampSetting(settings.chanOscThreads,0,256); clampSetting(settings.renderPoolThreads,0,DIV_MAX_CHIPS); @@ -3976,52 +4189,305 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.fontAutoHint,0,2); clampSetting(settings.fontAntiAlias,0,1); clampSetting(settings.selectAssetOnLoad,0,1); + clampSetting(settings.basicColors,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; - if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; + if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; +} - String initialSys2=e->getConfString("initialSys2",""); - bool oldVol=e->getConfInt("configVersion",DIV_ENGINE_VERSION)<135; - if (initialSys2.empty()) { - initialSys2=e->decodeSysDesc(e->getConfString("initialSys","")); - oldVol=false; +void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { + // general + if (groups&GUI_SETTINGS_GENERAL) { + conf.set("renderDriver",settings.renderDriver); + conf.set("noDMFCompat",settings.noDMFCompat); + + conf.set("dpiScale",settings.dpiScale); + + conf.set("initialSys2",settings.initialSys.toBase64()); + conf.set("initialSysName",settings.initialSysName); + + conf.set("noThreadedInput",settings.noThreadedInput); + conf.set("powerSave",settings.powerSave); + conf.set("eventDelay",settings.eventDelay); + + conf.set("renderBackend",settings.renderBackend); + conf.set("renderClearPos",settings.renderClearPos); + + conf.set("chanOscThreads",settings.chanOscThreads); + conf.set("renderPoolThreads",settings.renderPoolThreads); + conf.set("showPool",settings.showPool); + conf.set("writeInsNames",settings.writeInsNames); + conf.set("readInsNames",settings.readInsNames); + conf.set("defaultAuthorName",settings.defaultAuthorName); + + conf.set("hiddenSystems",settings.hiddenSystems); + conf.set("allowEditDocking",settings.allowEditDocking); + conf.set("sysFileDialog",settings.sysFileDialog); + conf.set("displayAllInsTypes",settings.displayAllInsTypes); + conf.set("displayPartial",settings.displayPartial); + + conf.set("blankIns",settings.blankIns); + + conf.set("saveWindowPos",settings.saveWindowPos); + + conf.set("saveUnusedPatterns",settings.saveUnusedPatterns); + conf.set("maxRecentFile",settings.maxRecentFile); + + conf.set("persistFadeOut",settings.persistFadeOut); + conf.set("exportLoops",settings.exportLoops); + conf.set("exportFadeOut",settings.exportFadeOut); + + conf.set("doubleClickTime",settings.doubleClickTime); + conf.set("disableFadeIn",settings.disableFadeIn); + conf.set("alwaysPlayIntro",settings.alwaysPlayIntro); + conf.set("iCannotWait",settings.iCannotWait); + + conf.set("compress",settings.compress); + conf.set("newPatternFormat",settings.newPatternFormat); + conf.set("newSongBehavior",settings.newSongBehavior); + conf.set("playOnLoad",settings.playOnLoad); + conf.set("centerPopup",settings.centerPopup); } - settings.initialSys.clear(); - settings.initialSys.loadFromBase64(initialSys2.c_str()); - if (settings.initialSys.getInt("id0",0)==0) { - settings.initialSys.clear(); - settings.initialSys.set("id0",e->systemToFileFur(DIV_SYSTEM_YM2612)); - settings.initialSys.set("vol0",1.0f); - settings.initialSys.set("pan0",0.0f); - settings.initialSys.set("fr0",0.0f); - settings.initialSys.set("flags0",""); - settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS)); - settings.initialSys.set("vol1",0.5f); - settings.initialSys.set("pan1",0); - settings.initialSys.set("fr1",0); - settings.initialSys.set("flags1",""); - } else { - if (oldVol) { - for (int i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) { - float newVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),64); - float newPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0); - newVol/=64.0f; - newPan/=127.0f; - settings.initialSys.set(fmt::sprintf("vol%d",i),newVol); - settings.initialSys.set(fmt::sprintf("pan%d",i),newPan); - } - e->setConf("initialSys2",settings.initialSys.toBase64()); - e->setConf("configVersion",DIV_ENGINE_VERSION); + + // audio + if (groups&GUI_SETTINGS_AUDIO) { + conf.set("audioEngine",String(audioBackends[settings.audioEngine])); + conf.set("audioDevice",settings.audioDevice); + conf.set("sdlAudioDriver",settings.sdlAudioDriver); + conf.set("audioQuality",settings.audioQuality); + conf.set("audioHiPass",settings.audioHiPass); + conf.set("audioBufSize",settings.audioBufSize); + conf.set("audioRate",settings.audioRate); + conf.set("audioChans",settings.audioChans); + + conf.set("lowLatency",settings.lowLatency); + + conf.set("metroVol",settings.metroVol); + conf.set("sampleVol",settings.sampleVol); + + conf.set("wasapiEx",settings.wasapiEx); + + conf.set("clampSamples",settings.clampSamples); + conf.set("forceMono",settings.forceMono); + } + + // MIDI + if (groups&GUI_SETTINGS_MIDI) { + conf.set("midiInDevice",settings.midiInDevice); + conf.set("midiOutDevice",settings.midiOutDevice); + conf.set("midiOutClock",settings.midiOutClock); + conf.set("midiOutTime",settings.midiOutTime); + conf.set("midiOutProgramChange",settings.midiOutProgramChange); + conf.set("midiOutMode",settings.midiOutMode); + conf.set("midiOutTimeRate",settings.midiOutTimeRate); + } + + // keyboard + if (groups&GUI_SETTINGS_KEYBOARD) { + // keybinds + for (int i=0; igetConfInt(String("keybind_GUI_ACTION_")+String(guiActions[i].name),guiActions[i].defaultBind); - } + // emulation + if (groups&GUI_SETTINGS_EMULATION) { + conf.set("arcadeCore",settings.arcadeCore); + conf.set("ym2612Core",settings.ym2612Core); + conf.set("snCore",settings.snCore); + conf.set("nesCore",settings.nesCore); + conf.set("fdsCore",settings.fdsCore); + conf.set("c64Core",settings.c64Core); + conf.set("pokeyCore",settings.pokeyCore); + conf.set("opnCore",settings.opnCore); + conf.set("opl2Core",settings.opl2Core); + conf.set("opl3Core",settings.opl3Core); + conf.set("arcadeCoreRender",settings.arcadeCoreRender); + conf.set("ym2612CoreRender",settings.ym2612CoreRender); + conf.set("snCoreRender",settings.snCoreRender); + conf.set("nesCoreRender",settings.nesCoreRender); + conf.set("fdsCoreRender",settings.fdsCoreRender); + conf.set("c64CoreRender",settings.c64CoreRender); + conf.set("pokeyCoreRender",settings.pokeyCoreRender); + conf.set("opnCoreRender",settings.opnCoreRender); + conf.set("opl2CoreRender",settings.opl2CoreRender); + conf.set("opl3CoreRender",settings.opl3CoreRender); - decodeKeyMap(noteKeys,e->getConfString("noteKeys",DEFAULT_NOTE_KEYS)); + conf.set("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); + + conf.set("yrw801Path",settings.yrw801Path); + conf.set("tg100Path",settings.tg100Path); + conf.set("mu5Path",settings.mu5Path); + } +} + +void FurnaceGUI::syncSettings() { + readConfig(e->getConfObject()); parseKeybinds(); @@ -4029,6 +4495,7 @@ void FurnaceGUI::syncSettings() { midiMap.compile(); e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); e->setMidiVolExp(midiMap.volExp); e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f); @@ -4064,216 +4531,10 @@ void FurnaceGUI::commitSettings() { settings.audioHiPass!=e->getConfInt("audioHiPass",1) ); - e->setConf("mainFontSize",settings.mainFontSize); - e->setConf("headFontSize",settings.headFontSize); - e->setConf("patFontSize",settings.patFontSize); - e->setConf("iconSize",settings.iconSize); - e->setConf("audioEngine",String(audioBackends[settings.audioEngine])); - e->setConf("audioDevice",settings.audioDevice); - e->setConf("midiInDevice",settings.midiInDevice); - e->setConf("midiOutDevice",settings.midiOutDevice); - e->setConf("renderDriver",settings.renderDriver); - e->setConf("sdlAudioDriver",settings.sdlAudioDriver); - e->setConf("audioQuality",settings.audioQuality); - e->setConf("audioHiPass",settings.audioHiPass); - e->setConf("audioBufSize",settings.audioBufSize); - e->setConf("audioRate",settings.audioRate); - e->setConf("audioChans",settings.audioChans); - e->setConf("arcadeCore",settings.arcadeCore); - e->setConf("ym2612Core",settings.ym2612Core); - e->setConf("snCore",settings.snCore); - e->setConf("nesCore",settings.nesCore); - e->setConf("fdsCore",settings.fdsCore); - e->setConf("c64Core",settings.c64Core); - e->setConf("pokeyCore",settings.pokeyCore); - e->setConf("opnCore",settings.opnCore); - e->setConf("opl2Core",settings.opl2Core); - e->setConf("opl3Core",settings.opl3Core); - e->setConf("arcadeCoreRender",settings.arcadeCoreRender); - e->setConf("ym2612CoreRender",settings.ym2612CoreRender); - e->setConf("snCoreRender",settings.snCoreRender); - e->setConf("nesCoreRender",settings.nesCoreRender); - e->setConf("fdsCoreRender",settings.fdsCoreRender); - e->setConf("c64CoreRender",settings.c64CoreRender); - e->setConf("pokeyCoreRender",settings.pokeyCoreRender); - e->setConf("opnCoreRender",settings.opnCoreRender); - e->setConf("opl2CoreRender",settings.opl2CoreRender); - e->setConf("opl3CoreRender",settings.opl3CoreRender); - e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); - e->setConf("yrw801Path",settings.yrw801Path); - e->setConf("tg100Path",settings.tg100Path); - e->setConf("mu5Path",settings.mu5Path); - e->setConf("mainFont",settings.mainFont); - e->setConf("headFont",settings.headFont); - e->setConf("patFont",settings.patFont); - e->setConf("mainFontPath",settings.mainFontPath); - e->setConf("headFontPath",settings.headFontPath); - e->setConf("patFontPath",settings.patFontPath); - e->setConf("patRowsBase",settings.patRowsBase); - e->setConf("orderRowsBase",settings.orderRowsBase); - e->setConf("soloAction",settings.soloAction); - e->setConf("pullDeleteBehavior",settings.pullDeleteBehavior); - e->setConf("wrapHorizontal",settings.wrapHorizontal); - e->setConf("wrapVertical",settings.wrapVertical); - e->setConf("macroView",settings.macroView); - e->setConf("fmNames",settings.fmNames); - e->setConf("allowEditDocking",settings.allowEditDocking); - e->setConf("chipNames",settings.chipNames); - e->setConf("overflowHighlight",settings.overflowHighlight); - e->setConf("partyTime",settings.partyTime); - e->setConf("flatNotes",settings.flatNotes); - e->setConf("germanNotation",settings.germanNotation); - e->setConf("stepOnDelete",settings.stepOnDelete); - e->setConf("scrollStep",settings.scrollStep); - e->setConf("sysSeparators",settings.sysSeparators); - e->setConf("forceMono",settings.forceMono); - e->setConf("controlLayout",settings.controlLayout); - e->setConf("statusDisplay",settings.statusDisplay); - e->setConf("dpiScale",settings.dpiScale); - e->setConf("viewPrevPattern",settings.viewPrevPattern); - e->setConf("guiColorsBase",settings.guiColorsBase); - e->setConf("guiColorsShading",settings.guiColorsShading); - e->setConf("avoidRaisingPattern",settings.avoidRaisingPattern); - e->setConf("insFocusesPattern",settings.insFocusesPattern); - e->setConf("stepOnInsert",settings.stepOnInsert); - e->setConf("unifiedDataView",settings.unifiedDataView); - e->setConf("sysFileDialog",settings.sysFileDialog); - e->setConf("roundedWindows",settings.roundedWindows); - e->setConf("roundedButtons",settings.roundedButtons); - e->setConf("roundedMenus",settings.roundedMenus); - e->setConf("loadJapanese",settings.loadJapanese); - e->setConf("loadChinese",settings.loadChinese); - e->setConf("loadChineseTraditional",settings.loadChineseTraditional); - e->setConf("loadKorean",settings.loadKorean); - e->setConf("fmLayout",settings.fmLayout); - e->setConf("sampleLayout",settings.sampleLayout); - e->setConf("waveLayout",settings.waveLayout); - e->setConf("susPosition",settings.susPosition); - e->setConf("effectCursorDir",settings.effectCursorDir); - e->setConf("cursorPastePos",settings.cursorPastePos); - e->setConf("titleBarInfo",settings.titleBarInfo); - e->setConf("titleBarSys",settings.titleBarSys); - e->setConf("frameBorders",settings.frameBorders); - e->setConf("effectDeletionAltersValue",settings.effectDeletionAltersValue); - e->setConf("oscRoundedCorners",settings.oscRoundedCorners); - e->setConf("oscTakesEntireWindow",settings.oscTakesEntireWindow); - e->setConf("oscBorder",settings.oscBorder); - e->setConf("oscEscapesBoundary",settings.oscEscapesBoundary); - e->setConf("oscMono",settings.oscMono); - e->setConf("oscAntiAlias",settings.oscAntiAlias); - e->setConf("separateFMColors",settings.separateFMColors); - e->setConf("insEditColorize",settings.insEditColorize); - e->setConf("metroVol",settings.metroVol); - e->setConf("sampleVol",settings.sampleVol); - e->setConf("pushNibble",settings.pushNibble); - e->setConf("scrollChangesOrder",settings.scrollChangesOrder); - e->setConf("oplStandardWaveNames",settings.oplStandardWaveNames); - e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); - e->setConf("lowLatency",settings.lowLatency); - e->setConf("notePreviewBehavior",settings.notePreviewBehavior); - e->setConf("powerSave",settings.powerSave); - e->setConf("absorbInsInput",settings.absorbInsInput); - e->setConf("eventDelay",settings.eventDelay); - e->setConf("moveWindowTitle",settings.moveWindowTitle); - e->setConf("hiddenSystems",settings.hiddenSystems); - e->setConf("initialSys2",settings.initialSys.toBase64()); - e->setConf("initialSysName",settings.initialSysName); - e->setConf("horizontalDataView",settings.horizontalDataView); - e->setConf("noMultiSystem",settings.noMultiSystem); - e->setConf("oldMacroVSlider",settings.oldMacroVSlider); - e->setConf("displayAllInsTypes",settings.displayAllInsTypes); - e->setConf("displayPartial",settings.displayPartial); - e->setConf("noteCellSpacing",settings.noteCellSpacing); - e->setConf("insCellSpacing",settings.insCellSpacing); - e->setConf("volCellSpacing",settings.volCellSpacing); - e->setConf("effectCellSpacing",settings.effectCellSpacing); - e->setConf("effectValCellSpacing",settings.effectValCellSpacing); - e->setConf("doubleClickColumn",settings.doubleClickColumn); - e->setConf("blankIns",settings.blankIns); - e->setConf("dragMovesSelection",settings.dragMovesSelection); - e->setConf("unsignedDetune",settings.unsignedDetune); - e->setConf("noThreadedInput",settings.noThreadedInput); - e->setConf("saveWindowPos",settings.saveWindowPos); - e->setConf("clampSamples",settings.clampSamples); - e->setConf("noteOffLabel",settings.noteOffLabel); - e->setConf("noteRelLabel",settings.noteRelLabel); - e->setConf("macroRelLabel",settings.macroRelLabel); - e->setConf("emptyLabel",settings.emptyLabel); - e->setConf("emptyLabel2",settings.emptyLabel2); - e->setConf("saveUnusedPatterns",settings.saveUnusedPatterns); - e->setConf("channelColors",settings.channelColors); - e->setConf("channelTextColors",settings.channelTextColors); - e->setConf("channelStyle",settings.channelStyle); - e->setConf("channelVolStyle",settings.channelVolStyle); - e->setConf("channelFeedbackStyle",settings.channelFeedbackStyle); - e->setConf("channelFont",settings.channelFont); - e->setConf("channelTextCenter",settings.channelTextCenter); - e->setConf("maxRecentFile",settings.maxRecentFile); - e->setConf("midiOutClock",settings.midiOutClock); - e->setConf("midiOutTime",settings.midiOutTime); - e->setConf("midiOutProgramChange",settings.midiOutProgramChange); - e->setConf("midiOutMode",settings.midiOutMode); - e->setConf("midiOutTimeRate",settings.midiOutTimeRate); - e->setConf("centerPattern",settings.centerPattern); - e->setConf("ordersCursor",settings.ordersCursor); - e->setConf("persistFadeOut",settings.persistFadeOut); - e->setConf("exportLoops",settings.exportLoops); - e->setConf("exportFadeOut",settings.exportFadeOut); - e->setConf("macroLayout",settings.macroLayout); - e->setConf("doubleClickTime",settings.doubleClickTime); - e->setConf("oneDigitEffects",settings.oneDigitEffects); - e->setConf("disableFadeIn",settings.disableFadeIn); - e->setConf("alwaysPlayIntro",settings.alwaysPlayIntro); - e->setConf("cursorFollowsOrder",settings.cursorFollowsOrder); - e->setConf("iCannotWait",settings.iCannotWait); - e->setConf("orderButtonPos",settings.orderButtonPos); - e->setConf("compress",settings.compress); - e->setConf("newPatternFormat",settings.newPatternFormat); - e->setConf("renderBackend",settings.renderBackend); - e->setConf("renderClearPos",settings.renderClearPos); - e->setConf("insertBehavior",settings.insertBehavior); - e->setConf("pullDeleteRow",settings.pullDeleteRow); - e->setConf("newSongBehavior",settings.newSongBehavior); - e->setConf("memUsageUnit",settings.memUsageUnit); - e->setConf("cursorFollowsWheel",settings.cursorFollowsWheel); - e->setConf("noDMFCompat",settings.noDMFCompat); - e->setConf("removeInsOff",settings.removeInsOff); - e->setConf("removeVolOff",settings.removeVolOff); - e->setConf("playOnLoad",settings.playOnLoad); - e->setConf("insTypeMenu",settings.insTypeMenu); - e->setConf("capitalMenuBar",settings.capitalMenuBar); - e->setConf("centerPopup",settings.centerPopup); - e->setConf("insIconsStyle",settings.insIconsStyle); - e->setConf("classicChipOptions",settings.classicChipOptions); - e->setConf("wasapiEx",settings.wasapiEx); - e->setConf("chanOscThreads",settings.chanOscThreads); - e->setConf("renderPoolThreads",settings.renderPoolThreads); - e->setConf("showPool",settings.showPool); - e->setConf("writeInsNames",settings.writeInsNames); - e->setConf("readInsNames",settings.readInsNames); - e->setConf("defaultAuthorName",settings.defaultAuthorName); - e->setConf("fontBackend",settings.fontBackend); - e->setConf("fontHinting",settings.fontHinting); - e->setConf("fontBitmap",settings.fontBitmap); - e->setConf("fontAutoHint",settings.fontAutoHint); - e->setConf("fontAntiAlias",settings.fontAntiAlias); - e->setConf("selectAssetOnLoad",settings.selectAssetOnLoad); - - // colors - for (int i=0; isetConf(guiColors[i].name,(int)ImGui::ColorConvertFloat4ToU32(uiColors[i])); - } - - // keybinds - for (int i=0; isetConf(String("keybind_GUI_ACTION_")+String(guiActions[i].name),actionKeys[i]); - } + writeConfig(e->getConfObject()); parseKeybinds(); - e->setConf("noteKeys",encodeKeyMap(noteKeys)); - midiMap.compile(); midiMap.write(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); @@ -4320,69 +4581,36 @@ void FurnaceGUI::commitSettings() { } bool FurnaceGUI::importColors(String path) { - FILE* f=ps_fopen(path.c_str(),"rb"); - if (f==NULL) { + DivConfig c; + if (!c.loadFromFile(path.c_str(),false,false)) { logW("error while opening color file for import: %s",strerror(errno)); return false; } - resetColors(); - char line[4096]; - while (!feof(f)) { - String key=""; - String value=""; - bool keyOrValue=false; - if (fgets(line,4095,f)==NULL) { - break; - } - for (char* i=line; *i; i++) { - if (*i=='\n') continue; - if (keyOrValue) { - value+=*i; - } else { - if (*i=='=') { - keyOrValue=true; - } else { - key+=*i; - } - } - } - if (keyOrValue) { - // unoptimal - const char* cs=key.c_str(); - bool found=false; - for (int i=0; igetConfInt(guiColors[i].name,guiColors[i].defaultColor)); - } - } - for (int i=0; i<64; i++) { ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN]; ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF]; @@ -4838,6 +5059,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { secondaryHoverActual=secondaryHover; + // TODO: improve if (mobileUI) { // disable all hovered colors primaryHover=primary; secondaryHover=secondary; @@ -4867,31 +5089,63 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { sty.Colors[ImGuiCol_NavWindowingHighlight]=uiColors[GUI_COLOR_NAV_WIN_HIGHLIGHT]; sty.Colors[ImGuiCol_NavWindowingDimBg]=uiColors[GUI_COLOR_NAV_WIN_BACKDROP]; sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT]; + sty.Colors[ImGuiCol_TextDisabled]=uiColors[GUI_COLOR_TEXT_DISABLED]; - sty.Colors[ImGuiCol_Button]=primary; - sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; - sty.Colors[ImGuiCol_ButtonActive]=primaryActive; - sty.Colors[ImGuiCol_Tab]=primary; - sty.Colors[ImGuiCol_TabHovered]=secondaryHover; - sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; - sty.Colors[ImGuiCol_TabUnfocused]=primary; - sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; - sty.Colors[ImGuiCol_Header]=secondary; - sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; - sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; - sty.Colors[ImGuiCol_ResizeGrip]=secondary; - sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; - sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; - sty.Colors[ImGuiCol_FrameBg]=secondary; - sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; - sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; - sty.Colors[ImGuiCol_SliderGrab]=primaryActive; - sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; - sty.Colors[ImGuiCol_TitleBgActive]=primary; - sty.Colors[ImGuiCol_CheckMark]=primaryActive; - sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHoverActual; - sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; - sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; + if (settings.basicColors) { + sty.Colors[ImGuiCol_Button]=primary; + sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; + sty.Colors[ImGuiCol_ButtonActive]=primaryActive; + sty.Colors[ImGuiCol_Tab]=primary; + sty.Colors[ImGuiCol_TabHovered]=secondaryHover; + sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; + sty.Colors[ImGuiCol_TabUnfocused]=primary; + sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; + sty.Colors[ImGuiCol_Header]=secondary; + sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; + sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; + sty.Colors[ImGuiCol_ResizeGrip]=secondary; + sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; + sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; + sty.Colors[ImGuiCol_FrameBg]=secondary; + sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; + sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; + sty.Colors[ImGuiCol_SliderGrab]=primaryActive; + sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; + sty.Colors[ImGuiCol_TitleBgActive]=primary; + sty.Colors[ImGuiCol_CheckMark]=primaryActive; + sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHoverActual; + sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; + sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; + } else { + sty.Colors[ImGuiCol_Button]=uiColors[GUI_COLOR_BUTTON]; + sty.Colors[ImGuiCol_ButtonHovered]=uiColors[GUI_COLOR_BUTTON_HOVER]; + sty.Colors[ImGuiCol_ButtonActive]=uiColors[GUI_COLOR_BUTTON_ACTIVE]; + sty.Colors[ImGuiCol_Tab]=uiColors[GUI_COLOR_TAB]; + sty.Colors[ImGuiCol_TabHovered]=uiColors[GUI_COLOR_TAB_HOVER]; + sty.Colors[ImGuiCol_TabActive]=uiColors[GUI_COLOR_TAB_ACTIVE]; + sty.Colors[ImGuiCol_TabUnfocused]=uiColors[GUI_COLOR_TAB_UNFOCUSED]; + sty.Colors[ImGuiCol_TabUnfocusedActive]=uiColors[GUI_COLOR_TAB_UNFOCUSED_ACTIVE]; + sty.Colors[ImGuiCol_Header]=uiColors[GUI_COLOR_IMGUI_HEADER]; + sty.Colors[ImGuiCol_HeaderHovered]=uiColors[GUI_COLOR_IMGUI_HEADER_HOVER]; + sty.Colors[ImGuiCol_HeaderActive]=uiColors[GUI_COLOR_IMGUI_HEADER_ACTIVE]; + sty.Colors[ImGuiCol_ResizeGrip]=uiColors[GUI_COLOR_RESIZE_GRIP]; + sty.Colors[ImGuiCol_ResizeGripHovered]=uiColors[GUI_COLOR_RESIZE_GRIP_HOVER]; + sty.Colors[ImGuiCol_ResizeGripActive]=uiColors[GUI_COLOR_RESIZE_GRIP_ACTIVE]; + sty.Colors[ImGuiCol_FrameBg]=uiColors[GUI_COLOR_WIDGET_BACKGROUND]; + sty.Colors[ImGuiCol_FrameBgHovered]=uiColors[GUI_COLOR_WIDGET_BACKGROUND_HOVER]; + sty.Colors[ImGuiCol_FrameBgActive]=uiColors[GUI_COLOR_WIDGET_BACKGROUND_ACTIVE]; + sty.Colors[ImGuiCol_SliderGrab]=uiColors[GUI_COLOR_SLIDER_GRAB]; + sty.Colors[ImGuiCol_SliderGrabActive]=uiColors[GUI_COLOR_SLIDER_GRAB_ACTIVE]; + sty.Colors[ImGuiCol_TitleBgActive]=uiColors[GUI_COLOR_TITLE_BACKGROUND_ACTIVE]; + sty.Colors[ImGuiCol_CheckMark]=uiColors[GUI_COLOR_CHECK_MARK]; + sty.Colors[ImGuiCol_TextSelectedBg]=uiColors[GUI_COLOR_TEXT_SELECTION]; + sty.Colors[ImGuiCol_PlotLines]=uiColors[GUI_COLOR_PLOT_LINES]; + sty.Colors[ImGuiCol_PlotLinesHovered]=uiColors[GUI_COLOR_PLOT_LINES_HOVER]; + sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_PLOT_HISTOGRAM]; + sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_PLOT_HISTOGRAM_HOVER]; + sty.Colors[ImGuiCol_TableRowBg]=uiColors[GUI_COLOR_TABLE_ROW_EVEN]; + sty.Colors[ImGuiCol_TableRowBgAlt]=uiColors[GUI_COLOR_TABLE_ROW_ODD]; + } sty.Colors[ImGuiCol_Border]=uiColors[GUI_COLOR_BORDER]; sty.Colors[ImGuiCol_BorderShadow]=uiColors[GUI_COLOR_BORDER_SHADOW];