diff --git a/demos/gameboy/dtect.fur b/demos/gameboy/dtect.fur new file mode 100644 index 000000000..17c5f5bc4 Binary files /dev/null and b/demos/gameboy/dtect.fur differ diff --git a/demos/gameboy/ice_3_advert.fur b/demos/gameboy/ice_3_advert.fur new file mode 100644 index 000000000..cb0d61a93 Binary files /dev/null and b/demos/gameboy/ice_3_advert.fur differ diff --git a/demos/genesis/mm5_boss.fur b/demos/genesis/mm5_boss.fur new file mode 100644 index 000000000..d3eb6caf5 Binary files /dev/null and b/demos/genesis/mm5_boss.fur differ diff --git a/demos/nes/the_best-1990.fur b/demos/nes/the_best-1990.fur index c5b62f15f..ca599260b 100644 Binary files a/demos/nes/the_best-1990.fur and b/demos/nes/the_best-1990.fur differ diff --git a/doc/1-intro/README.md b/doc/1-intro/README.md index 8e9289fc6..e5430504b 100644 --- a/doc/1-intro/README.md +++ b/doc/1-intro/README.md @@ -20,4 +20,10 @@ it also has a flexible windowing system which you may move around and organize. see [2-interface](../2-interface/README.md) and [3-pattern](../3-pattern/README.md) for more information. -once familiar with the tracker, look to [9-guides](../9-guides/README.md) for useful techniques. \ No newline at end of file +once familiar with the tracker, look to [9-guides](../9-guides/README.md) for useful techniques. + + + +# links + +[Furnace Tutorials](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1): video tutorials created by Spinning Square Waves. \ No newline at end of file diff --git a/doc/2-interface/README.md b/doc/2-interface/README.md index ae2768f2e..3d6067aff 100644 --- a/doc/2-interface/README.md +++ b/doc/2-interface/README.md @@ -13,7 +13,7 @@ primary topics: - [play/edit controls](play-edit-controls.md) - [instrument/wavetable/sample list](asset-list.md) - [song information](song-info.md) -- [effect list window](effect-list.md) +- [effect list window](effect-list-window.md) - [pattern view](../3-pattern/README.md) - [instrument editor](../4-instrument/README.md) - [wavetable editor](../5-wave/README.md) diff --git a/doc/2-interface/effect-list-window.md b/doc/2-interface/effect-list-window.md new file mode 100644 index 000000000..28a1c313c --- /dev/null +++ b/doc/2-interface/effect-list-window.md @@ -0,0 +1,10 @@ +# effect list window + +(for more details about these effects, see [the effects page](../3-pattern/effects.md)) + +![effect list window](effect-list.png) + +this window provides a list of the effects that are available. + +- **Chip at cursor**: the currently selected chip. the list only shows available effects for this chip. +- menu button: opens a small list of effect categories. toggle each to change whether they're shown in the list. diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index d6ed79c60..32c85242b 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -4,12 +4,38 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. -# General +## General -- **Workspace layout** - - **Import**: reads a .ini layout file. - - **Export**: writes current layout to a .ini file. - - **Reset**: resets layout to default. +### Program + +- **Render backend** + - changing this may help with performace issues. +- **Late render clear** +- **Power-saving mode** + - saves power by lowering the frame rate to 2fps when idle. + - may cause issues under Mesa drivers! +- **Disable threaded input (restart after changing!)** + - threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency. + - however, crashes have been reported when threaded input is on. enable this option if that is the case. +- **Enable event delay** + - may cause issues with high-polling-rate mice when previewing notes. + +### File + +- **Use system file picker**: use native OS file dialog instead of Furnace's. +- **Number of recent files** +- **Compress when saving** + - use zlib to compress saved songs. +- **Save unused patterns** +- **Use new pattern format when saving** +- **Don't apply compatibility flags when loading .dmf** +- **Audio export loop/fade out time:** + - **Set to these values on start-up:** + - **Loops**: number of additional times to play through `0Bxx` song loop. + - **Fade out (seconds)**: length of fade out after final loop. + - **Remember last values** + +### Chip - **Initial system**: the system of chips loaded on starting Furnace. - **Current system**: sets current chips as default. @@ -18,172 +44,131 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. - **Reset to defaults**: sets default to "Sega Genesis/Mega Drive". - **Name**: name for the default system. may be set to any text. - system configuration: same as in the [chip manager](../8-advanced/chip-manager.md) and [mixer](../8-advanced/mixer.md). +- **When creating new song**: + - **Display system preset selector** + - **Start with initial system** +- **Restart song when changing chip properties** + +### Start-up - **Play intro on start-up:** - **No**: skips intro entirely. - **Short**: shows silent title screen briefly. - **Full (short when loading song)**: shows animated musical intro unless started with a song (command line, double-clicking a .fur file, etc.) - **Full (always)**: always shows animated musical intro. -- **When creating new song**: - - **Display system preset selector** - - **Start with initial system** +- **Disable fade-in during start-up** +- **About screen party time** + - _warning:_ may cause epileptic seizures. + +### Behavior -- **Double-click time (seconds)**: maximum time between mouse clicks to recognize them as a double-click. -- **Toggle channel solo on:** select which interactions with a channel header will toggle solo for that channel. -- **Push value when overwriting instead of clearing it**: in the order list and pattern editors, typing into an already-filled value will shift digits instead of starting fresh. - - if off: moving the cursor onto the value `A5` and typing a "B" results in `0B`. - - if on: with the cursor on the value `A5` and typing a "B" results in `5B`. -- **Move cursor up on backspace-delete** -- **Move cursor by edit step on delete** -- **Change current instrument when changing instrument column (absorb)** -- **Delete effect value when deleting effect** -- **Change order when scrolling outside of pattern bounds**: - - if off, the pattern edit cursor will stay locked within the current order. - - if on, moving the cursor past the edge of the previous or next order will move to that order. -- **Move cursor by edit step on insert (push)** -- **Move cursor to end of clipboard content when pasting** -- **Don't scroll when moving cursor** -- **Double click selects entire column** -- **Allow docking editors** -- **Don't raise pattern editor on click** -- **Focus pattern editor when selecting instrument** -- **Restart song when changing chip properties** -- **Use system file picker**: use native OS file dialog instead of Furnace's. -- **Only allow window movement when clicking on title bar** -- **Enable event delay** - - may cause issues with high-polling-rate mice when previewing notes. -- **Power-saving mode** - - saves power by lowering the frame rate to 2fps when idle. - - may cause issues under Mesa drivers! -- **Disable threaded input (restart after changing!)** - - threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency. - - however, crashes have been reported when threaded input is on. enable this option if that is the case. -- **Remember window position** - - remembers the window's last position on start-up. - **New instruments are blank** -- **Save unused patterns** -- **Compress when saving** - - use zlib to compress saved songs. -- **Cursor follows current order when moving it** - - applies when playback is stopped. -- **Audio export loop/fade out time:** - - **Set to these values on start-up:** - - **Loops**: number of additional times to play through `0Bxx` song loop. - - **Fade out (seconds)**: length of fade out after final loop. - - **Remember last values** -- **Note preview behavior:** - - **Never** - - **When cursor is in Note column** - - **When cursor is in Note column or not in edit mode** - - **Always** -- **Wrap pattern cursor horizontally:** - - **No** - - **Yes** - - **Yes, and move to next/prev row** -- **Wrap pattern cursor vertically:** - - **No** - - **Yes** - - **Yes, and move to next/prev pattern** -- **Cursor movement keys behavior:** - - **Move by one** - - **Move by Edit Step** -- **Effect input cursor behavior:** - - **Move down** - - **Move to effect value (otherwise move down)** - - **Move to effect value/next effect and wrap around** -- **Allow dragging selection:** - - **No** - - **Yes** - - **Yes (while holding Ctrl only)** -# Audio/MIDI +## Audio + +### Output - **Backend**: select SDL or JACK for audio output. - only appears on Linux, or MacOS compiled with JACK support +- **Driver** - **Device**: audio device for playback. - **Sample rate** - **Outputs**: select number of audio outputs created, up to 16. - only appears when Backend is JACK. - **Channels**: number of output channels to use. - **Buffer size**: size of buffer in both samples and milliseconds. -- **Quality**: selects quality of resampling. low quality reduces CPU load. -- **Metronome volume** - **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode. - _warning:_ experimental! may produce glitches. only enable if your buffer size is small (10ms or less). - **Force mono audio** -- **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device. - - this avoids activating Windows' built-in limiter. - **want:** displays requested audio configuration. - **got:** displays actual audio configuration returned by audio backend. -- **MIDI input** -- **MIDI output** -- **MIDI input settings** - - **Note input** - - **Velocity input** - - **Map MIDI channels to direct channels** - - **Map Yamaha FM voice data to instruments** - - **Program change is instrument selection** - - **Value input style**: +### Mixing + +- **Quality**: selects quality of resampling. low quality reduces CPU load. +- **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device. + - this avoids activating Windows' built-in limiter. + +### Metronome + +- **Metronome volume** + + + +## MIDI + +### MIDI input + +- **MIDI input**: input device. +- **Note input** +- **Velocity input** +- **Map MIDI channels to direct channels** +- **Map Yamaha FM voice data to instruments** +- **Program change is instrument selection** +- **Value input style**: + - **Disabled/custom** + - **Two octaves (0 is C-4, F is D#5)** + - **Raw (note number is value)** + - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)** + - **Use dual control change (one for each nibble)** + - **CC of upper nibble** + - **CC of lower nibble** + - **Use 14-bit control change** + - **MSB CC** + - **LSB CC** + - **Use single control change** + - **Control** +- **Per-column control change** + - **Instrument**\ + **Volume**\ + **Effect `x` type**\ + **Effect `x` value** - **Disabled/custom** - - **Two octaves (0 is C-4, F is D#5)** - - **Raw (note number is value)** - - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)** - **Use dual control change (one for each nibble)** - **CC of upper nibble** - **CC of lower nibble** - **Use 14-bit control change** - **MSB CC** - **LSB CC** - - **Use single control change** + - **Use single control change (imprecise)** - **Control** - - **Per-column control change** - - **Instrument**\ - **Volume**\ - **Effect `x` type**\ - **Effect `x` value** - - **Disabled/custom** - - **Use dual control change (one for each nibble)** - - **CC of upper nibble** - - **CC of lower nibble** - - **Use 14-bit control change** - - **MSB CC** - - **LSB CC** - - **Use single control change (imprecise)** - - **Control** - - **Volume curve** - - **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. - - each action has the following: - - **Type** - - **Channel** - - **Note/Control** - - **Velocity/Value** - - **Action** - - **Learn** - - **Remove** +- **Volume curve** +- **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. + - each action has the following: + - **Type** + - **Channel** + - **Note/Control** + - **Velocity/Value** + - **Action** + - **Learn** + - **Remove** -- **MIDI output settings** - - **Output mode:** - - **Off (use for TX81Z)** - - **Melodic** - - **Send Program Change** - - **Send MIDI clock** - - **Send MIDI timecode** - - **Timecode frame rate:** - - **Closest to Tick Rate** - - **Film (24fps)** - - **PAL (25fps)** - - **NTSC drop (29.97fps)** - - **NTSC non-drop (30fps)** +### MIDI output -# Emulation +- **MIDI output**: output device. +- **Output mode:** + - **Off (use for TX81Z)** + - **Melodic** +- **Send Program Change** +- **Send MIDI clock** +- **Send MIDI timecode** + - **Timecode frame rate:** + - **Closest to Tick Rate** + - **Film (24fps)** + - **PAL (25fps)** + - **NTSC drop (29.97fps)** + - **NTSC non-drop (30fps)** +## Emulation + +### Cores + - **Arcade/YM2151 core**\ **Genesis/YM2612 core**\ **SN76489 core**\ @@ -202,16 +187,122 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. -# Appearance +## Keyboard + +### Keyboard + +- **Import** +- **Export** +- **Reset defaults** +- several categories of keybinds... + - click on a keybind then enter a key or key combination to change it + - right-click to clear the keybind + + + +## Interface + +### Layout + +- **Workspace layout** + - **Import**: reads a .ini layout file. + - **Export**: writes current layout to a .ini file. + - **Reset**: resets layout to default. +- **Allow docking editors** +- **Remember window position** + - remembers the window's last position on start-up. +- **Only allow window movement when clicking on title bar** +- **Play/edit controls layout:** + - **Classic** + - **Compact** + - **Compact (vertical)** + - **Split** +- **Position of buttons in Orders:** + - **Top** + - **Left** + - **Right** + +### Mouse + +- **Double-click time (seconds)**: maximum time between mouse clicks to recognize them as a double-click. +- **Don't raise pattern editor on click** +- **Focus pattern editor when selecting instrument** +- **Note preview behavior:** + - **Never** + - **When cursor is in Note column** + - **When cursor is in Note column or not in edit mode** + - **Always** +- **Allow dragging selection:** + - **No** + - **Yes** + - **Yes (while holding Ctrl only)** +- **Toggle channel solo on:** select which interactions with a channel header will toggle solo for that channel. + - Right-click or double click + - Right-click + - Double-click +- **Double click selects entire column** + +### Cursor behavior + +- **Insert pushes entire channel row** +- **Pull delete affects entire channel row** +- **Push value when overwriting instead of clearing it**: in the order list and pattern editors, typing into an already-filled value will shift digits instead of starting fresh. + - if off: moving the cursor onto the value `A5` and typing a "B" results in `0B`. + - if on: with the cursor on the value `A5` and typing a "B" results in `5B`. +- **Effect input behavior:** + - **Move down** + - **Move to effect value (otherwise move down)** + - **Move to effect value/next effect and wrap around** +- **Delete effect value when deleting effect** +- **Change current instrument when changing instrument column (absorb)** + + +### Cursor movement + +- **Wrap horizontally:** + - **No** + - **Yes** + - **Yes, and move to next/prev row** +- **Wrap vertically:** + - **No** + - **Yes** + - **Yes, and move to next/prev pattern** + - **Yes, and move to next/prev pattern (wrap around)** +- **Cursor movement keys behavior:** + - **Move by one** + - **Move by Edit Step** +- **Move cursor by edit step on delete** +- **Move cursor by edit step on insert (push)** +- **Move cursor up on backspace-delete** +- **Move cursor to end of clipboard content when pasting** + +### Scrolling + +- **Change order when scrolling outside of pattern bounds**: + - **No**: the pattern edit cursor will stay locked within the current order. + - **Yes**: moving the cursor past the edge of the previous or next order will move to that order, but not past the start or end of a song. + - **Yes, and wrap around song**: as above, but will wrap from song end to start. +- **Cursor follows current order when moving it** + - applies when playback is stopped. +- **Don't scroll when moving cursor** +- **Move cursor with scroll wheel** + + + +## Appearance + +### Scaling -- **Render driver** - **Automatic UI scaling factor**: automatically match the OS's UI scaling. - **UI scaling factor**: only if "Automatic UI scaling factor" is off. +- **Icon size** + +### Text + - **Main font**: if "Custom...", a file path selector will appear beneath. - **Size** - **Pattern font**: if "Custom...", a file path selector will appear beneath. - **Size** -- **Icon size** - **Display Japanese characters**\ **Display Chinese (Simplified) characters**\ **Display Chinese (Traditional) characters**\ @@ -219,25 +310,7 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. - only toggle these options if you have enough graphics memory. - these are a temporary solution until dynamic font atlas is implemented in Dear ImGui. -- **Number of recent files** - -- **Pattern view labels:** -- **Note off (3-char)**: default is `OFF` -- **Note release (3-char)**: default is `===`. -- **Macro release (3-char)**: default is `REL`. -- **Empty field (3-char)**: default is `...`. -- **Empty field (2-char)**: default is `..`. - -- **Orders row number format:** - - **Decimal** - - **Hexadecimal** -- **Pattern row number format:** - - **Decimal** - - **Hexadecimal** -- **FM parameter names:** - - **Friendly** - - **Technical** - - **Technical (alternate)** +### Program - **Title bar:** - **Furnace** @@ -251,43 +324,40 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. - **File path** - **Cursor details or file path** - **Nothing** -- **Play/edit controls layout:** - - **Classic** - - **Compact** - - **Compact (vertical)** - - **Split** -- **Position of buttons in Orders:** - - **Top** - - **Left** - - **Right** -- **FM parameter editor layout:** - - **Modern** - - **Compact (2x2, classic)** - - **Compact (1x4)** - - **Compact (4x1)** - - **Alternate (2x2)** - - **Alternate (1x4)** - - **Alternate (4x1)** -- **Position of Sustain in FM editor:** - - **Between Decay and Sustain Rate** - - **After Release Rate** -- **Macro editor layout:** - - **Unified** - - **Mobile** - - **Grid** - - **Single (with list)** - - **Single (combo box)** -- **Namco 163 chip name** +### Orders + +- **Highlight channel at cursor in Orders** +- **Orders row number format:** + - **Decimal** + - **Hexadecimal** + +### Pattern + +- **Center pattern view**: centers pattern horizontally in view. +- **Overflow pattern highlights** +- **Display previous/next pattern** +- **Pattern row number format:** + - **Decimal** + - **Hexadecimal** +- **Pattern view labels:** + - **Note off (3-char)**: default is `OFF` + - **Note release (3-char)**: default is `===`. + - **Macro release (3-char)**: default is `REL`. + - **Empty field (3-char)**: default is `...`. + - **Empty field (2-char)**: default is `..`. +- **Pattern view spacing after:** number of pixels of space between columns. + - **Note** + - **Instrument** + - **Volume** + - **Effect** + - **Effect value** +- **Single-digit effects for 00-0F** +- **Use flats instead of sharps** +- **Use German notation**: display `B` notes as `H`, and `A#` notes as `B`. + +### Channel -- **Channel colors:** - - **Single** - - **Channel type** - - **Instrument type** -- **Channel name colors:** - - **Single** - - **Channel type** - - **Instrument type** - **Channel style:** - **Classic** - **Line** @@ -310,60 +380,88 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. - **Regular** - **Monospace** - **Center channel name** +- **Channel colors:** + - **Single** + - **Channel type** + - **Instrument type** +- **Channel name colors:** + - **Single** + - **Channel type** + - **Instrument type** + +### Assets -- **Colorize instrument editor using instrument type** -- **Use separate colors for carriers/modulators in FM editor** - **Unified instrument/wavetable/sample list** - **Horizontal instrument list** -- **Use standard OPL waveform names** -- **Overflow pattern highlights** -- **Display previous/next pattern** -- **Use German notation**: display `B` notes as `H`, and `A#` notes as `B`. -- **Single-digit effects for 00-0F** -- **Center pattern view**: centers pattern horizontally in view. -- **Unsigned FM detune values** -- **Highlight channel at cursor in Orders** -- **About screen party time** - - _warning:_ may cause epileptic seizures. +- **Colorize instrument editor using instrument type** + +### Macro Editor + +- **Macro editor layout:** + - **Unified** + - **Mobile** + - **Grid** + - **Single (with list)** + - **Single (combo box)** +- **Use classic macro editor vertical slider** + +### Wave Editor - **Use compact wave editor** -- **Use classic macro editor vertical slider** + +### FM Editor + +- **FM parameter names:** + - **Friendly** + - **Technical** + - **Technical (alternate)** +- **Use standard OPL waveform names** +- **FM parameter editor layout:** + - **Modern** + - **Compact (2x2, classic)** + - **Compact (1x4)** + - **Compact (4x1)** + - **Alternate (2x2)** + - **Alternate (1x4)** + - **Alternate (4x1)** +- **Position of Sustain in FM editor:** + - **Between Decay and Sustain Rate** + - **After Release Rate** +- **Use separate colors for carriers/modulators in FM editor** +- **Unsigned FM detune values** + +### Statistics + +- **Chip memory usage unit:** + - **Bytes** + - **Kilobytes** + +### Oscilloscope + +- **Rounded corners** +- **Border** +- **Fill entire window** +- **Waveform goes out of bounds** + +### Windows + - **Rounded window corners** - **Rounded buttons** - **Rounded menu corners** - **Borders around widgets** -- **Disable fade-in during start-up** - -- **Oscilloscope settings:** - - **Rounded corners** - - **Fill entire window** - - **Waveform goes out of bounds** - - **Border** - -- **Pattern view spacing after:** - - **Note** - - **Instrument** - - **Volume** - - **Effect** - - **Effect value** -- **Color scheme** - - **Import** - - **Export** - - **Reset defaults** - - **General** - - **Color scheme type:** - - **Dark** - - **Light** - - **Frame shading** - - several more categories... -# Keyboard +## Color + +### Color scheme - **Import** - **Export** - **Reset defaults** -- several categories of keybinds... - - click on a keybind then enter a key or key combination to change it - - right-click to clear the keybind +- **General** + - **Color scheme type:** + - **Dark** + - **Light** + - **Frame shading**: applies a gradient effect to buttons and input boxes. +- several more categories... diff --git a/doc/4-instrument/n163.md b/doc/4-instrument/n163.md index 2e37b0d5f..b12bfe354 100644 --- a/doc/4-instrument/n163.md +++ b/doc/4-instrument/n163.md @@ -5,11 +5,11 @@ The Namco 163 instrument editor consists of two tabs: "Namco 163" for control of ## Namco 163 - **Load waveform**: if enabled, a waveform will be loaded when this instrument plays. - - if it isn't then only the offset/length change. + - if it isn't then only the position/length change. - **Waveform**: determines the waveform that will be loaded. - only appears when Load waveform is enabled. -- **Per-channel wave offset/length**: when enabled, the offset/length settings are split per channel. -- **Offset**: determines the waveform position in RAM. +- **Per-channel wave position/length**: when enabled, the position/length settings are split per channel. +- **Position**: determines the waveform position in RAM. - **Length**: determines the waveform length in RAM. ## Macros diff --git a/doc/5-wave/README.md b/doc/5-wave/README.md index 59131d39b..7c586ef78 100644 --- a/doc/5-wave/README.md +++ b/doc/5-wave/README.md @@ -2,19 +2,19 @@ Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. -Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "synth?" column. +Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "notes" column. -system | width | height | synth? +system | width | height | notes --------------------|------:|:-------|:------ Bubble System | 32 | 16 | -Game Boy | 32 | 16 | +Game Boy | 32 | 16 | phase reset on waveform change (clicking) SM8521 | 32 | 16 | Namco WSG | 32 | 16 | RAM only WonderSwan | 32 | 16 | -Namco 163 | ≤128 | 16 | +Namco 163 | ≤240 | 16 | limits differ depending on channel count SNES | ≤256 | 16 | -PC Engine | 32 | 32 | -Virtual Boy | 32 | 64 | no +PC Engine | 32 | 32 | phase reset on waveform change (clicking) +Virtual Boy | 32 | 64 | wavesynth unsupported Famicom Disk System | 64 | 64 | Konami SCC | 32 | 256 | Seta X1-010 | 128 | 256 | diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index 637884370..66d35479a 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -32,7 +32,6 @@ the following sound chips have sample support: - Ensoniq OTTO/ES5506 - Yamaha PCMD8/YMZ280B - MMC5 (last channel only) -- SNES/S-DSP ## compatible sample mode diff --git a/doc/7-systems/README.md b/doc/7-systems/README.md index 7e3728740..53be263ea 100644 --- a/doc/7-systems/README.md +++ b/doc/7-systems/README.md @@ -65,11 +65,11 @@ this is the full list of chips that Furnace supports. - [2A03](nes.md) - [Amiga](amiga.md) -- [AY-3-8910, AY-3-8914, YM2149(F), 5B](ay8910.md) -- [AY8930](ay8930.md) -- [MOS 6581, MOS 8580](c64.md) -- [ES5506](es5506.md) -- [SCC](scc.md) +- [AY-3-8910/8914/YM2149(F)/Sunsoft 5B](ay8910.md) +- [Microship AY8930](ay8930.md) +- [MOS 6581/8580 (SID)](c64.md) +- [Ensoniq ES5506](es5506.md) +- [Konami SCC](scc.md) - [FDS](fds.md) - [Game Boy](game-boy.md) - [Generic PCM DAC](dac.md) @@ -82,8 +82,8 @@ this is the full list of chips that Furnace supports. - [MSM5232](msm5232.md) - [MSM6258](msm6258.md) - [MSM6295](msm6295.md) -- [N163](n163.md) -- [Namco WSG, C15, C30](namco.md) +- [Namco 163](n163.md) +- [Namco WSG/C15/C30](namco.md) - [HuC6280](pce.md) - [PC Speaker](pcspkr.md) - [PET](pet.md) @@ -91,12 +91,11 @@ this is the full list of chips that Furnace supports. - [POKEY](pokey.md) - [PV-1000](pv1000.md) - [QSound](qsound.md) -- [RF5C68, RF5C164](ricoh.md) +- [RF5C68/RF5C164](ricoh.md) - [SAA1099](saa1099.md) -- [SCC](scc.md) - [SegaPCM](segapcm.md) - [SM8521](sm8521.md) -- [SN76489 / Sega PSG](sms.md) +- [SN76489/Sega PSG](sms.md) - [SNES](snes.md) - [tildearrow Sound Unit](soundunit.md) - [T6W28](t6w28.md) @@ -108,703 +107,16 @@ this is the full list of chips that Furnace supports. - [VRC6](vrc6.md) - [WonderSwan](wonderswan.md) - [X1-010](x1-010.md) -- [(OPL) VRC7, Y8950, YM3526, YM3812, YMF262](opl.md) -- [(OPLL) YM2413](opll.md) -- [(OPZ) YM2414](opz.md) -- [(OPM) YM2151](ym2152.md) -- [(OPN) YM2203](ym2203.md) -- [(OPNA) YM2608](ym2608.md) -- [(OPNB) YM2610](ym2610.md) -- [(OPNB) YM2610B](ym2610b.md) -- [(OPN2) YM2612, YM3438](ym2612.md) +- [VRC7, Y8950, YM3526, YM3812 and YMF262 (OPL)](opl.md) +- [YM2413 (OPLL)](opll.md) +- [YM2414 (OPZ)](opz.md) +- [YM2151 (OPM)](ym2152.md) +- [YM2203 (OPN)](ym2203.md) +- [YM2608 (OPNA)](ym2608.md) +- [YM2610 (OPNB)](ym2610.md) +- [YM2610B (OPNB2)](ym2610b.md) +- [YM2612/YM3438 (OPN2)](ym2612.md) - [YMZ280B](ymz280b.md) -- [ZXS Beeper](zxbeep.md) +- [ZX Spectrum Beeper](zxbeep.md) Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but... - - - diff --git a/doc/7-systems/ga20.md b/doc/7-systems/ga20.md new file mode 100644 index 000000000..875564870 --- /dev/null +++ b/doc/7-systems/ga20.md @@ -0,0 +1,8 @@ +# Irem GA20 + +ga20 is a 4 channel PCM sound source used by Irem in their arcades in late 1980s and eraly 1990s, often paired with [Yamaha YM2151](ym2151.md). Soundchip itself is rather unremarkable, having 8-bit volume and pitch control, no stereo panning + + +# effects + +none diff --git a/doc/7-systems/k056320.md b/doc/7-systems/k056320.md index a78c16a39..9e2e162f0 100644 --- a/doc/7-systems/k056320.md +++ b/doc/7-systems/k056320.md @@ -1,6 +1,6 @@ # Konami 056320 -the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats. +the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio, 12-bit pitch resolution and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats. diff --git a/doc/9-guides/README.md b/doc/9-guides/README.md index 80c96a005..aa6d5cbf6 100644 --- a/doc/9-guides/README.md +++ b/doc/9-guides/README.md @@ -3,4 +3,5 @@ here is a small collection of useful tricks and techniques to really make Furnace sing. - [using samples with limited playback rates](limited-samples.md) -- [choosing emulation cores](emulation-cores.md) \ No newline at end of file +- [choosing emulation cores](emulation-cores.md) +- [guide on using OPLL patch macro](opllswitching.md) \ No newline at end of file diff --git a/doc/9-guides/opllswitching.md b/doc/9-guides/opllswitching.md new file mode 100644 index 000000000..157a153f5 --- /dev/null +++ b/doc/9-guides/opllswitching.md @@ -0,0 +1,19 @@ +# using OPLL patch macro + +YM2413's biggest flaw (or, rather, cost-saving feature) was that it could use only one user-defined instrument at once. It wasn't monotimbrial; you could use 15 built-in presets and 5 built-in drum tones freely, but for these going off the beaten path, it surely was limiting. However, there is one technique, as amazing as simple: **mid-note preset switching**. + + the idea is to use the first patch to put the envelope in an unintended state for the second patch so that it sounds different, with a higher or lower modulation level. the sustain level defines at which "envelope level" the envelope will switch to the sustain state (or release depending on envelope type). if the first patch is used to put the envelope into sustain at a higher or lower envelope state than intended for the second patch, it'll still be in sustain/release but at a higher or lower level than it should be at that point. + +therefore, much more variety can be forced out, without using custom instruments. As of July 2023, Furnace is the only tool supporting this feature. It is accessed in 'Macros' tab in OPLL instrument editor. + +For example, try putting the first macro value as 14 (acoustic bass preset), followed by 4 (flute preset). This way you will get distortion guitar-like sound this is nothing like other 2413 preset! There are many combination to test out, which is highly recommended (I can only say, 12->1 or 12->4 produces sound similiar to the well-known 4-op FM mallet brass) + +## drums using this technique + +Using OPLL's drum mode, described is systems/opll.md, you gain access to 5 hardcoded drum tones at the expense of 3 melodic FM channels. Patch switching eliminates that, as using it, it's also possible to construct percussive sounds, some even fuller than their drum mode counterparts! +In short, noise portion of drums (as in hi-hats), can be created of the very high pitched pseudo-distortion guitar, described as above. For kicks, snares, toms and claps, more effort is needed, however using volume and arpeggio macros will help. + +## examples + +- [Lman-Clubster cover by Mahbod](https://www.youtube.com/watch?v=jfHs7tSyjXI) +- [OPLL Nation by Mahbod](https://www.youtube.com/watch?v=ou6pEfxByeE) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 2ccc3e9c0..d66dadf74 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -800,138 +800,138 @@ namespace IGFD // will be called internally // will not been exposed to IGFD API - bool IGFD::FilterManager::prFillFileStyle(std::shared_ptr vFileInfos) const + bool IGFD::FilterManager::prFillFileStyle(FileInfos& vFileInfos) const { - if (vFileInfos.use_count() && !prFilesStyle.empty()) + if (!prFilesStyle.empty()) { for (const auto& _flag : prFilesStyle) { for (const auto& _file : _flag.second) { - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd') { if (_file.first.empty()) // for all dirs { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } - else if (_file.first == vFileInfos->fileNameExt) // for dirs who are equal to style criteria + else if (_file.first == vFileInfos.fileNameExt) // for dirs who are equal to style criteria { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f') { if (_file.first.empty()) // for all files { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } - else if (_file.first == vFileInfos->fileNameExt) // for files who are equal to style criteria + else if (_file.first == vFileInfos.fileNameExt) // for files who are equal to style criteria { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l') { if (_file.first.empty()) // for all links { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } - else if (_file.first == vFileInfos->fileNameExt) // for links who are equal to style criteria + else if (_file.first == vFileInfos.fileNameExt) // for links who are equal to style criteria { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } if (_flag.first & IGFD_FileStyleByExtention) { - if (_file.first == vFileInfos->fileExt) + if (_file.first == vFileInfos.fileExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } // can make sense for some dirs like the hidden by ex ".git" - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd') { - if (_file.first == vFileInfos->fileExt) + if (_file.first == vFileInfos.fileExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f') { - if (_file.first == vFileInfos->fileExt) + if (_file.first == vFileInfos.fileExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l') { - if (_file.first == vFileInfos->fileExt) + if (_file.first == vFileInfos.fileExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } } if (_flag.first & IGFD_FileStyleByFullName) { - if (_file.first == vFileInfos->fileNameExt) + if (_file.first == vFileInfos.fileNameExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd') { - if (_file.first == vFileInfos->fileNameExt) + if (_file.first == vFileInfos.fileNameExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f') { - if (_file.first == vFileInfos->fileNameExt) + if (_file.first == vFileInfos.fileNameExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l') { - if (_file.first == vFileInfos->fileNameExt) + if (_file.first == vFileInfos.fileNameExt) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } } if (_flag.first & IGFD_FileStyleByContainedInFullName) { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } - if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd') + if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd') { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f') + else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f') { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } - else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l') + else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l') { - if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) + if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos) { - vFileInfos->fileStyle = _file.second; + vFileInfos.fileStyle = _file.second; } } } - if (vFileInfos->fileStyle.use_count()) + if (vFileInfos.fileStyle.use_count()) return true; } } @@ -1142,6 +1142,7 @@ namespace IGFD for (int i=1; i<4; i++) { puSortingDirection[i]=false; } + invalidFile.isValid=false; } void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal) @@ -1191,29 +1192,24 @@ namespace IGFD puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - // this code fail in c:\\Users with the link "All users". got a invalid comparator /* // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 // strict ordering for file/directory types beginning in '.' // common on Linux platforms - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') + if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.') return false; - if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') + if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.') return true; - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') + if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.') { - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case + return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case } */ - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case + if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first + return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case }); } else @@ -1222,28 +1218,23 @@ namespace IGFD puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - // this code fail in c:\\Users with the link "All users". got a invalid comparator /* // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571 // strict ordering for file/directory types beginning in '.' // common on Linux platforms - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.') + if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.') return false; - if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.') + if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.') return true; - if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.') + if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.') { - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case + return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case } */ - return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case + return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case }); } } @@ -1259,13 +1250,10 @@ namespace IGFD puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileExt < b->fileExt); // else + if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first + return (a.fileExt < b.fileExt); // else }); } else @@ -1274,15 +1262,10 @@ namespace IGFD puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileExt > b->fileExt); // else + if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last + return (a.fileExt > b.fileExt); // else }); } } @@ -1298,15 +1281,10 @@ namespace IGFD puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileSize < b->fileSize); // else + if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first + return (a.fileSize < b.fileSize); // else }); } else @@ -1315,15 +1293,10 @@ namespace IGFD puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileSize > b->fileSize); // else + if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last + return (a.fileSize > b.fileSize); // else }); } } @@ -1339,15 +1312,10 @@ namespace IGFD puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - return (a->fileModifDate < b->fileModifDate); // else + if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first + return (a.fileModifDate < b.fileModifDate); // else }); } else @@ -1356,15 +1324,10 @@ namespace IGFD puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (a==NULL || b==NULL) - return false; - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - return (a->fileModifDate > b->fileModifDate); // else + if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last + return (a.fileModifDate > b.fileModifDate); // else }); } } @@ -1384,15 +1347,12 @@ namespace IGFD puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first - if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) - return (a->thumbnailInfo.textureHeight < b->thumbnailInfo.textureHeight); - return (a->thumbnailInfo.textureWidth < b->thumbnailInfo.textureWidth); + if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first + if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth) + return (a.thumbnailInfo.textureHeight < b.thumbnailInfo.textureHeight); + return (a.thumbnailInfo.textureWidth < b.thumbnailInfo.textureWidth); }); } @@ -1402,15 +1362,12 @@ namespace IGFD puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails; #endif // USE_CUSTOM_SORTING_ICON std::sort(prFileList.begin(), prFileList.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + [](const FileInfos& a, const FileInfos& b) -> bool { - if (!a.use_count() || !b.use_count()) - return false; - - if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last - if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) - return (a->thumbnailInfo.textureHeight > b->thumbnailInfo.textureHeight); - return (a->thumbnailInfo.textureWidth > b->thumbnailInfo.textureWidth); + if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last + if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth) + return (a.thumbnailInfo.textureHeight > b.thumbnailInfo.textureHeight); + return (a.thumbnailInfo.textureWidth > b.thumbnailInfo.textureWidth); }); } } @@ -1441,38 +1398,38 @@ namespace IGFD return fileNameExt; } - void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const char& vFileType, void* ent) + void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, char vFileType, void* ent) { - auto infos = std::make_shared(); + FileInfos infos; #ifdef _WIN32 struct dirent* dent=(struct dirent*)ent; #endif - infos->filePath = vPath; - infos->fileNameExt = vFileName; - infos->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos->fileNameExt); - infos->fileType = vFileType; + infos.filePath = vPath; + infos.fileNameExt = vFileName; + infos.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos.fileNameExt); + infos.fileType = vFileType; - if (infos->fileNameExt.empty() || ((infos->fileNameExt == "." || infos->fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807 - if (infos->fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') // dont show hidden files - if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos->fileNameExt != ".")) // except "." if in directory mode //-V728 + if (infos.fileNameExt.empty() || ((infos.fileNameExt == "." || infos.fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807 + if (infos.fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos.fileNameExt[0] == '.') // dont show hidden files + if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos.fileNameExt != ".")) // except "." if in directory mode //-V728 return; - if (infos->fileType == 'f' || - infos->fileType == 'l') // link can have the same extention of a file + if (infos.fileType == 'f' || + infos.fileType == 'l') // link can have the same extention of a file { - size_t lpt = infos->fileNameExt.find_last_of('.'); + size_t lpt = infos.fileNameExt.find_last_of('.'); if (lpt != std::string::npos) { - infos->fileExt = infos->fileNameExt.substr(lpt); + infos.fileExt = infos.fileNameExt.substr(lpt); } - for (char& i: infos->fileExt) { + for (char& i: infos.fileExt) { if (i>='A' && i<='Z') i+='a'-'A'; } - if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos->fileExt)) + if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos.fileExt)) { return; } @@ -1483,16 +1440,16 @@ namespace IGFD SYSTEMTIME localTime; char timebuf[100]; - infos->fileSize=dent->dwin_size; + infos.fileSize=dent->dwin_size; if (FileTimeToSystemTime(&dent->dwin_mtime,&systemTime)==TRUE) { if (SystemTimeToTzSpecificLocalTime(NULL,&systemTime,&localTime)==TRUE) { snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",localTime.wYear,localTime.wMonth,localTime.wDay,localTime.wHour,localTime.wMinute); } else { snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute); } - infos->fileModifDate=timebuf; + infos.fileModifDate=timebuf; } else { - infos->fileModifDate="???"; + infos.fileModifDate="???"; } #endif @@ -1534,7 +1491,7 @@ namespace IGFD { struct dirent* ent = files[i]; std::string where = path + std::string(PATH_SEP_STR) + std::string(ent->d_name); - char fileType = 0; + char fileType = 'f'; #if defined(HAVE_DIRENT_TYPE) || defined(_WIN32) if (ent->d_type != DT_UNKNOWN) { @@ -1544,7 +1501,7 @@ namespace IGFD fileType = 'f'; break; case DT_DIR: fileType = 'd'; break; - case DT_LNK: + case DT_LNK: { #ifdef _WIN32 fileType = 'f'; #else @@ -1557,7 +1514,8 @@ namespace IGFD } else { - fileType = 'l'; + // why does 'l' make it crash? + fileType = 'f'; } } else @@ -1568,6 +1526,9 @@ namespace IGFD #endif break; } + default: + fileType = 'f'; break; + } } else #endif // HAVE_DIRENT_TYPE @@ -1622,12 +1583,12 @@ namespace IGFD ClearFileLists(); for (auto& drive : drives) { - auto info = std::make_shared(); - info->fileNameExt = drive; - info->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive); - info->fileType = 'd'; + FileInfos info; + info.fileNameExt = drive; + info.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive); + info.fileType = 'd'; - if (!info->fileNameExt.empty()) + if (!info.fileNameExt.empty()) { prFileList.push_back(info); } @@ -1658,11 +1619,11 @@ namespace IGFD return prFileList.size(); } - std::shared_ptr IGFD::FileManager::GetFullFileAt(size_t vIdx) + const FileInfos& IGFD::FileManager::GetFullFileAt(size_t vIdx) { if (vIdx < prFileList.size()) return prFileList[vIdx]; - return nullptr; + return invalidFile; } bool IGFD::FileManager::IsFilteredListEmpty() @@ -1675,11 +1636,11 @@ namespace IGFD return prFilteredFileList.size(); } - std::shared_ptr IGFD::FileManager::GetFilteredFileAt(size_t vIdx) + const FileInfos& IGFD::FileManager::GetFilteredFileAt(size_t vIdx) { if (vIdx < prFilteredFileList.size()) return prFilteredFileList[vIdx]; - return nullptr; + return invalidFile; } bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName) @@ -1706,14 +1667,12 @@ namespace IGFD void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal) { prFilteredFileList.clear(); - for (const auto& file : prFileList) + for (const FileInfos& file : prFileList) { - if (!file.use_count()) - continue; bool show = true; - if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag + if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag show = false; - if (puDLGDirectoryMode && file->fileType != 'd') // directory mode + if (puDLGDirectoryMode && file.fileType != 'd') // directory mode show = false; if (show) prFilteredFileList.push_back(file); @@ -1750,13 +1709,10 @@ namespace IGFD return ""; } - void IGFD::FileManager::prCompleteFileInfos(const std::shared_ptr& vInfos) + void IGFD::FileManager::prCompleteFileInfos(FileInfos& vInfos) { - if (!vInfos.use_count()) - return; - - if (vInfos->fileNameExt != "." && - vInfos->fileNameExt != "..") + if (vInfos.fileNameExt != "." && + vInfos.fileNameExt != "..") { // _stat struct : //dev_t st_dev; /* ID of device containing file */ @@ -1774,25 +1730,25 @@ namespace IGFD //time_t st_ctime; /* time of last status change - not sure out of ntfs */ #ifdef _WIN32 - if (vInfos->fileType != 'd') + if (vInfos.fileType != 'd') { - vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); + vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize); } #else std::string fpn; - if (vInfos->fileType == 'f' || vInfos->fileType == 'l' || vInfos->fileType == 'd') // file - fpn = vInfos->filePath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; + if (vInfos.fileType == 'f' || vInfos.fileType == 'l' || vInfos.fileType == 'd') // file + fpn = vInfos.filePath + std::string(1u, PATH_SEP) + vInfos.fileNameExt; struct stat statInfos = {}; char timebuf[100]; int result = stat(fpn.c_str(), &statInfos); if (result!=-1) { - if (vInfos->fileType != 'd') + if (vInfos.fileType != 'd') { - vInfos->fileSize = (size_t)statInfos.st_size; - vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); + vInfos.fileSize = (size_t)statInfos.st_size; + vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize); } size_t len = 0; @@ -1806,12 +1762,12 @@ namespace IGFD #endif // MSVC if (len) { - vInfos->fileModifDate = std::string(timebuf, len); + vInfos.fileModifDate = std::string(timebuf, len); } } else { - vInfos->fileSize=0; - vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize); - vInfos->fileModifDate="???"; + vInfos.fileSize=0; + vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize); + vInfos.fileModifDate="???"; } #endif } @@ -1994,14 +1950,11 @@ namespace IGFD IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName); } - bool IGFD::FileManager::SelectDirectory(const std::shared_ptr& vInfos) + bool IGFD::FileManager::SelectDirectory(const FileInfos& vInfos) { - if (!vInfos.use_count()) - return false; - bool pathClick = false; - if (vInfos->fileNameExt == "..") + if (vInfos.fileNameExt == "..") { pathClick = SetPathOnParentDirectoryIfAny(); } @@ -2011,23 +1964,23 @@ namespace IGFD if (puShowDrives) { - newPath = vInfos->fileNameExt + std::string(1u, PATH_SEP); + newPath = vInfos.fileNameExt + std::string(1u, PATH_SEP); } else { #ifdef __linux__ if (puFsRoot == prCurrentPath) - newPath = prCurrentPath + vInfos->fileNameExt; + newPath = prCurrentPath + vInfos.fileNameExt; else #endif // __linux__ - newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos->fileNameExt; + newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos.fileNameExt; } if (IGFD::Utils::IsDirectoryExist(newPath)) { if (puShowDrives) { - prCurrentPath = vInfos->fileNameExt; + prCurrentPath = vInfos.fileNameExt; puFsRoot = prCurrentPath; } else @@ -2041,35 +1994,32 @@ namespace IGFD return pathClick; } - void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr& vInfos) + void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const FileInfos& vInfos) { - if (!vInfos.use_count()) - return; - if (ImGui::GetIO().KeyCtrl) { if (puDLGcountSelectionMax == 0) // infinite selection { - if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add + if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add { - prAddFileNameInSelection(vInfos->fileNameExt, true); + prAddFileNameInSelection(vInfos.fileNameExt, true); } else // found +> remove { - prRemoveFileNameInSelection(vInfos->fileNameExt); + prRemoveFileNameInSelection(vInfos.fileNameExt); } } else // selection limited by size { if (prSelectedFileNames.size() < puDLGcountSelectionMax) { - if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add + if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add { - prAddFileNameInSelection(vInfos->fileNameExt, true); + prAddFileNameInSelection(vInfos.fileNameExt, true); } else // found +> remove { - prRemoveFileNameInSelection(vInfos->fileNameExt); + prRemoveFileNameInSelection(vInfos.fileNameExt); } } } @@ -2081,18 +2031,15 @@ namespace IGFD prSelectedFileNames.clear(); // we will iterate filelist and get the last selection after the start selection bool startMultiSelection = false; - std::string fileNameToSelect = vInfos->fileNameExt; + std::string fileNameToSelect = vInfos.fileNameExt; std::string savedLastSelectedFileName; // for invert selection mode - for (const auto& file : prFileList) + for (const FileInfos& file : prFileList) { - if (!file.use_count()) - continue; - bool canTake = true; - if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false; + if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false; if (canTake) // if not filtered, we will take files who are filtered by the dialog { - if (file->fileNameExt == prLastSelectedFileName) + if (file.fileNameExt == prLastSelectedFileName) { startMultiSelection = true; prAddFileNameInSelection(prLastSelectedFileName, false); @@ -2101,13 +2048,13 @@ namespace IGFD { if (puDLGcountSelectionMax == 0) // infinite selection { - prAddFileNameInSelection(file->fileNameExt, false); + prAddFileNameInSelection(file.fileNameExt, false); } else // selection limited by size { if (prSelectedFileNames.size() < puDLGcountSelectionMax) { - prAddFileNameInSelection(file->fileNameExt, false); + prAddFileNameInSelection(file.fileNameExt, false); } else { @@ -2119,7 +2066,7 @@ namespace IGFD } } - if (file->fileNameExt == fileNameToSelect) + if (file.fileNameExt == fileNameToSelect) { if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse { @@ -2145,7 +2092,7 @@ namespace IGFD { prSelectedFileNames.clear(); IGFD::Utils::ResetBuffer(puFileNameBuffer); - prAddFileNameInSelection(vInfos->fileNameExt, true); + prAddFileNameInSelection(vInfos.fileNameExt, true); } } @@ -3922,11 +3869,8 @@ namespace IGFD } // returns 0 if not break loop, 1 if break loop, 2 if exit dialog - int IGFD::FileDialog::prSelectableItem(int vidx, std::shared_ptr vInfos, bool vSelected, const char* vFmt, ...) + int IGFD::FileDialog::prSelectableItem(int vidx, const FileInfos& vInfos, bool vSelected, const char* vFmt, ...) { - if (!vInfos.use_count()) - return 0; - auto& fdi = prFileDialogInternal.puFileManager; static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | @@ -3957,7 +3901,7 @@ namespace IGFD #endif // USE_EXPLORATION_BY_KEYS if (res) { - if (vInfos->fileType == 'd') + if (vInfos.fileType == 'd') { bool isSelectingDir=false; // nav system, selectebale cause open directory or select directory @@ -4012,27 +3956,27 @@ namespace IGFD return 0; } - void IGFD::FileDialog::prBeginFileColorIconStyle(std::shared_ptr vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) + void IGFD::FileDialog::prBeginFileColorIconStyle(const FileInfos& vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) { vOutStr.clear(); vOutShowColor = false; - if (vFileInfos->fileStyle.use_count()) //-V807 //-V522 + if (vFileInfos.fileStyle.use_count()) //-V807 //-V522 { vOutShowColor = true; - *vOutFont = vFileInfos->fileStyle->font; + *vOutFont = vFileInfos.fileStyle->font; } - if (vOutShowColor && !vFileInfos->fileStyle->icon.empty()) vOutStr = vFileInfos->fileStyle->icon; - else if (vFileInfos->fileType == 'd') vOutStr = dirEntryString; - else if (vFileInfos->fileType == 'l') vOutStr = linkEntryString; - else if (vFileInfos->fileType == 'f') vOutStr = fileEntryString; + if (vOutShowColor && !vFileInfos.fileStyle->icon.empty()) vOutStr = vFileInfos.fileStyle->icon; + else if (vFileInfos.fileType == 'd') vOutStr = dirEntryString; + else if (vFileInfos.fileType == 'l') vOutStr = linkEntryString; + else if (vFileInfos.fileType == 'f') vOutStr = fileEntryString; - vOutStr += " " + vFileInfos->fileNameExt; + vOutStr += " " + vFileInfos.fileNameExt; if (vOutShowColor) - ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color); + ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos.fileStyle->color); if (*vOutFont) ImGui::PushFont(*vOutFont); } @@ -4126,13 +4070,13 @@ namespace IGFD { if (i < 0) continue; - auto infos = fdi.GetFilteredFileAt((size_t)i); - if (!infos.use_count()) + const FileInfos& infos = fdi.GetFilteredFileAt((size_t)i); + if (!infos.isValid) continue; prBeginFileColorIconStyle(infos, _showColor, _str, &_font); - bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found + bool selected = fdi.IsFileNameSelected(infos.fileNameExt); // found ImGui::TableNextRow(); @@ -4147,13 +4091,13 @@ namespace IGFD } if (ImGui::TableNextColumn()) // file type { - ImGui::Text("%s", infos->fileExt.c_str()); + ImGui::Text("%s", infos.fileExt.c_str()); } if (ImGui::TableNextColumn()) // file size { - if (infos->fileType != 'd') + if (infos.fileType != 'd') { - ImGui::Text("%s ", infos->formatedFileSize.c_str()); + ImGui::Text("%s ", infos.formatedFileSize.c_str()); } else { @@ -4162,7 +4106,7 @@ namespace IGFD } if (ImGui::TableNextColumn()) // file date + time { - ImGui::Text("%s", infos->fileModifDate.c_str()); + ImGui::Text("%s", infos.fileModifDate.c_str()); } prEndFileColorIconStyle(_showColor, _font); diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 93db26e9d..0850cd680 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -766,7 +766,7 @@ namespace IGFD void ParseFilters(const char* vFilters); // Parse filter syntax, detect and parse filter collection void SetSelectedFilterWithExt(const std::string& vFilter); // Select filter - bool prFillFileStyle(std::shared_ptr vFileInfos) const; // fill with the good style + bool prFillFileStyle(FileInfos& vFileInfos) const; // fill with the good style void SetFileStyle( const IGFD_FileStyleFlags& vFlags, @@ -812,6 +812,7 @@ namespace IGFD #ifdef USE_THUMBNAILS IGFD_Thumbnail_Info thumbnailInfo; // structre for the display for image file tetxure #endif // USE_THUMBNAILS + bool isValid = true; public: bool IsTagFound(const std::string& vTag) const; @@ -824,6 +825,7 @@ namespace IGFD class FileManager { public: // types + FileInfos invalidFile; enum class SortingFieldEnum // sorting for filetering of the file lsit { FIELD_NONE = 0, // no sorting preference, result indetermined haha.. @@ -839,8 +841,8 @@ namespace IGFD private: std::string prCurrentPath; // current path (to be decomposed in prCurrentPathDecomposition std::vector prCurrentPathDecomposition; // part words - std::vector> prFileList; // base container - std::vector> prFilteredFileList; // filtered container (search, sorting, etc..) + std::vector prFileList; // base container + std::vector prFilteredFileList; // filtered container (search, sorting, etc..) std::string prLastSelectedFileName; // for shift multi selection std::set prSelectedFileNames; // the user selection of FilePathNames bool prCreateDirectoryMode = false; // for create directory widget @@ -879,11 +881,11 @@ namespace IGFD static std::string prRoundNumber(double vvalue, int n); // custom rounding number static std::string prFormatFileSize(size_t vByteSize); // format file size field static std::string prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt); // turn all text in lower case for search facilitie - static void prCompleteFileInfos(const std::shared_ptr& FileInfos); // set time and date infos of a file (detail view mode) + static void prCompleteFileInfos(FileInfos& FileInfos); // set time and date infos of a file (detail view mode) void prRemoveFileNameInSelection(const std::string& vFileName); // selection : remove a file name void prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName); // selection : add a file name void AddFile(const FileDialogInternal& vFileDialogInternal, - const std::string& vPath, const std::string& vFileName, const char& vFileType, void* ent); // add file called by scandir + const std::string& vPath, const std::string& vFileName, char vFileType, void* ent); // add file called by scandir public: FileManager(); @@ -892,9 +894,9 @@ namespace IGFD bool IsFileListEmpty(); bool IsFilteredListEmpty(); size_t GetFullFileListSize(); - std::shared_ptr GetFullFileAt(size_t vIdx); + const FileInfos& GetFullFileAt(size_t vIdx); size_t GetFilteredListSize(); - std::shared_ptr GetFilteredFileAt(size_t vIdx); + const FileInfos& GetFilteredFileAt(size_t vIdx); bool IsFileNameSelected(const std::string& vFileName); std::string GetBack(); void ClearComposer(); @@ -912,9 +914,9 @@ namespace IGFD void SetCurrentPath(const std::string& vCurrentPath); // set the current path static bool IsFileExist(const std::string& vFile); void SetDefaultFileName(const std::string& vFileName); - bool SelectDirectory(const std::shared_ptr& vInfos); // enter directory + bool SelectDirectory(const FileInfos& vInfos); // enter directory void SelectFileName(const FileDialogInternal& vFileDialogInternal, - const std::shared_ptr& vInfos); // select filename + const FileInfos& vInfos); // select filename //depend of dirent.h void SetCurrentDir(const std::string& vPath); // define current directory for scan @@ -1311,7 +1313,7 @@ namespace IGFD // widgets components virtual void prDrawSidePane(float vHeight); // draw side pane virtual int prSelectableItem(int vidx, - std::shared_ptr vInfos, + const FileInfos& vInfos, bool vSelected, const char* vFmt, ...); // draw a custom selectable behavior item virtual bool prDrawFileListView(ImVec2 vSize); // draw file list view (default mode) @@ -1325,7 +1327,7 @@ namespace IGFD // - prDrawThumbnailsListView // - prDrawThumbnailsGridView void prBeginFileColorIconStyle( - std::shared_ptr vFileInfos, + const FileInfos& vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont); // begin style apply of filter with color an icon if any diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp index 59d8af8f7..cd6fe8e89 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -226,7 +226,7 @@ void k053260_core::voice_t::write(u8 address, u8 data) m_start = (m_start & ~0x00ff00) | (u32(data) << 8); break; case 6: // start address bit 16-20 - m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 16, 5)) << 16); + m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 0, 5)) << 16); break; case 7: // volume m_volume = bitfield(data, 0, 7); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1b9f427c8..d27b6a57e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3851,6 +3851,23 @@ void DivEngine::delSample(int index) { song.sampleLen=song.sample.size(); removeAsset(song.sampleDir,index); checkAssetDir(song.sampleDir,song.sample.size()); + + // compensate + for (DivInstrument* i: song.ins) { + if (i->amiga.initSample==index) { + i->amiga.initSample=-1; + } else if (i->amiga.initSample>index) { + i->amiga.initSample--; + } + for (int j=0; j<120; j++) { + if (i->amiga.noteMap[j].map==index) { + i->amiga.noteMap[j].map=-1; + } else if (i->amiga.noteMap[j].map>index) { + i->amiga.noteMap[j].map--; + } + } + } + renderSamples(); } saveLock.unlock(); @@ -4042,6 +4059,27 @@ void DivEngine::exchangeIns(int one, int two) { } } +void DivEngine::exchangeWave(int one, int two) { + // TODO +} + +void DivEngine::exchangeSample(int one, int two) { + for (DivInstrument* i: song.ins) { + if (i->amiga.initSample==one) { + i->amiga.initSample=two; + } else if (i->amiga.initSample==two) { + i->amiga.initSample=one; + } + for (int j=0; j<120; j++) { + if (i->amiga.noteMap[j].map==one) { + i->amiga.noteMap[j].map=two; + } else if (i->amiga.noteMap[j].map==two) { + i->amiga.noteMap[j].map=one; + } + } + } +} + bool DivEngine::moveInsUp(int which) { if (which<1 || which>=(int)song.ins.size()) return false; BUSY_BEGIN; @@ -4064,6 +4102,7 @@ bool DivEngine::moveWaveUp(int which) { song.wave[which]=song.wave[which-1]; song.wave[which-1]=prev; moveAsset(song.waveDir,which,which-1); + exchangeWave(which,which-1); saveLock.unlock(); BUSY_END; return true; @@ -4080,6 +4119,7 @@ bool DivEngine::moveSampleUp(int which) { song.sample[which]=song.sample[which-1]; song.sample[which-1]=prev; moveAsset(song.sampleDir,which,which-1); + exchangeSample(which,which-1); saveLock.unlock(); renderSamples(); BUSY_END; @@ -4107,6 +4147,7 @@ bool DivEngine::moveWaveDown(int which) { saveLock.lock(); song.wave[which]=song.wave[which+1]; song.wave[which+1]=prev; + exchangeWave(which,which+1); moveAsset(song.waveDir,which,which+1); saveLock.unlock(); BUSY_END; @@ -4123,6 +4164,7 @@ bool DivEngine::moveSampleDown(int which) { saveLock.lock(); song.sample[which]=song.sample[which+1]; song.sample[which+1]=prev; + exchangeSample(which,which+1); moveAsset(song.sampleDir,which,which+1); saveLock.unlock(); renderSamples(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 87ff7116e..0f13659b8 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -531,6 +531,9 @@ class DivEngine { void initSongWithDesc(const char* description, bool inBase64=true, bool oldVol=false); void exchangeIns(int one, int two); + void exchangeWave(int one, int two); + void exchangeSample(int one, int two); + void swapChannels(int src, int dest); void stompChannel(int ch); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 6e87a4431..0ba5a1327 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -142,76 +142,78 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } // compatibility flags - ds.limitSlides=true; - ds.linearPitch=1; - ds.loopModality=0; - ds.properNoiseLayout=false; - ds.waveDutyIsVol=false; - // TODO: WHAT?! geodude.dmf fails when this is true - // but isn't that how Defle behaves??? - ds.resetMacroOnPorta=false; - ds.legacyVolumeSlides=true; - ds.compatibleArpeggio=true; - ds.noteOffResetsSlides=true; - ds.targetResetsSlides=true; - ds.arpNonPorta=false; - ds.algMacroBehavior=false; - ds.brokenShortcutSlides=false; - ds.ignoreDuplicateSlides=true; - ds.brokenDACMode=true; - ds.oneTickCut=false; - ds.newInsTriggersInPorta=true; - ds.arp0Reset=true; - ds.brokenSpeedSel=true; - ds.noSlidesOnFirstTick=false; - ds.rowResetsArpPos=false; - ds.ignoreJumpAtEnd=true; - ds.buggyPortaAfterSlide=true; - ds.gbInsAffectsEnvelope=true; - ds.ignoreDACModeOutsideIntendedChannel=false; - ds.e1e2AlsoTakePriority=true; - ds.fbPortaPause=true; - ds.snDutyReset=true; - ds.oldOctaveBoundary=false; - ds.noOPN2Vol=true; - ds.newVolumeScaling=false; - ds.volMacroLinger=false; - ds.brokenOutVol=true; - ds.brokenOutVol2=true; - ds.e1e2StopOnSameNote=true; - ds.brokenPortaArp=false; - ds.snNoLowPeriods=true; - ds.disableSampleMacro=true; - ds.delayBehavior=0; - ds.jumpTreatment=2; + if (!getConfInt("noDMFCompat",0)) { + ds.limitSlides=true; + ds.linearPitch=1; + ds.loopModality=0; + ds.properNoiseLayout=false; + ds.waveDutyIsVol=false; + // TODO: WHAT?! geodude.dmf fails when this is true + // but isn't that how Defle behaves??? + ds.resetMacroOnPorta=false; + ds.legacyVolumeSlides=true; + ds.compatibleArpeggio=true; + ds.noteOffResetsSlides=true; + ds.targetResetsSlides=true; + ds.arpNonPorta=false; + ds.algMacroBehavior=false; + ds.brokenShortcutSlides=false; + ds.ignoreDuplicateSlides=true; + ds.brokenDACMode=true; + ds.oneTickCut=false; + ds.newInsTriggersInPorta=true; + ds.arp0Reset=true; + ds.brokenSpeedSel=true; + ds.noSlidesOnFirstTick=false; + ds.rowResetsArpPos=false; + ds.ignoreJumpAtEnd=true; + ds.buggyPortaAfterSlide=true; + ds.gbInsAffectsEnvelope=true; + ds.ignoreDACModeOutsideIntendedChannel=false; + ds.e1e2AlsoTakePriority=true; + ds.fbPortaPause=true; + ds.snDutyReset=true; + ds.oldOctaveBoundary=false; + ds.noOPN2Vol=true; + ds.newVolumeScaling=false; + ds.volMacroLinger=false; + ds.brokenOutVol=true; + ds.brokenOutVol2=true; + ds.e1e2StopOnSameNote=true; + ds.brokenPortaArp=false; + ds.snNoLowPeriods=true; + ds.disableSampleMacro=true; + ds.delayBehavior=0; + ds.jumpTreatment=2; - // 1.1 compat flags - if (ds.version>24) { - ds.waveDutyIsVol=true; - ds.legacyVolumeSlides=false; - } + // 1.1 compat flags + if (ds.version>24) { + ds.waveDutyIsVol=true; + ds.legacyVolumeSlides=false; + } - // Neo Geo detune is caused by Defle running Neo Geo at the wrong clock. - /* - if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT - || ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT - || ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) { - ds.tuning=443.23; - } - */ + // Neo Geo detune is caused by Defle running Neo Geo at the wrong clock. + /* + if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT + || ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT + || ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) { + ds.tuning=443.23; + } + */ - // Genesis detuned on Defle v10 and earlier - /*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) { - ds.tuning=443.23; - }*/ - // C64 detuned on Defle v11 and earlier - /*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) { - ds.tuning=433.2; - }*/ + // Genesis detuned on Defle v10 and earlier + /*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) { + ds.tuning=443.23; + }*/ + // C64 detuned on Defle v11 and earlier + /*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) { + ds.tuning=433.2; + }*/ - // Game Boy arp+soundLen screwery - if (ds.system[0]==DIV_SYSTEM_GB) { - ds.systemFlags[0].set("enoughAlready",true); + // Game Boy arp+soundLen screwery + if (ds.system[0]==DIV_SYSTEM_GB) { + ds.systemFlags[0].set("enoughAlready",true); + } } logI("reading module data..."); @@ -869,7 +871,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { if (ds.version>0x15) { sample->depth=(DivSampleDepth)reader.readC(); if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) { - logW("%d: sample depth is wrong! (%d)",i,sample->depth); + logW("%d: sample depth is wrong! (%d)",i,(int)sample->depth); sample->depth=DIV_SAMPLE_DEPTH_16BIT; } } else { @@ -1937,8 +1939,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.system[i]=systemFromFileFur(sysID); logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i])); if (sysID!=0 && systemToFileFur(ds.system[i])==0) { - logE("unrecognized system ID %.2x",ds.system[i]); - lastError=fmt::sprintf("unrecognized system ID %.2x!",ds.system[i]); + logE("unrecognized system ID %.2x",sysID); + lastError=fmt::sprintf("unrecognized system ID %.2x!",sysID); delete[] file; return false; } diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index c44cdb0cf..4f8745bda 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -360,26 +360,6 @@ void DivPlatformES5506::tick(bool sysTick) { } } } - if (chan[i].pcm.isNoteMap) { - // note map macros - if (chan[i].std.wave.had) { - if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<120) { - if (chan[i].pcm.next!=chan[i].std.wave.val) { - chan[i].pcm.next=chan[i].std.wave.val; - chan[i].pcmChanged.index=1; - } - } - } - } else if (!chan[i].pcm.isNoteMap) { - if (chan[i].std.wave.had) { - if (chan[i].std.wave.val>=0 && chan[i].std.wave.valsong.sampleLen) { - if (chan[i].pcm.next!=chan[i].std.wave.val) { - chan[i].pcm.next=chan[i].std.wave.val; - chan[i].pcmChanged.index=1; - } - } - } - } // update registers if (chan[i].volChanged.changed) { // calculate volume (16 bit) @@ -432,7 +412,7 @@ void DivPlatformES5506::tick(bool sysTick) { off=(double)center/8363.0; } if (ins->amiga.useNoteMap) { - chan[i].pcm.note=next; + //chan[i].pcm.note=next; } // get loop mode DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX; @@ -748,13 +728,13 @@ int DivPlatformES5506::dispatch(DivCommand c) { if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) || ((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSamplesong.sampleLen))) { int sample=ins->amiga.getSample(c.value); - c.value=ins->amiga.getFreq(c.value); if (sample>=0 && samplesong.sampleLen) { sampleValid=true; chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff; chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff; - chan[c.chan].pcm.note=c.value; chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample; + c.value=ins->amiga.getFreq(c.value); + chan[c.chan].pcm.note=c.value; chan[c.chan].filter=ins->es5506.filter; chan[c.chan].envelope=ins->es5506.envelope; } @@ -870,20 +850,6 @@ int DivPlatformES5506::dispatch(DivCommand c) { chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; - // sample commands - case DIV_CMD_WAVE: - if (!chan[c.chan].useWave) { - if (chan[c.chan].active) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506); - if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) || - ((!ins->amiga.useNoteMap) && (c.value>=0 && c.valuesong.sampleLen))) { - chan[c.chan].pcm.next=c.value; - chan[c.chan].pcmChanged.index=1; - } - } - } - // reserved for useWave - break; // Filter commands case DIV_CMD_ES5506_FILTER_MODE: if (!chan[c.chan].active) { diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index c0fb7dd2f..9be217b94 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -105,15 +105,17 @@ class DivPlatformFMBase: public DivDispatch { } } } + // only used by OPN2 for DAC writes inline void urgentWrite(unsigned short a, unsigned char v) { if (!skipRegisterWrites && !flushFirst) { - if (writes.empty()) { - writes.push_back(QueuedWrite(a,v)); - } else if (writes.size()>16 || writes.front().addrOrVal) { - writes.push_back(QueuedWrite(a,v)); - } else { - writes.push_front(QueuedWrite(a,v)); + if (!writes.empty()) { + // check for hard reset + if (writes.front().addr==0xf0) { + // replace hard reset with DAC write + writes.pop_front(); + } } + writes.push_front(QueuedWrite(a,v)); if (dumpWrites) { addWrite(a,v); } diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index cec83e6df..7e2086db0 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -81,7 +81,6 @@ void DivPlatformGB::acquire(short** buf, size_t len) { } void DivPlatformGB::updateWave() { - logV("WAVE UPDATE"); rWrite(0x1a,0); for (int i=0; i<16; i++) { int nibble1=ws.output[((i<<1)+antiClickWavePos)&31]; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 20e87d618..9a8bafc94 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -96,32 +96,22 @@ void DivPlatformGenesis::processDAC(int iRate) { //sample>>=1; if (sample<-128) sample=-128; if (sample>127) sample=127; - urgentWrite(0x2a,(unsigned char)sample+0x80); + dacWrite=(unsigned char)(sample+0x80); } } else { - if (!chan[5].dacReady) { - chan[5].dacDelay+=32000; - if (chan[5].dacDelay>=iRate) { - chan[5].dacDelay-=iRate; - chan[5].dacReady=true; - } - } if (chan[5].dacMode && chan[5].dacSample!=-1) { chan[5].dacPeriod+=chan[5].dacRate; if (chan[5].dacPeriod>=iRate) { DivSample* s=parent->getSample(chan[5].dacSample); if (s->samples>0 && chan[5].dacPossamples) { if (!isMuted[5]) { - if (chan[5].dacReady && writes.size()<16) { - int sample; - if (parent->song.noOPN2Vol) { - sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]; - } else { - sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7; - } - urgentWrite(0x2a,(unsigned char)sample+0x80); - chan[5].dacReady=false; + int sample; + if (parent->song.noOPN2Vol) { + sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]; + } else { + sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7; } + dacWrite=(unsigned char)(sample+0x80); } chan[5].dacPos++; if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->loopEnd)) { @@ -151,24 +141,34 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { os[0]=0; os[1]=0; for (int i=0; i<6; i++) { if (!writes.empty()) { - if (--delay<0) { - delay=0; - QueuedWrite& w=writes.front(); - if (w.addrOrVal) { - //logV("%.3x = %.2x",w.addr,w.val); - OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); - lastBusy=0; - regPool[w.addr&0x1ff]=w.val; - writes.pop_front(); - } else { - lastBusy++; - if (fm.write_busy==0) { - OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); - w.addrOrVal=true; + QueuedWrite& w=writes.front(); + if (w.addrOrVal) { + //logV("%.3x = %.2x",w.addr,w.val); + OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + + if (dacWrite>=0) { + if (!canWriteDAC) { + canWriteDAC=true; + } else { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + canWriteDAC=writes.empty(); } } + } else { + if (fm.write_busy==0) { + OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } } } else { + canWriteDAC=true; + if (dacWrite>=0) { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + } flushFirst=false; } @@ -227,8 +227,22 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val); regPool[w.addr&0x1ff]=w.val; writes.pop_front(); - lastBusy=1; + + if (dacWrite>=0) { + if (!canWriteDAC) { + canWriteDAC=true; + } else { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + canWriteDAC=writes.empty(); + } + } } else { + canWriteDAC=true; + if (dacWrite>=0) { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + } flushFirst=false; } @@ -288,6 +302,11 @@ void DivPlatformGenesis::fillStream(std::vector& stream, int sR for (size_t i=0; i=0) { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + } + while (!writes.empty()) { QueuedWrite& w=writes.front(); stream.push_back(DivDelayedWrite(i,w.addr,w.val)); @@ -1314,11 +1333,12 @@ void DivPlatformGenesis::reset() { pendingWrites[i]=-1; } - lastBusy=60; lfoValue=8; softPCMTimer=0; extMode=false; flushFirst=false; + dacWrite=-1; + canWriteDAC=true; if (softPCM) { chan[5].dacMode=true; @@ -1330,8 +1350,6 @@ void DivPlatformGenesis::reset() { // LFO immWrite(0x22,lfoValue); - - delay=0; } int DivPlatformGenesis::getOutputCount() { diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index c9de0493f..d618c6892 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -55,7 +55,6 @@ class DivPlatformGenesis: public DivPlatformOPN { unsigned int dacPos; int dacSample; int dacDelay; - bool dacReady; bool dacDirection; bool setPos; unsigned char sampleBank; @@ -69,7 +68,6 @@ class DivPlatformGenesis: public DivPlatformOPN { dacPos(0), dacSample(-1), dacDelay(0), - dacReady(true), dacDirection(false), setPos(false), sampleBank(0), @@ -86,8 +84,9 @@ class DivPlatformGenesis: public DivPlatformOPN { int softPCMTimer; - bool extMode, softPCM, noExtMacros, useYMFM; + bool extMode, softPCM, noExtMacros, useYMFM, canWriteDAC; unsigned char chipType; + short dacWrite; unsigned char dacVolTable[128]; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d4ac27603..56ae1ceaf 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -19,6 +19,7 @@ #include "genesisext.h" #include "../engine.h" +#include "../../ta-log.h" #include #define CHIP_FREQBASE fmFreqBase @@ -476,13 +477,6 @@ void DivPlatformGenesisExt::tick(bool sysTick) { if (chan[csmChan].active) { // CSM writeMask^=0xf0; } - /*printf( - "Mask: %c %c %c %c\n", - (writeMask&0x10)?'1':'-', - (writeMask&0x20)?'2':'-', - (writeMask&0x40)?'3':'-', - (writeMask&0x80)?'4':'-' - );*/ immWrite(0x28,writeMask); } } @@ -518,6 +512,39 @@ void DivPlatformGenesisExt::tick(bool sysTick) { opChan[i].freqChanged=true; } + // channel macros + if (opChan[i].std.alg.had) { + chan[extChanOffs].state.alg=opChan[i].std.alg.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j]; + if (isOpMuted[j] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + } + } + } + if (i==0 || fbAllOps) { + if (opChan[i].std.fb.had) { + chan[extChanOffs].state.fb=opChan[i].std.fb.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + } + } + if (opChan[i].std.fms.had) { + chan[extChanOffs].state.fms=opChan[i].std.fms.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ams.had) { + chan[extChanOffs].state.ams=opChan[i].std.ams.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ex3.had) { + lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]]; @@ -576,6 +603,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { bool writeNoteOn=false; unsigned char writeMask=2; + unsigned char hardResetMask=0; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -603,8 +631,13 @@ void DivPlatformGenesisExt::tick(bool sysTick) { writeNoteOn=true; if (opChan[i].mask) { writeMask|=1<<(4+i); + if (opChan[i].hardReset) { + hardResetMask|=1<<(4+i); + } + } + if (!opChan[i].hardReset) { + opChan[i].keyOn=false; } - opChan[i].keyOn=false; } } @@ -631,14 +664,9 @@ void DivPlatformGenesisExt::tick(bool sysTick) { if (chan[csmChan].active) { // CSM writeMask^=0xf0; } - /*printf( - "Mask: %c %c %c %c\n", - (writeMask&0x10)?'1':'-', - (writeMask&0x20)?'2':'-', - (writeMask&0x40)?'3':'-', - (writeMask&0x80)?'4':'-' - );*/ + writeMask^=hardResetMask; immWrite(0x28,writeMask); + writeMask^=hardResetMask; // hard reset handling if (mustHardReset) { @@ -651,6 +679,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i]; immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + opChan[i].keyOn=false; } } immWrite(0x28,writeMask); diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp index 7ba1cc2ca..5791d2426 100644 --- a/src/engine/platform/k053260.cpp +++ b/src/engine/platform/k053260.cpp @@ -145,15 +145,15 @@ void DivPlatformK053260::tick(bool sysTick) { off=8363.0/s->centerRate; } } - DivSample* s=parent->getSample(chan[i].sample); + DivSample* s=parent->getSample(sample); chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { unsigned int start=0; unsigned int length=0; - if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { - start=sampleOffK053260[chan[i].sample]; + if (sample>=0 && samplesong.sampleLen) { + start=sampleOffK053260[sample]; length=s->length8; if (chan[i].reverse) { start+=length; @@ -163,8 +163,7 @@ void DivPlatformK053260::tick(bool sysTick) { if (chan[i].audPos>0) { if (chan[i].reverse) { start=start-MIN(chan[i].audPos,s->length8); - } - else { + } else { start=start+MIN(chan[i].audPos,s->length8); } length=MAX(1,length-chan[i].audPos); diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index fbede0b59..e5de16456 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -386,6 +386,9 @@ int DivPlatformMSM6258::init(DivEngine* p, int channels, int sugRate, const DivC oscBuf[i]=new DivDispatchOscBuffer; } msm=new okim6258_device(4000000); + msm->set_start_div(okim6258_device::FOSC_DIV_BY_1024); + msm->set_type(okim6258_device::TYPE_4BITS); + msm->set_outbits(okim6258_device::OUTPUT_12BITS); msm->device_start(); setFlags(flags); reset(); diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index 2c18d90c4..6be120c2b 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -47,7 +47,6 @@ class DivPlatformMSM6258: public DivDispatch { }; FixedQueue writes; okim6258_device* msm; - unsigned char lastBusy; unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel; signed char msmDividerCount, msmClockCount; diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 5b804b23f..5ac493f19 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -571,7 +571,7 @@ void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) { chipClock=3072000; CHECK_CUSTOM_CLOCK; rate=chipClock/32; - namco->device_clock_changed(rate); + namco->device_clock_changed(96000); for (int i=0; irate=rate; } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 8fac52356..657a084e0 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -130,9 +130,9 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) { for (size_t i=0; iTick(1); - nes2_NP->TickFrameSequence(1); - nes2_NP->Tick(1); + nes1_NP->Tick(8); + nes2_NP->TickFrameSequence(8); + nes2_NP->Tick(8); nes1_NP->Render(out1); nes2_NP->Render(out2); @@ -140,7 +140,7 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) { if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; buf[0][i]=sample; - if (++writeOscBuf>=32) { + if (++writeOscBuf>=4) { writeOscBuf=0; oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11; oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11; @@ -332,7 +332,7 @@ void DivPlatformNES::tick(bool sysTick) { if (chan[4].keyOn) { if (dpcmMode && !skipRegisterWrites && dacSample>=0 && dacSamplesong.sampleLen) { unsigned int dpcmAddr=sampleOffDPCM[dacSample]; - unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; + unsigned int dpcmLen=parent->getSample(dacSample)->lengthDPCM>>4; if (dpcmLen>255) dpcmLen=255; goingToLoop=parent->getSample(dacSample)->isLoopable(); // write DPCM @@ -749,8 +749,11 @@ void DivPlatformNES::setFlags(const DivConfig& flags) { } CHECK_CUSTOM_CLOCK; rate=chipClock; + if (useNP) { + rate/=8; + } for (int i=0; i<5; i++) { - oscBuf[i]->rate=rate/32; + oscBuf[i]->rate=rate/(useNP?4:32); } dpcmModeDefault=flags.getBool("dpcmMode",true); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index b0012c224..1d72760a6 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -392,6 +392,15 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) { } } +void DivPlatformOPLL::switchMode(bool mode) { + if (mode==properDrums) return; + if (mode) { + + } else { + + } +} + int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -773,18 +782,10 @@ int DivPlatformOPLL::dispatch(DivCommand c) { if ((int)properDrums==c.value) break; if (c.value) { properDrums=true; - immWrite(0x0e,0x20); - drumState=0; } else { properDrums=false; - immWrite(0x0e,0x00); - drumState=0; } - chan[6].freqChanged=true; - chan[7].freqChanged=true; - chan[8].freqChanged=true; - chan[9].freqChanged=true; - chan[10].freqChanged=true; + switchMode(properDrums); break; case DIV_CMD_MACRO_OFF: chan[c.chan].std.mask(c.value,true); diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 7333f7298..f5d79b39c 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -75,6 +75,7 @@ class DivPlatformOPLL: public DivDispatch { int octave(int freq); int toFreq(int freq); void commitState(int ch, DivInstrument* ins); + void switchMode(bool mode); friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index a275c6350..593c41b8a 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -17,9 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _USE_MATH_DEFINES #include "pcspkr.h" #include "../engine.h" #include "../../ta-log.h" + #include #ifdef __linux__ @@ -190,9 +192,6 @@ const char** DivPlatformPCSpeaker::getRegisterSheet() { return regCheatSheetPCSpeaker; } -const float cut=0.05; -const float reso=0.06; - void DivPlatformPCSpeaker::acquire_unfilt(short** buf, size_t len) { int out=0; for (size_t i=0; i((freq+16)>>1) && !isMuted[0])?1:0; - low+=0.04*band; - band+=0.04*(next-low-band); + low+=cut*band; + band+=cut*(next-low-band); float out=(low+band)*0.75; if (out>1.0) out=1.0; if (out<-1.0) out=-1.0; @@ -612,6 +611,17 @@ void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) { rate=chipClock/PCSPKR_DIVIDER; speakerType=flags.getInt("speakerType",0)&3; oscBuf->rate=rate; + + switch (speakerType) { + case 1: + cut=2.0*sin(M_PI*1900.0/rate); + reso=0.0; + break; + default: + cut=2.0*sin(M_PI*2375.0/rate); + reso=0.06; + break; + } } void DivPlatformPCSpeaker::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 0437a90c4..d9315b5ec 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -57,6 +57,9 @@ class DivPlatformPCSpeaker: public DivDispatch { float low, band; float low2, high2, band2; float low3, band3; + float cut; + float reso; + unsigned short freq, lastFreq; unsigned char regPool[2]; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 640f364ea..76aa62fcb 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -296,6 +296,10 @@ void DivPlatformSMS::tick(bool sysTick) { rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15)))); chan[i].writeVol=false; } + if (chan[i].keyOff) { + rWrite(0,0x9f|i<<5); + chan[i].keyOff=false; + } } } @@ -309,6 +313,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { chan[c.chan].actualNote=c.value; } chan[c.chan].active=true; + chan[c.chan].keyOff=false; //if (!parent->song.brokenOutVol2) { chan[c.chan].writeVol=true; chan[c.chan].outVol=chan[c.chan].vol; @@ -321,7 +326,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; - rWrite(0,0x9f|c.chan<<5); + chan[c.chan].keyOff=true; chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index 43401ebcb..a03121613 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -733,6 +733,7 @@ int DivPlatformSNES::getRegisterPoolSize() { void DivPlatformSNES::initEcho() { unsigned char esa=0xf8-(echoDelay<<3); + unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20); if (echoOn) { rWrite(0x6d,esa); rWrite(0x7d,echoDelay); @@ -742,13 +743,14 @@ void DivPlatformSNES::initEcho() { for (int i=0; i<8; i++) { rWrite(0x0f+(i<<4),echoFIR[i]); } + rWrite(0x6c,control); } else { - rWrite(0x6d,0); - rWrite(0x7d,0); rWrite(0x2c,0); rWrite(0x3c,0); + rWrite(0x6c,control); + rWrite(0x7d,0); + rWrite(0x6d,0xff); } - writeControl=true; } void DivPlatformSNES::reset() { diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 1836bee10..86038216a 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -356,12 +356,12 @@ void DivPlatformTIA::poke(std::vector& wlist) { void DivPlatformTIA::setFlags(const DivConfig& flags) { if (flags.getInt("clockSel",0)) { - rate=COLOR_PAL*4.0/5.0; + chipClock=COLOR_PAL*4.0/5.0; } else { - rate=COLOR_NTSC; + chipClock=COLOR_NTSC; } CHECK_CUSTOM_CLOCK; - chipClock=rate; + rate=chipClock; mixingType=flags.getInt("mixingType",0)&3; for (int i=0; i<2; i++) { oscBuf[i]->rate=rate/114; diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 6398ba7f1..8bad13d48 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -355,6 +355,9 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_HARD_RESET: + opChan[ch].hardReset=c.value; + break; case DIV_CMD_GET_VOLMAX: return 127; break; @@ -385,6 +388,9 @@ static int opChanOffsH[4]={ }; void DivPlatformYM2203Ext::tick(bool sysTick) { + int hardResetElapsed=0; + bool mustHardReset=false; + if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -395,6 +401,12 @@ void DivPlatformYM2203Ext::tick(bool sysTick) { writeMask&=~(1<<(4+i)); opChan[i].keyOff=false; } + if (opChan[i].hardReset && opChan[i].keyOn) { + mustHardReset=true; + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i]; + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; + } } if (writeSomething) { immWrite(0x28,writeMask); @@ -432,6 +444,27 @@ void DivPlatformYM2203Ext::tick(bool sysTick) { opChan[i].freqChanged=true; } + // channel macros + if (opChan[i].std.alg.had) { + chan[extChanOffs].state.alg=opChan[i].std.alg.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j]; + if (isOpMuted[j] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + } + } + } + if (i==0 || fbAllOps) { + if (opChan[i].std.fb.had) { + chan[extChanOffs].state.fb=opChan[i].std.fb.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + } + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]]; @@ -491,6 +524,7 @@ void DivPlatformYM2203Ext::tick(bool sysTick) { bool writeNoteOn=false; unsigned char writeMask=2; + unsigned char hardResetMask=0; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -517,12 +551,36 @@ void DivPlatformYM2203Ext::tick(bool sysTick) { writeNoteOn=true; if (opChan[i].mask) { writeMask|=1<<(4+i); + if (opChan[i].hardReset) { + hardResetMask|=1<<(4+i); + } + } + if (!opChan[i].hardReset) { + opChan[i].keyOn=false; } - opChan[i].keyOn=false; } } if (writeNoteOn) { + writeMask^=hardResetMask; immWrite(0x28,writeMask); + writeMask^=hardResetMask; + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; isong.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j]; + if (isOpMuted[j] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + } + } + } + if (i==0 || fbAllOps) { + if (opChan[i].std.fb.had) { + chan[extChanOffs].state.fb=opChan[i].std.fb.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + } + } + if (opChan[i].std.fms.had) { + chan[extChanOffs].state.fms=opChan[i].std.fms.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ams.had) { + chan[extChanOffs].state.ams=opChan[i].std.ams.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ex3.had) { + lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } + + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]]; @@ -513,6 +559,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) { bool writeNoteOn=false; unsigned char writeMask=2; + unsigned char hardResetMask=0; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -539,12 +586,36 @@ void DivPlatformYM2608Ext::tick(bool sysTick) { writeNoteOn=true; if (opChan[i].mask) { writeMask|=1<<(4+i); + if (opChan[i].hardReset) { + hardResetMask|=1<<(4+i); + } + } + if (!opChan[i].hardReset) { + opChan[i].keyOn=false; } - opChan[i].keyOn=false; } } if (writeNoteOn) { + writeMask^=hardResetMask; immWrite(0x28,writeMask); + writeMask^=hardResetMask; + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; isong.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j]; + if (isOpMuted[j] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + } + } + } + if (i==0 || fbAllOps) { + if (opChan[i].std.fb.had) { + chan[extChanOffs].state.fb=opChan[i].std.fb.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + } + } + if (opChan[i].std.fms.had) { + chan[extChanOffs].state.fms=opChan[i].std.fms.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ams.had) { + chan[extChanOffs].state.ams=opChan[i].std.ams.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ex3.had) { + lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; @@ -509,6 +554,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { bool writeNoteOn=false; unsigned char writeMask=2; + unsigned char hardResetMask=0; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -535,12 +581,36 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { writeNoteOn=true; if (opChan[i].mask) { writeMask|=1<<(4+i); + if (opChan[i].hardReset) { + hardResetMask|=1<<(4+i); + } + } + if (!opChan[i].hardReset) { + opChan[i].keyOn=false; } - opChan[i].keyOn=false; } } if (writeNoteOn) { + writeMask^=hardResetMask; immWrite(0x28,writeMask); + writeMask^=hardResetMask; + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; isong.algMacroBehavior) for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j]; + if (isOpMuted[j] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + } + } + } + if (i==0 || fbAllOps) { + if (opChan[i].std.fb.had) { + chan[extChanOffs].state.fb=opChan[i].std.fb.val; + rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + } + } + if (opChan[i].std.fms.had) { + chan[extChanOffs].state.fms=opChan[i].std.fms.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ams.had) { + chan[extChanOffs].state.ams=opChan[i].std.ams.val; + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + if (opChan[i].std.ex3.had) { + lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; @@ -509,6 +554,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { bool writeNoteOn=false; unsigned char writeMask=2; + unsigned char hardResetMask=0; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -535,12 +581,36 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { writeNoteOn=true; if (opChan[i].mask) { writeMask|=1<<(4+i); + if (opChan[i].hardReset) { + hardResetMask|=1<<(4+i); + } + } + if (!opChan[i].hardReset) { + opChan[i].keyOn=false; } - opChan[i].keyOn=false; } } if (writeNoteOn) { + writeMask^=hardResetMask; immWrite(0x28,writeMask); + writeMask^=hardResetMask; + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; i>MASTER_CLOCK_PREC); logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG); totalLoops++; diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index d88bf4bc9..17cd235c1 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -207,7 +207,7 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) { // render data if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) { - logW("sample depth is wrong! (%d)",depth); + logW("sample depth is wrong! (%d)",(int)depth); depth=DIV_SAMPLE_DEPTH_16BIT; } samples=(double)samples/samplePitchesSD[pitch]; @@ -480,6 +480,7 @@ bool DivSample::saveRaw(const char* path) { // 16-bit memory is padded to 512, to make things easier for ADPCM-A/B. bool DivSample::initInternal(DivSampleDepth d, int count) { + logV("initInternal(%d,%d)",(int)d,count); switch (d) { case DIV_SAMPLE_DEPTH_1BIT: // 1-bit if (data1!=NULL) delete[] data1; @@ -489,7 +490,7 @@ bool DivSample::initInternal(DivSampleDepth d, int count) { break; case DIV_SAMPLE_DEPTH_1BIT_DPCM: // DPCM if (dataDPCM!=NULL) delete[] dataDPCM; - lengthDPCM=1+((((count+7)/8)+15)&(~15)); + lengthDPCM=1+((((count-1)/8)+15)&(~15)); dataDPCM=new unsigned char[lengthDPCM]; memset(dataDPCM,0xaa,lengthDPCM); break; @@ -748,7 +749,11 @@ void DivSample::convert(DivSampleDepth newDepth) { setSampleCount((samples+7)&(~7)); break; case DIV_SAMPLE_DEPTH_1BIT_DPCM: - setSampleCount((1+((((samples+7)/8)+15)&(~15)))<<3); + if (samples) { + setSampleCount((1+((((samples-1)/8)+15)&(~15)))<<3); + } else { + setSampleCount(8); + } break; case DIV_SAMPLE_DEPTH_YMZ_ADPCM: setSampleCount(((lengthZ+3)&(~0x03))*2); @@ -1168,7 +1173,7 @@ void DivSample::render(unsigned int formatMask) { if (!initInternal(DIV_SAMPLE_DEPTH_1BIT_DPCM,samples)) return; int accum=63; int next=63; - for (unsigned int i=0; i>3)>9; if (next>accum) { dataDPCM[i>>3]|=1<<(i&7); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index e242f0a39..f999a2d6f 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1542,7 +1542,6 @@ void DivEngine::registerSystems() { ); EffectHandlerMap es5506PreEffectHandlerMap={ - {0x10, {DIV_CMD_WAVE, "10xx: Change waveform (00 to FF)",effectVal}}, {0x11, {DIV_CMD_ES5506_FILTER_MODE, "11xx: Set filter mode (00 to 03)",effectValAnd<3>}}, {0x14, {DIV_CMD_ES5506_FILTER_K1, "14xx: Set filter coefficient K1 low byte (00 to FF)",effectValShift<0>,constVal<0x00ff>}}, {0x15, {DIV_CMD_ES5506_FILTER_K1, "15xx: Set filter coefficient K1 high byte (00 to FF)",effectValShift<8>,constVal<0xff00>}}, diff --git a/src/engine/zsmOps.cpp b/src/engine/zsmOps.cpp index 85ddb5d47..92b8aecb8 100644 --- a/src/engine/zsmOps.cpp +++ b/src/engine/zsmOps.cpp @@ -52,7 +52,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { break; default: IGNORED++; - logD("Ignoring chip %d systemID %d",i,song.system[i]); + logD("Ignoring chip %d systemID %d",i,(int)song.system[i]); break; } } @@ -94,6 +94,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { playSub(false); //size_t tickCount=0; bool done=false; + bool loopNow=false; int loopPos=-1; int fracWait=0; // accumulates fractional ticks if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(true); @@ -109,9 +110,17 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { while (!done) { if (loopPos==-1) { - if (loopOrder==curOrder && loopRow==curRow && ticks==1 && loop) { - loopPos=zsm.getoffset(); - zsm.setLoopPoint(); + if (loopOrder==curOrder && loopRow==curRow && loop) + loopNow=true; + if (loopNow) { + // If Virtual Tempo is in use, our exact loop point + // might be skipped due to quantization error. + // If this happens, the tick immediately following is our loop point. + if (ticks==1 || !(loopOrder==curOrder && loopRow==curRow)) { + loopPos=zsm.getoffset(); + zsm.setLoopPoint(); + loopNow=false; + } } } if (nextTick() || !playing) { diff --git a/src/gui/about.cpp b/src/gui/about.cpp index de4a50d19..b849c6bbe 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -121,6 +121,7 @@ const char* aboutLine[]={ "SwapXFO", "TakuikaNinja", "TCORPStudios", + "Teuthida", "The Blender Fiddler", "TheDuccinator", "theloredev", @@ -137,6 +138,7 @@ const char* aboutLine[]={ "ZoomTen (Zumi)", "", "-- additional feedback/fixes --", + "Electric Keet", "fd", "GENATARi", "host12prog", diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 803a5665b..e4e9e85fb 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -41,6 +41,13 @@ const char* chanOscRefs[]={ "Note Trigger" }; +const char* autoColsTypes[]={ + "Off", + "Mode 1", + "Mode 2", + "Mode 3" +}; + float FurnaceGUI::computeGradPos(int type, int chan) { switch (type) { case GUI_OSCREF_NONE: @@ -146,6 +153,19 @@ void FurnaceGUI::drawChanOsc() { centerSettingReset=true; } + ImGui::TableNextColumn(); + ImGui::Text("Automatic columns"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + const char* previewColType=autoColsTypes[chanOscAutoColsType&3]; + if (ImGui::BeginCombo("##AutoCols",previewColType)) { + for (int j=0; j<4; j++) { + const bool isSelected=(chanOscAutoColsType==j); + if (ImGui::Selectable(autoColsTypes[j],isSelected)) chanOscAutoColsType=j; + if (isSelected) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } ImGui::EndTable(); } @@ -304,6 +324,7 @@ void FurnaceGUI::drawChanOsc() { "- %C: channel short name\n" "- %d: channel number (starting from 0)\n" "- %D: channel number (starting from 1)\n" + "- %n: channel note\n" "- %i: instrument name\n" "- %I: instrument number (decimal)\n" "- %x: instrument number (hex)\n" @@ -345,6 +366,25 @@ void FurnaceGUI::drawChanOsc() { oscChans.push_back(i); } } + + // 0: none + // 1: sqrt(chans) + // 2: sqrt(chans+1) + // 3: sqrt(chans)+1 + switch (chanOscAutoColsType) { + case 1: + chanOscCols=sqrt(oscChans.size()); + break; + case 2: + chanOscCols=sqrt(oscChans.size()+1); + break; + case 3: + chanOscCols=sqrt(oscChans.size())+1; + break; + } + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; for (size_t i=0; ivolume>>8); break; } + case 'n': { + DivChannelState* chanState=e->getChanState(ch); + if (chanState==NULL || !(chanState->keyOn)) break; + short tempNote=chanState->note; //all of this conversion is necessary because notes 100-102 are special chars + short noteMod=tempNote%12+12; //also note 0 is a BUG, hence +12 on the note and -1 on the octave + short oct=tempNote/12-1; + text+=fmt::sprintf("%s",noteName(noteMod,oct)); + break; + } case '%': text+='%'; break; diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 48d5ed8cb..dd19d652d 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -69,6 +69,7 @@ void FurnaceGUI::drawChannels() { if (dragItem->IsDataType("FUR_CHAN")) { if (chanToMove!=i && chanToMove>=0) { e->swapChannelsP(chanToMove,i); + MARK_MODIFIED; } chanToMove=-1; } diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 289584492..f9e2ab749 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -330,9 +330,6 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { if (ImGui::MenuItem("save")) { doAction(GUI_ACTION_INS_LIST_SAVE); } - if (ImGui::MenuItem("save (legacy .fui)")) { - doAction(GUI_ACTION_INS_LIST_SAVE_OLD); - } if (ImGui::MenuItem("save (.dmp)")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index bd6bdb81f..d2b2f44ab 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -62,7 +62,6 @@ #define FM_CHIP_DEBUG \ COMMON_CHIP_DEBUG; \ - ImGui::Text("- lastBusy: %d",ch->lastBusy); \ ImGui::Text("- delay: %d",ch->delay); #define FM_OPN_CHIP_DEBUG \ @@ -167,7 +166,6 @@ ImGui::TextColored(ch->hardReset?colorOn:colorOff,">> hardReset"); \ ImGui::TextColored(ch->opMaskChanged?colorOn:colorOff,">> opMaskChanged"); \ ImGui::TextColored(ch->dacMode?colorOn:colorOff,">> DACMode"); \ - ImGui::TextColored(ch->dacReady?colorOn:colorOff,">> DACReady"); \ ImGui::TextColored(ch->dacDirection?colorOn:colorOff,">> DACDirection"); #define GENESIS_OPCHAN_DEBUG \ @@ -381,7 +379,6 @@ void putDispatchChip(void* data, int type) { ImGui::Text("- pcmR: %d",ch->pcmR); ImGui::Text("- pcmCycles: %d",ch->pcmCycles); ImGui::Text("- sampleBank: %d",ch->sampleBank); - ImGui::Text("- lastBusy: %d",ch->lastBusy); COMMON_CHIP_DEBUG_BOOL; break; } @@ -389,7 +386,6 @@ void putDispatchChip(void* data, int type) { DivPlatformAY8910* ch=(DivPlatformAY8910*)data; ImGui::Text("> AY-3-8910"); COMMON_CHIP_DEBUG; - ImGui::Text("- lastBusy: %d",ch->lastBusy); ImGui::Text("- sampleBank: %d",ch->sampleBank); ImGui::Text("- stereoSep: %d",ch->stereoSep); ImGui::Text("- delay: %d",ch->delay); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 60d713d32..56f1e32f9 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -642,9 +642,6 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_SAVE: if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); break; - case GUI_ACTION_INS_LIST_SAVE_OLD: - if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD); - break; case GUI_ACTION_INS_LIST_SAVE_DMP: if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP); break; diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp index 604204b86..2d1bd1f1e 100644 --- a/src/gui/effectList.cpp +++ b/src/gui/effectList.cpp @@ -1,6 +1,7 @@ #include "gui.h" #include "guiConst.h" #include +#include "IconsFontAwesome4.h" void FurnaceGUI::drawEffectList() { if (nextWindow==GUI_WINDOW_EFFECT_LIST) { @@ -11,7 +12,28 @@ void FurnaceGUI::drawEffectList() { if (!effectListOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(60.0f*dpiScale,20.0f*dpiScale),ImVec2(canvasW,canvasH)); if (ImGui::Begin("Effect List",&effectListOpen,globalWinFlags)) { - ImGui::Text("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse])); + float availB=ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing(); + if (availB>0) { + ImGui::PushTextWrapPos(availB); + ImGui::TextWrapped("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse])); + ImGui::PopTextWrapPos(); + ImGui::SameLine(); + } + ImGui::Button(ICON_FA_BARS "##SortEffects"); + if (ImGui::BeginPopupContextItem("effectSort",ImGuiPopupFlags_MouseButtonLeft)) { + for (int i=0; i<9; i++) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColorsSort[i]]); + ImGui::Checkbox(fxColorsNames[i],&effectsShow[i]); + ImGui::PopStyleColor(); + } + + if (ImGui::Button("All")) memset(effectsShow,1,sizeof(bool)*10); + ImGui::SameLine(); + if (ImGui::Button("None")) memset(effectsShow,0,sizeof(bool)*10); + + ImGui::EndPopup(); + } + if (ImGui::BeginTable("effectList",2)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); @@ -25,11 +47,24 @@ void FurnaceGUI::drawEffectList() { const char* prevName=NULL; for (int i=0; i<256; i++) { const char* name=e->getEffectDesc(i,cursor.xCoarse); + bool effectShow = true; if (name==prevName) { continue; } prevName=name; - if (name!=NULL) { + switch (fxColors[i]) { + case GUI_COLOR_PATTERN_EFFECT_MISC: effectShow = effectsShow[8]; break; + case GUI_COLOR_PATTERN_EFFECT_SONG: effectShow = effectsShow[1]; break; + case GUI_COLOR_PATTERN_EFFECT_SPEED: effectShow = effectsShow[3]; break; + case GUI_COLOR_PATTERN_EFFECT_TIME: effectShow = effectsShow[2]; break; + case GUI_COLOR_PATTERN_EFFECT_PITCH: effectShow = effectsShow[0]; break; + case GUI_COLOR_PATTERN_EFFECT_PANNING: effectShow = effectsShow[4]; break; + case GUI_COLOR_PATTERN_EFFECT_VOLUME: effectShow = effectsShow[5]; break; + case GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY: effectShow = effectsShow[6]; break; + case GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY: effectShow = effectsShow[7]; break; + default: effectShow = true; break; + } + if (name!=NULL && effectShow) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushFont(patFont); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e84c3ecd2..a262b9da2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1438,8 +1438,8 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num); }); MARK_MODIFIED; + curNibble=!curNibble; if (orderEditMode==2 || orderEditMode==3) { - curNibble=!curNibble; if (!curNibble) { if (orderEditMode==2) { orderCursor++; @@ -1653,16 +1653,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; - case GUI_FILE_INS_SAVE_OLD: - if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); - hasOpened=fileDialog->openSave( - "Save Instrument", - {"Furnace instrument", "*.fui"}, - "Furnace instrument{.fui}", - workingDirIns, - dpiScale - ); - break; case GUI_FILE_INS_SAVE_DMP: if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openSave( @@ -1845,6 +1835,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; + case GUI_FILE_LOAD_HEAD_FONT: + if (!dirExists(workingDirFont)) workingDirFont=getHomeDir(); + hasOpened=fileDialog->openLoad( + "Select Font", + {"compatible files", "*.ttf *.otf *.ttc"}, + "compatible files{.ttf,.otf,.ttc}", + workingDirFont, + dpiScale + ); + break; case GUI_FILE_LOAD_PAT_FONT: if (!dirExists(workingDirFont)) workingDirFont=getHomeDir(); hasOpened=fileDialog->openLoad( @@ -3869,6 +3869,7 @@ bool FurnaceGUI::loop() { mainFont=ImGui::GetIO().Fonts->AddFontDefault(); patFont=mainFont; bigFont=mainFont; + headFont=mainFont; if (rend) rend->destroyFontsTexture(); if (!ImGui::GetIO().Fonts->Build()) { logE("error again while building font atlas!"); @@ -4159,7 +4160,7 @@ bool FurnaceGUI::loop() { exitDisabledTimer=1; for (int i=0; isong.systemLen; i++) { if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true); + drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true,true); ImGui::TreePop(); } } @@ -4486,17 +4487,6 @@ bool FurnaceGUI::loop() { } MEASURE(songInfo,drawSongInfo()); MEASURE(orders,drawOrders()); - if (introMonOpen) { - int totalTicks=e->getTotalTicks(); - int totalSeconds=e->getTotalSeconds(); - double newMonitorPos=totalSeconds+((double)totalTicks/1000000.0); - - if (fabs(newMonitorPos-monitorPos)>0.08) monitorPos=newMonitorPos; - - drawIntro(monitorPos,true); - - if (e->isPlaying()) monitorPos+=ImGui::GetIO().DeltaTime; - } MEASURE(sampleList,drawSampleList()); MEASURE(sampleEdit,drawSampleEdit()); MEASURE(waveList,drawWaveList()); @@ -4531,6 +4521,11 @@ bool FurnaceGUI::loop() { MEASURE(effectList,drawEffectList()); } + for (int i=0; igetTotalChannelCount(); i++) { + keyHit1[i]-=0.2f; + if (keyHit1[i]<0.0f) keyHit1[i]=0.0f; + } + activateTutorial(GUI_TUTORIAL_OVERVIEW); if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); @@ -4592,7 +4587,6 @@ bool FurnaceGUI::loop() { case GUI_FILE_INS_OPEN: case GUI_FILE_INS_OPEN_REPLACE: case GUI_FILE_INS_SAVE: - case GUI_FILE_INS_SAVE_OLD: case GUI_FILE_INS_SAVE_DMP: workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR; break; @@ -4628,6 +4622,7 @@ bool FurnaceGUI::loop() { workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; break; case GUI_FILE_LOAD_MAIN_FONT: + case GUI_FILE_LOAD_HEAD_FONT: case GUI_FILE_LOAD_PAT_FONT: workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR; break; @@ -4695,9 +4690,6 @@ bool FurnaceGUI::loop() { if (curFileDialog==GUI_FILE_INS_SAVE) { checkExtension(".fui"); } - if (curFileDialog==GUI_FILE_INS_SAVE_OLD) { - checkExtension(".fui"); - } if (curFileDialog==GUI_FILE_INS_SAVE_DMP) { checkExtension(".dmp"); } @@ -4791,11 +4783,6 @@ bool FurnaceGUI::loop() { e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song); } break; - case GUI_FILE_INS_SAVE_OLD: - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - e->song.ins[curIns]->save(copyOfName.c_str(),true); - } - break; case GUI_FILE_INS_SAVE_DMP: if (curIns>=0 && curIns<(int)e->song.ins.size()) { if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) { @@ -5097,6 +5084,9 @@ bool FurnaceGUI::loop() { case GUI_FILE_LOAD_MAIN_FONT: settings.mainFontPath=copyOfName; break; + case GUI_FILE_LOAD_HEAD_FONT: + settings.headFontPath=copyOfName; + break; case GUI_FILE_LOAD_PAT_FONT: settings.patFontPath=copyOfName; break; @@ -6025,6 +6015,7 @@ bool FurnaceGUI::loop() { mainFont=ImGui::GetIO().Fonts->AddFontDefault(); patFont=mainFont; bigFont=mainFont; + headFont=mainFont; if (rend) rend->destroyFontsTexture(); if (!ImGui::GetIO().Fonts->Build()) { logE("error again while building font atlas!"); @@ -6093,7 +6084,6 @@ bool FurnaceGUI::init() { clockOpen=e->getConfBool("clockOpen",false); speedOpen=e->getConfBool("speedOpen",true); groovesOpen=e->getConfBool("groovesOpen",false); - introMonOpen=e->getConfBool("introMonOpen",false); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); effectListOpen=e->getConfBool("effectListOpen",true); @@ -6152,6 +6142,7 @@ bool FurnaceGUI::init() { pianoInputPadMode=e->getConfInt("pianoInputPadMode",pianoInputPadMode); chanOscCols=e->getConfInt("chanOscCols",3); + chanOscAutoColsType=e->getConfInt("chanOscAutoColsType",0); chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER); chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER); chanOscTextX=e->getConfFloat("chanOscTextX",0.0f); @@ -6480,6 +6471,7 @@ bool FurnaceGUI::init() { mainFont=ImGui::GetIO().Fonts->AddFontDefault(); patFont=mainFont; bigFont=mainFont; + headFont=mainFont; if (rend) rend->destroyFontsTexture(); if (!ImGui::GetIO().Fonts->Build()) { logE("error again while building font atlas!"); @@ -6611,7 +6603,6 @@ void FurnaceGUI::commitState() { e->setConf("clockOpen",clockOpen); e->setConf("speedOpen",speedOpen); e->setConf("groovesOpen",groovesOpen); - e->setConf("introMonOpen",introMonOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); e->setConf("effectListOpen",effectListOpen); @@ -6670,6 +6661,7 @@ void FurnaceGUI::commitState() { // commit per-chan osc state e->setConf("chanOscCols",chanOscCols); + e->setConf("chanOscAutoColsType",chanOscAutoColsType); e->setConf("chanOscColorX",chanOscColorX); e->setConf("chanOscColorY",chanOscColorY); e->setConf("chanOscTextX",chanOscTextX); @@ -6826,6 +6818,7 @@ FurnaceGUI::FurnaceGUI(): iconFont(NULL), patFont(NULL), bigFont(NULL), + headFont(NULL), fontRange(NULL), prevInsData(NULL), curIns(0), @@ -6895,7 +6888,6 @@ FurnaceGUI::FurnaceGUI(): clockOpen(false), speedOpen(true), groovesOpen(false), - introMonOpen(false), basicMode(true), shortIntro(false), insListDir(false), @@ -7114,6 +7106,7 @@ FurnaceGUI::FurnaceGUI(): oscWindowSize(20.0f), oscZoomSlider(false), chanOscCols(3), + chanOscAutoColsType(0), chanOscColorX(GUI_OSCREF_CENTER), chanOscColorY(GUI_OSCREF_CENTER), chanOscWindowSize(20.0f), @@ -7288,6 +7281,8 @@ FurnaceGUI::FurnaceGUI(): memset(macroRelLabel,0,32); memset(emptyLabel,0,32); memset(emptyLabel2,0,32); + //effect sorting + memset(effectsShow,1,sizeof(bool)*10); strncpy(noteOffLabel,"OFF",32); strncpy(noteRelLabel,"===",32); diff --git a/src/gui/gui.h b/src/gui/gui.h index b4a8a3e5b..101247dc0 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -402,7 +402,6 @@ enum FurnaceGUIFileDialogs { GUI_FILE_INS_OPEN, GUI_FILE_INS_OPEN_REPLACE, GUI_FILE_INS_SAVE, - GUI_FILE_INS_SAVE_OLD, GUI_FILE_INS_SAVE_DMP, GUI_FILE_WAVE_OPEN, GUI_FILE_WAVE_OPEN_REPLACE, @@ -424,6 +423,7 @@ enum FurnaceGUIFileDialogs { GUI_FILE_EXPORT_CMDSTREAM_BINARY, GUI_FILE_EXPORT_ROM, GUI_FILE_LOAD_MAIN_FONT, + GUI_FILE_LOAD_HEAD_FONT, GUI_FILE_LOAD_PAT_FONT, GUI_FILE_IMPORT_COLORS, GUI_FILE_IMPORT_KEYBINDS, @@ -607,7 +607,6 @@ enum FurnaceGUIActions { GUI_ACTION_INS_LIST_OPEN, GUI_ACTION_INS_LIST_OPEN_REPLACE, GUI_ACTION_INS_LIST_SAVE, - GUI_ACTION_INS_LIST_SAVE_OLD, GUI_ACTION_INS_LIST_SAVE_DMP, GUI_ACTION_INS_LIST_MOVE_UP, GUI_ACTION_INS_LIST_MOVE_DOWN, @@ -1371,6 +1370,7 @@ class FurnaceGUI { ImFont* iconFont; ImFont* patFont; ImFont* bigFont; + ImFont* headFont; ImWchar* fontRange; ImVec4 uiColors[GUI_COLOR_MAX]; ImVec4 volColors[128]; @@ -1389,7 +1389,7 @@ class FurnaceGUI { char emptyLabel2[32]; struct Settings { - int mainFontSize, patFontSize, iconSize; + int mainFontSize, patFontSize, headFontSize, iconSize; int audioEngine; int audioQuality; int audioChans; @@ -1406,6 +1406,7 @@ class FurnaceGUI { String tg100Path; String mu5Path; int mainFont; + int headFont; int patFont; int audioRate; int audioBufSize; @@ -1525,8 +1526,10 @@ class FurnaceGUI { int newSongBehavior; int memUsageUnit; int cursorFollowsWheel; + int noDMFCompat; unsigned int maxUndoSteps; String mainFontPath; + String headFontPath; String patFontPath; String audioDevice; String midiInDevice; @@ -1545,6 +1548,7 @@ class FurnaceGUI { Settings(): mainFontSize(18), patFontSize(18), + headFontSize(27), iconSize(16), audioEngine(DIV_AUDIO_SDL), audioQuality(0), @@ -1679,8 +1683,10 @@ class FurnaceGUI { newSongBehavior(0), memUsageUnit(1), cursorFollowsWheel(0), + noDMFCompat(0), maxUndoSteps(100), mainFontPath(""), + headFontPath(""), patFontPath(""), audioDevice(""), midiInDevice(""), @@ -1727,7 +1733,7 @@ class FurnaceGUI { bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen; - bool groovesOpen, introMonOpen; + bool groovesOpen; bool basicMode, shortIntro; bool insListDir, waveListDir, sampleListDir; @@ -1970,7 +1976,7 @@ class FurnaceGUI { bool oscZoomSlider; // per-channel oscilloscope - int chanOscCols, chanOscColorX, chanOscColorY; + int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify; bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize; String chanOscTextFormat; @@ -2031,6 +2037,9 @@ class FurnaceGUI { bool pianoReadonly; int pianoOffset, pianoOffsetEdit; int pianoView, pianoInputPadMode; + + //effect sorting + bool effectsShow[10]; // TX81Z bool hasACED; @@ -2074,7 +2083,7 @@ class FurnaceGUI { void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType); void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); - bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange); + bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false); void kvsConfig(DivInstrument* ins); void drawFMPreview(const ImVec2& size); void renderFMPreview(const DivInstrumentFM& params, int pos=0); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 7e7aecc18..c1446ac6f 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -208,6 +208,32 @@ const char* resampleStrats[]={ "best possible" }; +const FurnaceGUIColors fxColorsSort[]={//used for sorting + GUI_COLOR_PATTERN_EFFECT_PITCH, + GUI_COLOR_PATTERN_EFFECT_SONG, + GUI_COLOR_PATTERN_EFFECT_TIME, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_VOLUME, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_INVALID +}; + +const char* fxColorsNames[]={ + "Pitch", + "Song", + "Time", + "Speed", + "Panning", + "Volume", + "System Primary", + "System Secondary", + "Miscellaneous", + "Invalid" +}; + const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_MISC, // 00 GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 @@ -641,7 +667,6 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("INS_LIST_OPEN", "Open", 0), D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0), D("INS_LIST_SAVE", "Save", 0), - D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0), D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0), D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP), D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN), diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index 494ba58dc..616692de5 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -57,4 +57,6 @@ extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; extern const int vgmVersions[7]; -extern const FurnaceGUIColors fxColors[256]; \ No newline at end of file +extern const FurnaceGUIColors fxColors[256]; +extern const FurnaceGUIColors fxColorsSort[10]; +extern const char* fxColorsNames[10]; \ No newline at end of file diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e3997bf46..e5f60d86b 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2288,9 +2288,6 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetTooltip("Save"); } if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) { - if (ImGui::MenuItem("save in legacy format...")) { - doAction(GUI_ACTION_INS_LIST_SAVE_OLD); - } if (ImGui::MenuItem("save as .dmp...")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } @@ -4688,7 +4685,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::Separator(); - P(ImGui::Checkbox("Per-channel wave offset/length",&ins->n163.perChanPos)); + P(ImGui::Checkbox("Per-channel wave position/length",&ins->n163.perChanPos)); if (ins->n163.perChanPos) { if (ImGui::BeginTable("N1PerChPos",3)) { @@ -4700,7 +4697,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Text("Ch"); ImGui::TableNextColumn(); - ImGui::Text("Offset"); + ImGui::Text("Position"); ImGui::TableNextColumn(); ImGui::Text("Length"); @@ -4732,7 +4729,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } } else { - if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER + if (ImGui::InputInt("Position##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER if (ins->n163.wavePos<0) ins->n163.wavePos=0; if (ins->n163.wavePos>255) ins->n163.wavePos=255; } @@ -5563,6 +5560,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MSM6295) waveMax=0; if (ins->type==DIV_INS_SEGAPCM) waveMax=0; if (ins->type==DIV_INS_K007232) waveMax=0; + if (ins->type==DIV_INS_ES5506) waveMax=0; if (ins->type==DIV_INS_GA20) waveMax=0; if (ins->type==DIV_INS_K053260) waveMax=0; if (ins->type==DIV_INS_POKEMINI) waveMax=0; diff --git a/src/gui/intro.cpp b/src/gui/intro.cpp index bace14396..0fc1c9600 100644 --- a/src/gui/intro.cpp +++ b/src/gui/intro.cpp @@ -104,10 +104,7 @@ void FurnaceGUI::endIntroTune() { void FurnaceGUI::drawIntro(double introTime, bool monitor) { if (monitor) { - if (introTime<0.0) introTime=0.0; - if (introTime>11.0) introTime=11.0; - if (!introMonOpen) return; - if (introPos<(shortIntro?1.0:11.0)) return; + return; } if (introPos<(shortIntro?1.0:11.0) || monitor) { if (!monitor) { @@ -117,7 +114,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) { ImGui::SetNextWindowSize(ImVec2(canvasW,canvasH)); if (introPos<0.1) ImGui::SetNextWindowFocus(); } - if (ImGui::Begin(monitor?"IntroMon X":"Intro",monitor?(&introMonOpen):NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) { + if (ImGui::Begin(monitor?"IntroMon X":"Intro",NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) { if (monitor) { if (ImGui::Button("Preview")) { introPos=0; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 590676fc7..c21372466 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -829,8 +829,6 @@ void FurnaceGUI::drawPattern() { ImGui::GetColorU32(chanHeadBase) ); } - keyHit1[i]-=0.2f; - if (keyHit1[i]<0.0f) keyHit1[i]=0.0f; } } diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 05f5af87a..356ac6f6a 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -991,7 +991,7 @@ void FurnaceGUI::initSystemPresets() { } ); ENTRY( - "PC + AdLib/Sound Blaster (drums mode)", { + "PC + Sound Blaster (drums mode)", { CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""), CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, ""), CH(DIV_SYSTEM_PCM_DAC, 1.0f, 0, diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 57f1fa32a..7f582f6c8 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -58,6 +58,16 @@ const char* mainFonts[]={ "" }; +const char* headFonts[]={ + "IBM Plex Sans", + "Liberation Sans", + "Exo", + "Proggy Clean", + "GNU Unifont", + "", + "" +}; + const char* patFonts[]={ "IBM Plex Mono", "Mononoki", @@ -236,6 +246,27 @@ const char* specificControls[18]={ } \ if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) actionKeys[what]=0; +#define CONFIG_SUBSECTION(what) \ + if (_subInit) { \ + ImGui::Separator(); \ + } else { \ + _subInit=true; \ + } \ + ImGui::PushFont(headFont); \ + ImGui::TextUnformatted(what); \ + ImGui::PopFont(); + +#define CONFIG_SECTION(what) \ + if (ImGui::BeginTabItem(what)) { \ + bool _subInit=false; \ + ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); \ + settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; \ + if (ImGui::BeginChild("SettingsView",settingsViewSize)) + +#define END_SECTION } \ + ImGui::EndChild(); \ + ImGui::EndTabItem(); + String stripName(String what) { String ret; for (char& i: what) { @@ -286,382 +317,1288 @@ void FurnaceGUI::drawSettings() { showWarning("Do you want to save your settings?",GUI_WARN_CLOSE_SETTINGS); } if (ImGui::BeginTabBar("settingsTab")) { - if (ImGui::BeginTabItem("General")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { - ImGui::Text("Workspace layout:"); - ImGui::SameLine(); - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_LAYOUT); + // NEW SETTINGS HERE + CONFIG_SECTION("General") { + // SUBSECTION PROGRAM + CONFIG_SUBSECTION("Program"); + String curRenderBackend=settings.renderBackend.empty()?GUI_BACKEND_DEFAULT_NAME:settings.renderBackend; + if (ImGui::BeginCombo("Render backend",curRenderBackend.c_str())) { +#ifdef HAVE_RENDER_SDL + if (ImGui::Selectable("SDL Renderer",curRenderBackend=="SDL")) { + settings.renderBackend="SDL"; } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_LAYOUT); +#endif +#ifdef HAVE_RENDER_DX11 + if (ImGui::Selectable("DirectX 11",curRenderBackend=="DirectX 11")) { + settings.renderBackend="DirectX 11"; } - ImGui::SameLine(); - if (ImGui::Button("Reset")) { - showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); +#endif +#ifdef HAVE_RENDER_GL + if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) { + settings.renderBackend="OpenGL"; } - - ImGui::Separator(); - - ImGui::Text("Initial system:"); - ImGui::SameLine(); - if (ImGui::Button("Current system")) { - settings.initialSys.clear(); - for (int i=0; isong.systemLen; i++) { - settings.initialSys.set(fmt::sprintf("id%d",i),e->systemToFileFur(e->song.system[i])); - settings.initialSys.set(fmt::sprintf("vol%d",i),(float)e->song.systemVol[i]); - settings.initialSys.set(fmt::sprintf("pan%d",i),(float)e->song.systemPan[i]); - settings.initialSys.set(fmt::sprintf("fr%d",i),(float)e->song.systemPanFR[i]); - settings.initialSys.set(fmt::sprintf("flags%d",i),e->song.systemFlags[i].toBase64()); +#endif + ImGui::EndCombo(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } + if (curRenderBackend=="SDL") { + if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) { + if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) { + settings.renderDriver=""; } - settings.initialSysName=e->song.systemName; - } - ImGui::SameLine(); - if (ImGui::Button("Randomize")) { - settings.initialSys.clear(); - int howMany=1+rand()%3; - int totalAvailSys=0; - for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); - if (totalAvailSys>0) { - for (int i=0; isystemToFileFur((DivSystem)availableSystems[rand()%totalAvailSys])); - settings.initialSys.set(fmt::sprintf("vol%d",i),1.0f); - settings.initialSys.set(fmt::sprintf("pan%d",i),0.0f); - settings.initialSys.set(fmt::sprintf("fr%d",i),0.0f); - settings.initialSys.set(fmt::sprintf("flags%d",i),""); + for (String& i: availRenderDrivers) { + if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) { + settings.renderDriver=i; } - } else { - settings.initialSys.set("id0",e->systemToFileFur(DIV_SYSTEM_DUMMY)); - settings.initialSys.set("vol0",1.0f); - settings.initialSys.set("pan0",0.0f); - settings.initialSys.set("fr0",0.0f); - settings.initialSys.set("flags0",""); - howMany=1; } - // randomize system name - std::vector wordPool[6]; + ImGui::EndCombo(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } + } + + bool renderClearPosB=settings.renderClearPos; + if (ImGui::Checkbox("Late render clear",&renderClearPosB)) { + settings.renderClearPos=renderClearPosB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("calls rend->clear() after rend->present(). might reduce UI latency by one frame in some drivers."); + } + + bool powerSaveB=settings.powerSave; + if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) { + settings.powerSave=powerSaveB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!"); + } + +#ifndef IS_MOBILE + bool noThreadedInputB=settings.noThreadedInput; + if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) { + settings.noThreadedInput=noThreadedInputB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.\nhowever, crashes have been reported when threaded input is on. enable this option if that is the case."); + } +#endif + + bool eventDelayB=settings.eventDelay; + if (ImGui::Checkbox("Enable event delay",&eventDelayB)) { + settings.eventDelay=eventDelayB; + applyUISettings(false); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes."); + } + + // SUBSECTION FILE + CONFIG_SUBSECTION("File"); + + bool sysFileDialogB=settings.sysFileDialog; + if (ImGui::Checkbox("Use system file picker",&sysFileDialogB)) { + settings.sysFileDialog=sysFileDialogB; + } + + if (ImGui::InputInt("Number of recent files",&settings.maxRecentFile)) { + if (settings.maxRecentFile<0) settings.maxRecentFile=0; + if (settings.maxRecentFile>30) settings.maxRecentFile=30; + } + + bool compressB=settings.compress; + if (ImGui::Checkbox("Compress when saving",&compressB)) { + settings.compress=compressB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use zlib to compress saved songs."); + } + + bool saveUnusedPatternsB=settings.saveUnusedPatterns; + if (ImGui::Checkbox("Save unused patterns",&saveUnusedPatternsB)) { + settings.saveUnusedPatterns=saveUnusedPatternsB; + } + + bool newPatternFormatB=settings.newPatternFormat; + if (ImGui::Checkbox("Use new pattern format when saving",&newPatternFormatB)) { + settings.newPatternFormat=newPatternFormatB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a packed format which saves space when saving songs.\ndisable if you need compatibility with older Furnace and/or tools\nwhich do not support this format."); + } + + bool noDMFCompatB=settings.noDMFCompat; + if (ImGui::Checkbox("Don't apply compatibility flags when loading .dmf",&noDMFCompatB)) { + settings.noDMFCompat=noDMFCompatB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("do not report any issues arising from the use of this option!"); + } + + ImGui::Text("Audio export loop/fade out time:"); + if (ImGui::RadioButton("Set to these values on start-up:##fot0",settings.persistFadeOut==0)) { + settings.persistFadeOut=0; + } + ImGui::BeginDisabled(settings.persistFadeOut); + if (ImGui::InputInt("Loops",&settings.exportLoops,1,2)) { + if (exportLoops<0) exportLoops=0; + exportLoops=settings.exportLoops; + } + if (ImGui::InputDouble("Fade out (seconds)",&settings.exportFadeOut,1.0,2.0,"%.1f")) { + if (exportFadeOut<0.0) exportFadeOut=0.0; + exportFadeOut=settings.exportFadeOut; + } + ImGui::EndDisabled(); + if (ImGui::RadioButton("Remember last values##fot1",settings.persistFadeOut==1)) { + settings.persistFadeOut=1; + } + + // SUBSECTION CHIP + CONFIG_SUBSECTION("Chip"); + ImGui::Text("Initial system:"); + ImGui::SameLine(); + if (ImGui::Button("Current system")) { + settings.initialSys.clear(); + for (int i=0; isong.systemLen; i++) { + settings.initialSys.set(fmt::sprintf("id%d",i),e->systemToFileFur(e->song.system[i])); + settings.initialSys.set(fmt::sprintf("vol%d",i),(float)e->song.systemVol[i]); + settings.initialSys.set(fmt::sprintf("pan%d",i),(float)e->song.systemPan[i]); + settings.initialSys.set(fmt::sprintf("fr%d",i),(float)e->song.systemPanFR[i]); + settings.initialSys.set(fmt::sprintf("flags%d",i),e->song.systemFlags[i].toBase64()); + } + settings.initialSysName=e->song.systemName; + } + ImGui::SameLine(); + if (ImGui::Button("Randomize")) { + settings.initialSys.clear(); + int howMany=1+rand()%3; + int totalAvailSys=0; + for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); + if (totalAvailSys>0) { for (int i=0; isystemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0)); - String sName=e->getSystemName(sysID); - String nameWord; - sName+=" "; - for (char& i: sName) { - if (i==' ') { - if (nameWord!="") { - wordPool[wpPos++].push_back(nameWord); - if (wpPos>=6) break; - nameWord=""; - } - } else { - nameWord+=i; - } - } + settings.initialSys.set(fmt::sprintf("id%d",i),e->systemToFileFur((DivSystem)availableSystems[rand()%totalAvailSys])); + settings.initialSys.set(fmt::sprintf("vol%d",i),1.0f); + settings.initialSys.set(fmt::sprintf("pan%d",i),0.0f); + settings.initialSys.set(fmt::sprintf("fr%d",i),0.0f); + settings.initialSys.set(fmt::sprintf("flags%d",i),""); } - settings.initialSysName=""; - for (int i=0; i<6; i++) { - if (wordPool[i].empty()) continue; - settings.initialSysName+=wordPool[i][rand()%wordPool[i].size()]; - settings.initialSysName+=" "; - } - } - ImGui::SameLine(); - if (ImGui::Button("Reset to defaults")) { - settings.initialSys.clear(); - settings.initialSys.set("id0",e->systemToFileFur(DIV_SYSTEM_YM2612)); + } else { + settings.initialSys.set("id0",e->systemToFileFur(DIV_SYSTEM_DUMMY)); 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.0f); - settings.initialSys.set("fr1",0.0f); - settings.initialSys.set("flags1",""); - settings.initialSysName="Sega Genesis/Mega Drive"; + howMany=1; + } + // randomize system name + std::vector wordPool[6]; + for (int i=0; isystemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0)); + String sName=e->getSystemName(sysID); + String nameWord; + sName+=" "; + for (char& i: sName) { + if (i==' ') { + if (nameWord!="") { + wordPool[wpPos++].push_back(nameWord); + if (wpPos>=6) break; + nameWord=""; + } + } else { + nameWord+=i; + } + } + } + settings.initialSysName=""; + for (int i=0; i<6; i++) { + if (wordPool[i].empty()) continue; + settings.initialSysName+=wordPool[i][rand()%wordPool[i].size()]; + settings.initialSysName+=" "; + } + } + ImGui::SameLine(); + if (ImGui::Button("Reset to defaults")) { + 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.0f); + settings.initialSys.set("fr1",0.0f); + settings.initialSys.set("flags1",""); + settings.initialSysName="Sega Genesis/Mega Drive"; + } + + ImGui::Text("Name"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputText("##InitSysName",&settings.initialSysName); + + int sysCount=0; + int doRemove=-1; + for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) { + DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0)); + float sysVol=settings.initialSys.getFloat(fmt::sprintf("vol%d",i),0); + float sysPan=settings.initialSys.getFloat(fmt::sprintf("pan%d",i),0); + float sysPanFR=settings.initialSys.getFloat(fmt::sprintf("fr%d",i),0); + + sysCount=i+1; + + //bool doRemove=false; + bool doInvert=(sysVol<0); + float vol=fabs(sysVol); + ImGui::PushID(i); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x); + if (ImGui::BeginCombo("##System",getSystemName(sysID))) { + for (int j=0; availableSystems[j]; j++) { + if (ImGui::Selectable(getSystemName((DivSystem)availableSystems[j]),sysID==availableSystems[j])) { + sysID=(DivSystem)availableSystems[j]; + settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID)); + settings.initialSys.set(fmt::sprintf("flags%d",i),""); + } + } + ImGui::EndCombo(); } - ImGui::Text("Name"); ImGui::SameLine(); + if (ImGui::Checkbox("Invert",&doInvert)) { + sysVol=-sysVol; + settings.initialSys.set(fmt::sprintf("vol%d",i),sysVol); + } + ImGui::SameLine(); + //ImGui::BeginDisabled(settings.initialSys.size()<=4); + if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) { + doRemove=i; + } + //ImGui::EndDisabled(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("##InitSysName",&settings.initialSysName); + if (CWSliderFloat("Volume",&vol,0.0f,3.0f)) { + if (doInvert) { + if (vol<0.0001) vol=0.0001; + } + if (vol<0) vol=0; + if (vol>10) vol=10; + sysVol=doInvert?-vol:vol; + settings.initialSys.set(fmt::sprintf("vol%d",i),(float)sysVol); + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderFloat("Panning",&sysPan,-1.0f,1.0f)) { + if (sysPan<-1.0f) sysPan=-1.0f; + if (sysPan>1.0f) sysPan=1.0f; + settings.initialSys.set(fmt::sprintf("pan%d",i),(float)sysPan); + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderFloat("Front/Rear",&sysPanFR,-1.0f,1.0f)) { + if (sysPanFR<-1.0f) sysPanFR=-1.0f; + if (sysPanFR>1.0f) sysPanFR=1.0f; + settings.initialSys.set(fmt::sprintf("fr%d",i),(float)sysPanFR); + } rightClickable - int sysCount=0; - int doRemove=-1; - for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) { - DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0)); - float sysVol=settings.initialSys.getFloat(fmt::sprintf("vol%d",i),0); - float sysPan=settings.initialSys.getFloat(fmt::sprintf("pan%d",i),0); - float sysPanFR=settings.initialSys.getFloat(fmt::sprintf("fr%d",i),0); + // oh please MSVC don't cry + if (ImGui::TreeNode("Configure")) { + String sysFlagsS=settings.initialSys.getString(fmt::sprintf("flags%d",i),""); + DivConfig sysFlags; + sysFlags.loadFromBase64(sysFlagsS.c_str()); + if (drawSysConf(-1,sysID,sysFlags,false)) { + settings.initialSys.set(fmt::sprintf("flags%d",i),sysFlags.toBase64()); + } + ImGui::TreePop(); + } - sysCount=i+1; + ImGui::PopID(); + } - //bool doRemove=false; - bool doInvert=(sysVol<0); - float vol=fabs(sysVol); + if (doRemove>=0 && sysCount>1) { + for (int i=doRemove; isystemToFileFur(DIV_SYSTEM_YM2612)); + settings.initialSys.set(fmt::sprintf("vol%d",sysCount),1.0f); + settings.initialSys.set(fmt::sprintf("pan%d",sysCount),0.0f); + settings.initialSys.set(fmt::sprintf("fr%d",sysCount),0.0f); + settings.initialSys.set(fmt::sprintf("flags%d",sysCount),""); + } + + ImGui::Text("When creating new song:"); + if (ImGui::RadioButton("Display system preset selector##NSB0",settings.newSongBehavior==0)) { + settings.newSongBehavior=0; + } + if (ImGui::RadioButton("Start with initial system##NSB1",settings.newSongBehavior==1)) { + settings.newSongBehavior=1; + } + + bool restartOnFlagChangeB=settings.restartOnFlagChange; + if (ImGui::Checkbox("Restart song when changing chip properties",&restartOnFlagChangeB)) { + settings.restartOnFlagChange=restartOnFlagChangeB; + } + + // SUBSECTION START-UP + CONFIG_SUBSECTION("Start-up"); + ImGui::Text("Play intro on start-up:"); + if (ImGui::RadioButton("No##pis0",settings.alwaysPlayIntro==0)) { + settings.alwaysPlayIntro=0; + } + if (ImGui::RadioButton("Short##pis1",settings.alwaysPlayIntro==1)) { + settings.alwaysPlayIntro=1; + } + if (ImGui::RadioButton("Full (short when loading song)##pis2",settings.alwaysPlayIntro==2)) { + settings.alwaysPlayIntro=2; + } + if (ImGui::RadioButton("Full (always)##pis3",settings.alwaysPlayIntro==3)) { + settings.alwaysPlayIntro=3; + } + + bool disableFadeInB=settings.disableFadeIn; + if (ImGui::Checkbox("Disable fade-in during start-up",&disableFadeInB)) { + settings.disableFadeIn=disableFadeInB; + } + + bool partyTimeB=settings.partyTime; + if (ImGui::Checkbox("About screen party time",&partyTimeB)) { + settings.partyTime=partyTimeB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Warning: may cause epileptic seizures."); + } + + // SUBSECTION BEHAVIOR + CONFIG_SUBSECTION("Behavior"); + bool blankInsB=settings.blankIns; + if (ImGui::Checkbox("New instruments are blank",&blankInsB)) { + settings.blankIns=blankInsB; + } + + END_SECTION; + } + CONFIG_SECTION("Audio") { + // SUBSECTION OUTPUT + CONFIG_SUBSECTION("Output"); +#ifdef HAVE_JACK + ImGui::Text("Backend"); + ImGui::SameLine(); + int prevAudioEngine=settings.audioEngine; + if (ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2)) { + if (settings.audioEngine!=prevAudioEngine) { + if (!isProAudio[settings.audioEngine]) settings.audioChans=2; + } + } +#endif + + if (settings.audioEngine==DIV_AUDIO_SDL) { + ImGui::Text("Driver"); + ImGui::SameLine(); + if (ImGui::BeginCombo("##SDLADriver",settings.sdlAudioDriver.empty()?"Automatic":settings.sdlAudioDriver.c_str())) { + if (ImGui::Selectable("Automatic",settings.sdlAudioDriver.empty())) { + settings.sdlAudioDriver=""; + } + for (String& i: availAudioDrivers) { + if (ImGui::Selectable(i.c_str(),i==settings.sdlAudioDriver)) { + settings.sdlAudioDriver=i; + } + } + ImGui::EndCombo(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } + } + + ImGui::Text("Device"); + ImGui::SameLine(); + String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; + if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { + if (ImGui::Selectable("",settings.audioDevice.empty())) { + settings.audioDevice=""; + } + for (String& i: e->getAudioDevices()) { + if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { + settings.audioDevice=i; + } + } + ImGui::EndCombo(); + } + + ImGui::Text("Sample rate"); + ImGui::SameLine(); + String sr=fmt::sprintf("%d",settings.audioRate); + if (ImGui::BeginCombo("##SampleRate",sr.c_str())) { + SAMPLE_RATE_SELECTABLE(8000); + SAMPLE_RATE_SELECTABLE(16000); + SAMPLE_RATE_SELECTABLE(22050); + SAMPLE_RATE_SELECTABLE(32000); + SAMPLE_RATE_SELECTABLE(44100); + SAMPLE_RATE_SELECTABLE(48000); + SAMPLE_RATE_SELECTABLE(88200); + SAMPLE_RATE_SELECTABLE(96000); + SAMPLE_RATE_SELECTABLE(192000); + ImGui::EndCombo(); + } + + if (isProAudio[settings.audioEngine]) { + ImGui::Text("Outputs"); + ImGui::SameLine(); + if (ImGui::InputInt("##AudioChansI",&settings.audioChans,1,1)) { + if (settings.audioChans<1) settings.audioChans=1; + if (settings.audioChans>16) settings.audioChans=16; + } + } else { + ImGui::Text("Channels"); + ImGui::SameLine(); + String chStr=(settings.audioChans<1 || settings.audioChans>8)?"What?":nonProAudioOuts[settings.audioChans-1]; + if (ImGui::BeginCombo("##AudioChans",chStr.c_str())) { + CHANS_SELECTABLE(1); + CHANS_SELECTABLE(2); + CHANS_SELECTABLE(4); + CHANS_SELECTABLE(6); + CHANS_SELECTABLE(8); + ImGui::EndCombo(); + } + } + + ImGui::Text("Buffer size"); + ImGui::SameLine(); + String bs=fmt::sprintf("%d (latency: ~%.1fms)",settings.audioBufSize,2000.0*(double)settings.audioBufSize/(double)MAX(1,settings.audioRate)); + if (ImGui::BeginCombo("##BufferSize",bs.c_str())) { + BUFFER_SIZE_SELECTABLE(64); + BUFFER_SIZE_SELECTABLE(128); + BUFFER_SIZE_SELECTABLE(256); + BUFFER_SIZE_SELECTABLE(512); + BUFFER_SIZE_SELECTABLE(1024); + BUFFER_SIZE_SELECTABLE(2048); + ImGui::EndCombo(); + } + + bool lowLatencyB=settings.lowLatency; + if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) { + settings.lowLatency=lowLatencyB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less)."); + } + + bool forceMonoB=settings.forceMono; + if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { + settings.forceMono=forceMonoB; + } + + TAAudioDesc& audioWant=e->getAudioDescWant(); + TAAudioDesc& audioGot=e->getAudioDescGot(); + + ImGui::Text("want: %d samples @ %.0fHz (%d channels)",audioWant.bufsize,audioWant.rate,audioWant.outChans); + ImGui::Text("got: %d samples @ %.0fHz (%d channels)",audioGot.bufsize,audioGot.rate,audioWant.outChans); + + // SUBSECTION MIXING + CONFIG_SUBSECTION("Mixing"); + ImGui::Text("Quality"); + ImGui::SameLine(); + ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); + + bool clampSamplesB=settings.clampSamples; + if (ImGui::Checkbox("Software clipping",&clampSamplesB)) { + settings.clampSamples=clampSamplesB; + } + + // SUBSECTION METRONOME + CONFIG_SUBSECTION("Metronome"); + ImGui::Text("Metronome volume"); + ImGui::SameLine(); + if (ImGui::SliderInt("##MetroVol",&settings.metroVol,0,200,"%d%%")) { + if (settings.metroVol<0) settings.metroVol=0; + if (settings.metroVol>200) settings.metroVol=200; + e->setMetronomeVol(((float)settings.metroVol)/100.0f); + } + + END_SECTION; + } + CONFIG_SECTION("MIDI") { + // SUBSECTION MIDI INPUT + CONFIG_SUBSECTION("MIDI input"); + ImGui::Text("MIDI input"); + ImGui::SameLine(); + String midiInName=settings.midiInDevice.empty()?"":settings.midiInDevice; + bool hasToReloadMidi=false; + if (ImGui::BeginCombo("##MidiInDevice",midiInName.c_str())) { + if (ImGui::Selectable("",settings.midiInDevice.empty())) { + settings.midiInDevice=""; + hasToReloadMidi=true; + } + for (String& i: e->getMidiIns()) { + if (ImGui::Selectable(i.c_str(),i==settings.midiInDevice)) { + settings.midiInDevice=i; + hasToReloadMidi=true; + } + } + ImGui::EndCombo(); + } + + if (hasToReloadMidi) { + midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + midiMap.compile(); + } + + ImGui::Checkbox("Note input",&midiMap.noteInput); + ImGui::Checkbox("Velocity input",&midiMap.volInput); + // TODO + //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); + //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); + ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel); + ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse); + ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange); + //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); + //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); + ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7); + if (midiMap.valueInputStyle>3) { + if (midiMap.valueInputStyle==6) { + if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputControlSingle,1,16)) { + if (midiMap.valueInputControlSingle<0) midiMap.valueInputControlSingle=0; + if (midiMap.valueInputControlSingle>127) midiMap.valueInputControlSingle=127; + } + } else { + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) { + if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0; + if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127; + } + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) { + if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0; + if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127; + } + } + } + if (ImGui::TreeNode("Per-column control change")) { + for (int i=0; i<18; i++) { ImGui::PushID(i); + ImGui::Combo(specificControls[i],&midiMap.valueInputSpecificStyle[i],valueSInputStyles,4); + if (midiMap.valueInputSpecificStyle[i]>0) { + ImGui::Indent(); + if (midiMap.valueInputSpecificStyle[i]==3) { + if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputSpecificSingle[i],1,16)) { + if (midiMap.valueInputSpecificSingle[i]<0) midiMap.valueInputSpecificSingle[i]=0; + if (midiMap.valueInputSpecificSingle[i]>127) midiMap.valueInputSpecificSingle[i]=127; + } + } else { + if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputSpecificMSB[i],1,16)) { + if (midiMap.valueInputSpecificMSB[i]<0) midiMap.valueInputSpecificMSB[i]=0; + if (midiMap.valueInputSpecificMSB[i]>127) midiMap.valueInputSpecificMSB[i]=127; + } + if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputSpecificLSB[i],1,16)) { + if (midiMap.valueInputSpecificLSB[i]<0) midiMap.valueInputSpecificLSB[i]=0; + if (midiMap.valueInputSpecificLSB[i]>127) midiMap.valueInputSpecificLSB[i]=127; + } + } + ImGui::Unindent(); + } + ImGui::PopID(); + } + ImGui::TreePop(); + } + if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { + if (midiMap.volExp<0.01) midiMap.volExp=0.01; + if (midiMap.volExp>8.0) midiMap.volExp=8.0; + } rightClickable + float curve[128]; + for (int i=0; i<128; i++) { + curve[i]=(int)(pow((double)i/127.0,midiMap.volExp)*127.0); + } + ImGui::PlotLines("##VolCurveDisplay",curve,128,0,"Volume curve",0.0,127.0,ImVec2(200.0f*dpiScale,200.0f*dpiScale)); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x); - if (ImGui::BeginCombo("##System",getSystemName(sysID))) { - for (int j=0; availableSystems[j]; j++) { - if (ImGui::Selectable(getSystemName((DivSystem)availableSystems[j]),sysID==availableSystems[j])) { - sysID=(DivSystem)availableSystems[j]; - settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID)); - settings.initialSys.set(fmt::sprintf("flags%d",i),""); + ImGui::Text("Actions:"); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS "##AddAction")) { + midiMap.binds.push_back(MIDIBind()); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_EXTERNAL_LINK "##AddLearnAction")) { + midiMap.binds.push_back(MIDIBind()); + learning=midiMap.binds.size()-1; + } + if (learning!=-1) { + ImGui::SameLine(); + ImGui::Text("(learning! press a button or move a slider/knob/something on your device.)"); + } + + if (ImGui::BeginTable("MIDIActions",7)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.2); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.1); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.3); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.2); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.5); + ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::TableNextColumn(); + ImGui::Text("Channel"); + ImGui::TableNextColumn(); + ImGui::Text("Note/Control"); + ImGui::TableNextColumn(); + ImGui::Text("Velocity/Value"); + ImGui::TableNextColumn(); + ImGui::Text("Action"); + ImGui::TableNextColumn(); + ImGui::Text("Learn"); + ImGui::TableNextColumn(); + ImGui::Text("Remove"); + + for (size_t i=0; i10) vol=10; - sysVol=doInvert?-vol:vol; - settings.initialSys.set(fmt::sprintf("vol%d",i),(float)sysVol); - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderFloat("Panning",&sysPan,-1.0f,1.0f)) { - if (sysPan<-1.0f) sysPan=-1.0f; - if (sysPan>1.0f) sysPan=1.0f; - settings.initialSys.set(fmt::sprintf("pan%d",i),(float)sysPan); - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderFloat("Front/Rear",&sysPanFR,-1.0f,1.0f)) { - if (sysPanFR<-1.0f) sysPanFR=-1.0f; - if (sysPanFR>1.0f) sysPanFR=1.0f; - settings.initialSys.set(fmt::sprintf("fr%d",i),(float)sysPanFR); - } rightClickable + for (int j=0; j<16; j++) { + if (ImGui::Selectable(messageChannels[j],bind.channel==j)) { + bind.channel=j; + } + } + ImGui::EndCombo(); + } - // oh please MSVC don't cry - if (ImGui::TreeNode("Configure")) { - String sysFlagsS=settings.initialSys.getString(fmt::sprintf("flags%d",i),""); - DivConfig sysFlags; - sysFlags.loadFromBase64(sysFlagsS.c_str()); - if (drawSysConf(-1,sysID,sysFlags,false)) { - settings.initialSys.set(fmt::sprintf("flags%d",i),sysFlags.toBase64()); + ImGui::TableNextColumn(); + if (bind.data1==128) { + snprintf(bindID,1024,"Any"); + } else { + const char* nName="???"; + if ((bind.data1+60)>0 && (bind.data1+60)<180) { + nName=noteNames[bind.data1+60]; } - ImGui::TreePop(); + snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,nName); + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BValue1",bindID)) { + if (ImGui::Selectable("Any",bind.data1==128)) { + bind.data1=128; + } + for (int j=0; j<128; j++) { + const char* nName="???"; + if ((j+60)>0 && (j+60)<180) { + nName=noteNames[j+60]; + } + snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,nName,j); + if (ImGui::Selectable(bindID,bind.data1==j)) { + bind.data1=j; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + if (bind.data2==128) { + snprintf(bindID,1024,"Any"); + } else { + snprintf(bindID,1024,"%d (0x%.2X)",bind.data2,bind.data2); + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BValue2",bindID)) { + if (ImGui::Selectable("Any",bind.data2==128)) { + bind.data2=128; + } + for (int j=0; j<128; j++) { + snprintf(bindID,1024,"%d (0x%.2X)##BV2_%d",j,j,j); + if (ImGui::Selectable(bindID,bind.data2==j)) { + bind.data2=j; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BAction",(bind.action==0)?"--none--":guiActions[bind.action].friendlyName)) { + if (ImGui::Selectable("--none--",bind.action==0)) { + bind.action=0; + } + for (int j=0; j=0 && sysCount>1) { - for (int i=doRemove; i":settings.midiOutDevice; + if (ImGui::BeginCombo("##MidiOutDevice",midiOutName.c_str())) { + if (ImGui::Selectable("",settings.midiOutDevice.empty())) { + settings.midiOutDevice=""; + } + for (String& i: e->getMidiIns()) { + if (ImGui::Selectable(i.c_str(),i==settings.midiOutDevice)) { + settings.midiOutDevice=i; + } + } + ImGui::EndCombo(); + } + + ImGui::Text("Output mode:"); + if (ImGui::RadioButton("Off (use for TX81Z)",settings.midiOutMode==0)) { + settings.midiOutMode=0; + } + if (ImGui::RadioButton("Melodic",settings.midiOutMode==1)) { + settings.midiOutMode=1; + } + /* + if (ImGui::RadioButton("Light Show (use for Launchpad)",settings.midiOutMode==2)) { + settings.midiOutMode=2; + }*/ + + bool midiOutProgramChangeB=settings.midiOutProgramChange; + if (ImGui::Checkbox("Send Program Change",&midiOutProgramChangeB)) { + settings.midiOutProgramChange=midiOutProgramChangeB; + } + + bool midiOutClockB=settings.midiOutClock; + if (ImGui::Checkbox("Send MIDI clock",&midiOutClockB)) { + settings.midiOutClock=midiOutClockB; + } + + bool midiOutTimeB=settings.midiOutTime; + if (ImGui::Checkbox("Send MIDI timecode",&midiOutTimeB)) { + settings.midiOutTime=midiOutTimeB; + } + + if (settings.midiOutTime) { + ImGui::Text("Timecode frame rate:"); + if (ImGui::RadioButton("Closest to Tick Rate",settings.midiOutTimeRate==0)) { + settings.midiOutTimeRate=0; + } + if (ImGui::RadioButton("Film (24fps)",settings.midiOutTimeRate==1)) { + settings.midiOutTimeRate=1; + } + if (ImGui::RadioButton("PAL (25fps)",settings.midiOutTimeRate==2)) { + settings.midiOutTimeRate=2; + } + if (ImGui::RadioButton("NTSC drop (29.97fps)",settings.midiOutTimeRate==3)) { + settings.midiOutTimeRate=3; + } + if (ImGui::RadioButton("NTSC non-drop (30fps)",settings.midiOutTimeRate==4)) { + settings.midiOutTimeRate=4; + } + } + + END_SECTION; + } + CONFIG_SECTION("Emulation") { + // SUBSECTION LAYOUT + CONFIG_SUBSECTION("Cores"); + ImGui::Text("Arcade/YM2151 core"); + ImGui::SameLine(); + ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2); + + ImGui::Text("Genesis/YM2612 core"); + ImGui::SameLine(); + ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2); + + ImGui::Text("SN76489 core"); + ImGui::SameLine(); + ImGui::Combo("##SNCore",&settings.snCore,snCores,2); + + ImGui::Text("NES core"); + ImGui::SameLine(); + ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); + + ImGui::Text("FDS core"); + ImGui::SameLine(); + ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + + ImGui::Text("SID core"); + ImGui::SameLine(); + ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,3); + + ImGui::Text("POKEY core"); + ImGui::SameLine(); + ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2); + + ImGui::Text("OPN/OPNA/OPNB cores"); + ImGui::SameLine(); + ImGui::Combo("##OPNCore",&settings.opnCore,opnCores,2); + + ImGui::Separator(); + + ImGui::Text("PC Speaker strategy"); + ImGui::SameLine(); + ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); + + ImGui::Separator(); + ImGui::Text("Sample ROMs:"); + + ImGui::Text("OPL4 YRW801 path"); + ImGui::SameLine(); + ImGui::InputText("##YRW801Path",&settings.yrw801Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##YRW801Load")) { + openFileDialog(GUI_FILE_YRW801_ROM_OPEN); + } + + ImGui::Text("MultiPCM TG100 path"); + ImGui::SameLine(); + ImGui::InputText("##TG100Path",&settings.tg100Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##TG100Load")) { + openFileDialog(GUI_FILE_TG100_ROM_OPEN); + } + + ImGui::Text("MultiPCM MU5 path"); + ImGui::SameLine(); + ImGui::InputText("##MU5Path",&settings.mu5Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) { + openFileDialog(GUI_FILE_MU5_ROM_OPEN); + } + + END_SECTION; + } + CONFIG_SECTION("Keyboard") { + // SUBSECTION LAYOUT + CONFIG_SUBSECTION("Keyboard"); + if (ImGui::Button("Import")) { + openFileDialog(GUI_FILE_IMPORT_KEYBINDS); + } + ImGui::SameLine(); + if (ImGui::Button("Export")) { + openFileDialog(GUI_FILE_EXPORT_KEYBINDS); + } + ImGui::SameLine(); + if (ImGui::Button("Reset defaults")) { + showWarning("Are you sure you want to reset the keyboard settings?",GUI_WARN_RESET_KEYBINDS); + } + if (ImGui::TreeNode("Global hotkeys")) { + KEYBIND_CONFIG_BEGIN("keysGlobal"); + + UI_KEYBIND_CONFIG(GUI_ACTION_NEW); + UI_KEYBIND_CONFIG(GUI_ACTION_OPEN); + 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_UNDO); + UI_KEYBIND_CONFIG(GUI_ACTION_REDO); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_TOGGLE); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY); + UI_KEYBIND_CONFIG(GUI_ACTION_STOP); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_START); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_REPEAT); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_CURSOR); + UI_KEYBIND_CONFIG(GUI_ACTION_STEP_ONE); + UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_STEP_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_STEP_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_TOGGLE_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_METRONOME); + UI_KEYBIND_CONFIG(GUI_ACTION_REPEAT_PATTERN); + UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_ORDERS); + UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_PATTERN); + UI_KEYBIND_CONFIG(GUI_ACTION_FULLSCREEN); + UI_KEYBIND_CONFIG(GUI_ACTION_PANIC); + + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Window activation")) { + KEYBIND_CONFIG_BEGIN("keysWindow"); + + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EDIT_CONTROLS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ORDERS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SUBSONGS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_LIST); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ABOUT); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SETTINGS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_MIXER); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_OSCILLOSCOPE); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHAN_OSC); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EFFECT_LIST); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_VOL_METER); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PIANO); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_NOTES); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHANNELS); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_REGISTER_VIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_LOG); + + UI_KEYBIND_CONFIG(GUI_ACTION_COLLAPSE_WINDOW); + UI_KEYBIND_CONFIG(GUI_ACTION_CLOSE_WINDOW); + + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Note input")) { + std::vector sorted; + if (ImGui::BeginTable("keysNoteInput",4)) { + for (std::map::value_type& i: noteKeys) { + std::vector::iterator j; + for (j=sorted.begin(); j!=sorted.end(); j++) { + if (j->val>i.second) { + break; + } + } + sorted.insert(j,MappedInput(i.first,i.second)); } - settings.initialSys.remove(fmt::sprintf("id%d",sysCount-1)); - settings.initialSys.remove(fmt::sprintf("vol%d",sysCount-1)); - settings.initialSys.remove(fmt::sprintf("pan%d",sysCount-1)); - settings.initialSys.remove(fmt::sprintf("fr%d",sysCount-1)); - settings.initialSys.remove(fmt::sprintf("flags%d",sysCount-1)); - } + static char id[4096]; - if (sysCount<32) if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { - settings.initialSys.set(fmt::sprintf("id%d",sysCount),(int)e->systemToFileFur(DIV_SYSTEM_YM2612)); - settings.initialSys.set(fmt::sprintf("vol%d",sysCount),1.0f); - settings.initialSys.set(fmt::sprintf("pan%d",sysCount),0.0f); - settings.initialSys.set(fmt::sprintf("fr%d",sysCount),0.0f); - settings.initialSys.set(fmt::sprintf("flags%d",sysCount),""); - } + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Key"); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::TableNextColumn(); + ImGui::Text("Value"); + ImGui::TableNextColumn(); + ImGui::Text("Remove"); - ImGui::Separator(); + for (MappedInput& i: sorted) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s",SDL_GetScancodeName((SDL_Scancode)i.scan)); + ImGui::TableNextColumn(); + if (i.val==102) { + snprintf(id,4095,"Macro release##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=0; + } + } else if (i.val==101) { + snprintf(id,4095,"Note release##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=102; + } + } else if (i.val==100) { + snprintf(id,4095,"Note off##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=101; + } + } else { + snprintf(id,4095,"Note##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=100; + } + } + ImGui::TableNextColumn(); + if (i.val<100) { + snprintf(id,4095,"##SNValue_%d",i.scan); + if (ImGui::InputInt(id,&i.val,1,1)) { + if (i.val<0) i.val=0; + if (i.val>96) i.val=96; + noteKeys[i.scan]=i.val; + } + } + ImGui::TableNextColumn(); + snprintf(id,4095,ICON_FA_TIMES "##SNRemove_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys.erase(i.scan); + } + } + ImGui::EndTable(); - ImGui::Text("Play intro on start-up:"); - if (ImGui::RadioButton("No##pis0",settings.alwaysPlayIntro==0)) { - settings.alwaysPlayIntro=0; - } - if (ImGui::RadioButton("Short##pis1",settings.alwaysPlayIntro==1)) { - settings.alwaysPlayIntro=1; - } - if (ImGui::RadioButton("Full (short when loading song)##pis2",settings.alwaysPlayIntro==2)) { - settings.alwaysPlayIntro=2; - } - if (ImGui::RadioButton("Full (always)##pis3",settings.alwaysPlayIntro==3)) { - settings.alwaysPlayIntro=3; + if (ImGui::BeginCombo("##SNAddNew","Add...")) { + for (int i=0; i1.0) settings.doubleClickTime=1.0; + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_ADD); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DUPLICATE); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN_REPLACE); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DELETE); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DIR_VIEW); - applyUISettings(false); - } + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Wavetable list")) { + KEYBIND_CONFIG_BEGIN("keysWaveList"); - ImGui::Text("Toggle channel solo on:"); - if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { - settings.soloAction=0; - } - if (ImGui::RadioButton("Right-click##soloR",settings.soloAction==1)) { - settings.soloAction=1; - } - if (ImGui::RadioButton("Double-click##soloD",settings.soloAction==2)) { - settings.soloAction=2; - } + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_ADD); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DUPLICATE); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_MOVE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_MOVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DELETE); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DIR_VIEW); - bool pushNibbleB=settings.pushNibble; - if (ImGui::Checkbox("Push value when overwriting instead of clearing it",&pushNibbleB)) { - settings.pushNibble=pushNibbleB; - } + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Sample list")) { + KEYBIND_CONFIG_BEGIN("keysSampleList"); - bool pullDeleteBehaviorB=settings.pullDeleteBehavior; - if (ImGui::Checkbox("Move cursor up on backspace-delete",&pullDeleteBehaviorB)) { - settings.pullDeleteBehavior=pullDeleteBehaviorB; - } + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_ADD); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DUPLICATE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_SAVE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MOVE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DELETE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_EDIT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW); - bool stepOnDeleteB=settings.stepOnDelete; - if (ImGui::Checkbox("Move cursor by edit step on delete",&stepOnDeleteB)) { - settings.stepOnDelete=stepOnDeleteB; - } + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Orders")) { + KEYBIND_CONFIG_BEGIN("keysOrders"); - bool insertBehaviorB=settings.insertBehavior; - if (ImGui::Checkbox("Insert pushes entire channel row",&insertBehaviorB)) { - settings.insertBehavior=insertBehaviorB; - } + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_LEFT); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_RIGHT); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_INCREASE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DECREASE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_EDIT_MODE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_LINK); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_ADD); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DUPLICATE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DEEP_CLONE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DUPLICATE_END); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_DEEP_CLONE_END); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_REMOVE); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_MOVE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_MOVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_ORDERS_REPLAY); - bool pullDeleteRowB=settings.pullDeleteRow; - if (ImGui::Checkbox("Pull delete affects entire channel row",&pullDeleteRowB)) { - settings.pullDeleteRow=pullDeleteRowB; - } + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Sample editor")) { + KEYBIND_CONFIG_BEGIN("keysSampleEdit"); - bool absorbInsInputB=settings.absorbInsInput; - if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) { - settings.absorbInsInput=absorbInsInputB; - } + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DRAW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_CUT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_COPY); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_REPLACE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_MIX); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT_ALL); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESIZE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESAMPLE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_AMPLIFY); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_NORMALIZE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_IN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_OUT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_INSERT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SILENCE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DELETE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_TRIM); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_REVERSE); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_INVERT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SIGN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FILTER); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PREVIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_STOP_PREVIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_IN); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_MAKE_INS); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SET_LOOP); - bool effectDeletionAltersValueB=settings.effectDeletionAltersValue; - if (ImGui::Checkbox("Delete effect value when deleting effect",&effectDeletionAltersValueB)) { - settings.effectDeletionAltersValue=effectDeletionAltersValueB; - } + KEYBIND_CONFIG_END; + ImGui::TreePop(); + } + END_SECTION; + } + CONFIG_SECTION("Interface") { + // SUBSECTION LAYOUT + CONFIG_SUBSECTION("Layout"); + ImGui::Text("Workspace layout:"); + ImGui::SameLine(); + if (ImGui::Button("Import")) { + openFileDialog(GUI_FILE_IMPORT_LAYOUT); + } + ImGui::SameLine(); + if (ImGui::Button("Export")) { + openFileDialog(GUI_FILE_EXPORT_LAYOUT); + } + ImGui::SameLine(); + if (ImGui::Button("Reset")) { + showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); + } - bool stepOnInsertB=settings.stepOnInsert; - if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) { - settings.stepOnInsert=stepOnInsertB; - } - - bool cursorPastePosB=settings.cursorPastePos; - if (ImGui::Checkbox("Move cursor to end of clipboard content when pasting",&cursorPastePosB)) { - settings.cursorPastePos=cursorPastePosB; - } - - bool cursorMoveNoScrollB=settings.cursorMoveNoScroll; - if (ImGui::Checkbox("Don't scroll when moving cursor",&cursorMoveNoScrollB)) { - settings.cursorMoveNoScroll=cursorMoveNoScrollB; - } - - bool cursorFollowsWheelB=settings.cursorFollowsWheel; - if (ImGui::Checkbox("Move cursor with scroll wheel",&cursorFollowsWheelB)) { - settings.cursorFollowsWheel=cursorFollowsWheelB; - } - - bool doubleClickColumnB=settings.doubleClickColumn; - if (ImGui::Checkbox("Double click selects entire column",&doubleClickColumnB)) { - settings.doubleClickColumn=doubleClickColumnB; - } - - bool allowEditDockingB=settings.allowEditDocking; - if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { - settings.allowEditDocking=allowEditDockingB; - } - - bool avoidRaisingPatternB=settings.avoidRaisingPattern; - if (ImGui::Checkbox("Don't raise pattern editor on click",&avoidRaisingPatternB)) { - settings.avoidRaisingPattern=avoidRaisingPatternB; - } - - bool insFocusesPatternB=settings.insFocusesPattern; - if (ImGui::Checkbox("Focus pattern editor when selecting instrument",&insFocusesPatternB)) { - settings.insFocusesPattern=insFocusesPatternB; - } - - bool restartOnFlagChangeB=settings.restartOnFlagChange; - if (ImGui::Checkbox("Restart song when changing chip properties",&restartOnFlagChangeB)) { - settings.restartOnFlagChange=restartOnFlagChangeB; - } - - bool sysFileDialogB=settings.sysFileDialog; - if (ImGui::Checkbox("Use system file picker",&sysFileDialogB)) { - settings.sysFileDialog=sysFileDialogB; - } - - bool moveWindowTitleB=settings.moveWindowTitle; - if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) { - settings.moveWindowTitle=moveWindowTitleB; - applyUISettings(false); - } - - bool eventDelayB=settings.eventDelay; - if (ImGui::Checkbox("Enable event delay",&eventDelayB)) { - settings.eventDelay=eventDelayB; - applyUISettings(false); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes."); - } - - bool powerSaveB=settings.powerSave; - if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) { - settings.powerSave=powerSaveB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!"); - } - - bool renderClearPosB=settings.renderClearPos; - if (ImGui::Checkbox("Late render clear",&renderClearPosB)) { - settings.renderClearPos=renderClearPosB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("calls rend->clear() after rend->present(). might reduce UI latency by one frame in some drivers."); - } + bool allowEditDockingB=settings.allowEditDocking; + if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { + settings.allowEditDocking=allowEditDockingB; + } #ifndef IS_MOBILE - bool noThreadedInputB=settings.noThreadedInput; - if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) { - settings.noThreadedInput=noThreadedInputB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.\nhowever, crashes have been reported when threaded input is on. enable this option if that is the case."); - } - bool saveWindowPosB=settings.saveWindowPos; if (ImGui::Checkbox("Remember window position",&saveWindowPosB)) { settings.saveWindowPos=saveWindowPosB; @@ -671,1925 +1608,1057 @@ void FurnaceGUI::drawSettings() { } #endif - bool blankInsB=settings.blankIns; - if (ImGui::Checkbox("New instruments are blank",&blankInsB)) { - settings.blankIns=blankInsB; - } - - bool saveUnusedPatternsB=settings.saveUnusedPatterns; - if (ImGui::Checkbox("Save unused patterns",&saveUnusedPatternsB)) { - settings.saveUnusedPatterns=saveUnusedPatternsB; - } - - bool compressB=settings.compress; - if (ImGui::Checkbox("Compress when saving",&compressB)) { - settings.compress=compressB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use zlib to compress saved songs."); - } - - bool newPatternFormatB=settings.newPatternFormat; - if (ImGui::Checkbox("Use new pattern format when saving",&newPatternFormatB)) { - settings.newPatternFormat=newPatternFormatB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a packed format which saves space when saving songs.\ndisable if you need compatibility with older Furnace and/or tools\nwhich do not support this format."); - } - - bool cursorFollowsOrderB=settings.cursorFollowsOrder; - if (ImGui::Checkbox("Cursor follows current order when moving it",&cursorFollowsOrderB)) { - settings.cursorFollowsOrder=cursorFollowsOrderB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("applies when playback is stopped."); - } - - ImGui::Text("Audio export loop/fade out time:"); - if (ImGui::RadioButton("Set to these values on start-up:##fot0",settings.persistFadeOut==0)) { - settings.persistFadeOut=0; - } - ImGui::BeginDisabled(settings.persistFadeOut); - if (ImGui::InputInt("Loops",&settings.exportLoops,1,2)) { - if (exportLoops<0) exportLoops=0; - exportLoops=settings.exportLoops; - } - if (ImGui::InputDouble("Fade out (seconds)",&settings.exportFadeOut,1.0,2.0,"%.1f")) { - if (exportFadeOut<0.0) exportFadeOut=0.0; - exportFadeOut=settings.exportFadeOut; - } - ImGui::EndDisabled(); - if (ImGui::RadioButton("Remember last values##fot1",settings.persistFadeOut==1)) { - settings.persistFadeOut=1; - } - - ImGui::Text("Note preview behavior:"); - if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { - settings.notePreviewBehavior=0; - } - if (ImGui::RadioButton("When cursor is in Note column##npb1",settings.notePreviewBehavior==1)) { - settings.notePreviewBehavior=1; - } - if (ImGui::RadioButton("When cursor is in Note column or not in edit mode##npb2",settings.notePreviewBehavior==2)) { - settings.notePreviewBehavior=2; - } - if (ImGui::RadioButton("Always##npb3",settings.notePreviewBehavior==3)) { - settings.notePreviewBehavior=3; - } - - ImGui::Text("Wrap pattern cursor horizontally:"); - if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) { - settings.wrapHorizontal=0; - } - if (ImGui::RadioButton("Yes##wrapH1",settings.wrapHorizontal==1)) { - settings.wrapHorizontal=1; - } - if (ImGui::RadioButton("Yes, and move to next/prev row##wrapH2",settings.wrapHorizontal==2)) { - settings.wrapHorizontal=2; - } - - ImGui::Text("Wrap pattern cursor vertically:"); - if (ImGui::RadioButton("No##wrapV0",settings.wrapVertical==0)) { - settings.wrapVertical=0; - } - if (ImGui::RadioButton("Yes##wrapV1",settings.wrapVertical==1)) { - settings.wrapVertical=1; - } - if (ImGui::RadioButton("Yes, and move to next/prev pattern##wrapV2",settings.wrapVertical==2)) { - settings.wrapVertical=2; - } - if (ImGui::RadioButton("Yes, and move to next/prev pattern (wrap around)##wrapV2",settings.wrapVertical==3)) { - settings.wrapVertical=3; - } - - ImGui::Text("Change order when scrolling outside of pattern bounds:"); - if (ImGui::RadioButton("No##pscroll0",settings.scrollChangesOrder==0)) { - settings.scrollChangesOrder=0; - } - if (ImGui::RadioButton("Yes##pscroll1",settings.scrollChangesOrder==1)) { - settings.scrollChangesOrder=1; - } - if (ImGui::RadioButton("Yes, and wrap around song##pscroll2",settings.scrollChangesOrder==2)) { - settings.scrollChangesOrder=2; - } - - - ImGui::Text("Cursor movement keys behavior:"); - if (ImGui::RadioButton("Move by one##cmk0",settings.scrollStep==0)) { - settings.scrollStep=0; - } - if (ImGui::RadioButton("Move by Edit Step##cmk1",settings.scrollStep==1)) { - settings.scrollStep=1; - } - - ImGui::Text("Effect input cursor behavior:"); - if (ImGui::RadioButton("Move down##eicb0",settings.effectCursorDir==0)) { - settings.effectCursorDir=0; - } - if (ImGui::RadioButton("Move to effect value (otherwise move down)##eicb1",settings.effectCursorDir==1)) { - settings.effectCursorDir=1; - } - if (ImGui::RadioButton("Move to effect value/next effect and wrap around##eicb2",settings.effectCursorDir==2)) { - settings.effectCursorDir=2; - } - - ImGui::Text("Allow dragging selection:"); - if (ImGui::RadioButton("No##dms0",settings.dragMovesSelection==0)) { - settings.dragMovesSelection=0; - } - if (ImGui::RadioButton("Yes##dms1",settings.dragMovesSelection==1)) { - settings.dragMovesSelection=1; - } - if (ImGui::RadioButton("Yes (while holding Ctrl only)##dms2",settings.dragMovesSelection==2)) { - settings.dragMovesSelection=2; - } + bool moveWindowTitleB=settings.moveWindowTitle; + if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) { + settings.moveWindowTitle=moveWindowTitleB; + applyUISettings(false); } - ImGui::EndChild(); - ImGui::EndTabItem(); + + ImGui::Text("Play/edit controls layout:"); + if (ImGui::RadioButton("Classic##ecl0",settings.controlLayout==0)) { + settings.controlLayout=0; + } + if (ImGui::RadioButton("Compact##ecl1",settings.controlLayout==1)) { + settings.controlLayout=1; + } + if (ImGui::RadioButton("Compact (vertical)##ecl2",settings.controlLayout==2)) { + settings.controlLayout=2; + } + if (ImGui::RadioButton("Split##ecl3",settings.controlLayout==3)) { + settings.controlLayout=3; + } + + ImGui::Text("Position of buttons in Orders:"); + if (ImGui::RadioButton("Top##obp0",settings.orderButtonPos==0)) { + settings.orderButtonPos=0; + } + if (ImGui::RadioButton("Left##obp1",settings.orderButtonPos==1)) { + settings.orderButtonPos=1; + } + if (ImGui::RadioButton("Right##obp2",settings.orderButtonPos==2)) { + settings.orderButtonPos=2; + } + + // SUBSECTION MOUSE + CONFIG_SUBSECTION("Mouse"); + + if (CWSliderFloat("Double-click time (seconds)",&settings.doubleClickTime,0.02,1.0,"%.2f")) { + if (settings.doubleClickTime<0.02) settings.doubleClickTime=0.02; + if (settings.doubleClickTime>1.0) settings.doubleClickTime=1.0; + + applyUISettings(false); + } + + bool avoidRaisingPatternB=settings.avoidRaisingPattern; + if (ImGui::Checkbox("Don't raise pattern editor on click",&avoidRaisingPatternB)) { + settings.avoidRaisingPattern=avoidRaisingPatternB; + } + + bool insFocusesPatternB=settings.insFocusesPattern; + if (ImGui::Checkbox("Focus pattern editor when selecting instrument",&insFocusesPatternB)) { + settings.insFocusesPattern=insFocusesPatternB; + } + + ImGui::Text("Note preview behavior:"); + if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { + settings.notePreviewBehavior=0; + } + if (ImGui::RadioButton("When cursor is in Note column##npb1",settings.notePreviewBehavior==1)) { + settings.notePreviewBehavior=1; + } + if (ImGui::RadioButton("When cursor is in Note column or not in edit mode##npb2",settings.notePreviewBehavior==2)) { + settings.notePreviewBehavior=2; + } + if (ImGui::RadioButton("Always##npb3",settings.notePreviewBehavior==3)) { + settings.notePreviewBehavior=3; + } + + ImGui::Text("Allow dragging selection:"); + if (ImGui::RadioButton("No##dms0",settings.dragMovesSelection==0)) { + settings.dragMovesSelection=0; + } + if (ImGui::RadioButton("Yes##dms1",settings.dragMovesSelection==1)) { + settings.dragMovesSelection=1; + } + if (ImGui::RadioButton("Yes (while holding Ctrl only)##dms2",settings.dragMovesSelection==2)) { + settings.dragMovesSelection=2; + } + + ImGui::Text("Toggle channel solo on:"); + if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { + settings.soloAction=0; + } + if (ImGui::RadioButton("Right-click##soloR",settings.soloAction==1)) { + settings.soloAction=1; + } + if (ImGui::RadioButton("Double-click##soloD",settings.soloAction==2)) { + settings.soloAction=2; + } + + bool doubleClickColumnB=settings.doubleClickColumn; + if (ImGui::Checkbox("Double click selects entire column",&doubleClickColumnB)) { + settings.doubleClickColumn=doubleClickColumnB; + } + + // SUBSECTION CURSOR BEHAVIOR + CONFIG_SUBSECTION("Cursor behavior"); + bool insertBehaviorB=settings.insertBehavior; + if (ImGui::Checkbox("Insert pushes entire channel row",&insertBehaviorB)) { + settings.insertBehavior=insertBehaviorB; + } + + bool pullDeleteRowB=settings.pullDeleteRow; + if (ImGui::Checkbox("Pull delete affects entire channel row",&pullDeleteRowB)) { + settings.pullDeleteRow=pullDeleteRowB; + } + + bool pushNibbleB=settings.pushNibble; + if (ImGui::Checkbox("Push value when overwriting instead of clearing it",&pushNibbleB)) { + settings.pushNibble=pushNibbleB; + } + + ImGui::Text("Effect input behavior:"); + if (ImGui::RadioButton("Move down##eicb0",settings.effectCursorDir==0)) { + settings.effectCursorDir=0; + } + if (ImGui::RadioButton("Move to effect value (otherwise move down)##eicb1",settings.effectCursorDir==1)) { + settings.effectCursorDir=1; + } + if (ImGui::RadioButton("Move to effect value/next effect and wrap around##eicb2",settings.effectCursorDir==2)) { + settings.effectCursorDir=2; + } + + bool effectDeletionAltersValueB=settings.effectDeletionAltersValue; + if (ImGui::Checkbox("Delete effect value when deleting effect",&effectDeletionAltersValueB)) { + settings.effectDeletionAltersValue=effectDeletionAltersValueB; + } + + bool absorbInsInputB=settings.absorbInsInput; + if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) { + settings.absorbInsInput=absorbInsInputB; + } + + // SUBSECTION CURSOR MOVEMENT + CONFIG_SUBSECTION("Cursor movement"); + + ImGui::Text("Wrap horizontally:"); + if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) { + settings.wrapHorizontal=0; + } + if (ImGui::RadioButton("Yes##wrapH1",settings.wrapHorizontal==1)) { + settings.wrapHorizontal=1; + } + if (ImGui::RadioButton("Yes, and move to next/prev row##wrapH2",settings.wrapHorizontal==2)) { + settings.wrapHorizontal=2; + } + + ImGui::Text("Wrap vertically:"); + if (ImGui::RadioButton("No##wrapV0",settings.wrapVertical==0)) { + settings.wrapVertical=0; + } + if (ImGui::RadioButton("Yes##wrapV1",settings.wrapVertical==1)) { + settings.wrapVertical=1; + } + if (ImGui::RadioButton("Yes, and move to next/prev pattern##wrapV2",settings.wrapVertical==2)) { + settings.wrapVertical=2; + } + if (ImGui::RadioButton("Yes, and move to next/prev pattern (wrap around)##wrapV2",settings.wrapVertical==3)) { + settings.wrapVertical=3; + } + + ImGui::Text("Cursor movement keys behavior:"); + if (ImGui::RadioButton("Move by one##cmk0",settings.scrollStep==0)) { + settings.scrollStep=0; + } + if (ImGui::RadioButton("Move by Edit Step##cmk1",settings.scrollStep==1)) { + settings.scrollStep=1; + } + + bool stepOnDeleteB=settings.stepOnDelete; + if (ImGui::Checkbox("Move cursor by edit step on delete",&stepOnDeleteB)) { + settings.stepOnDelete=stepOnDeleteB; + } + + bool stepOnInsertB=settings.stepOnInsert; + if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) { + settings.stepOnInsert=stepOnInsertB; + } + + bool pullDeleteBehaviorB=settings.pullDeleteBehavior; + if (ImGui::Checkbox("Move cursor up on backspace-delete",&pullDeleteBehaviorB)) { + settings.pullDeleteBehavior=pullDeleteBehaviorB; + } + + bool cursorPastePosB=settings.cursorPastePos; + if (ImGui::Checkbox("Move cursor to end of clipboard content when pasting",&cursorPastePosB)) { + settings.cursorPastePos=cursorPastePosB; + } + + // SUBSECTION SCROLLING + CONFIG_SUBSECTION("Scrolling"); + + ImGui::Text("Change order when scrolling outside of pattern bounds:"); + if (ImGui::RadioButton("No##pscroll0",settings.scrollChangesOrder==0)) { + settings.scrollChangesOrder=0; + } + if (ImGui::RadioButton("Yes##pscroll1",settings.scrollChangesOrder==1)) { + settings.scrollChangesOrder=1; + } + if (ImGui::RadioButton("Yes, and wrap around song##pscroll2",settings.scrollChangesOrder==2)) { + settings.scrollChangesOrder=2; + } + + bool cursorFollowsOrderB=settings.cursorFollowsOrder; + if (ImGui::Checkbox("Cursor follows current order when moving it",&cursorFollowsOrderB)) { + settings.cursorFollowsOrder=cursorFollowsOrderB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("applies when playback is stopped."); + } + + bool cursorMoveNoScrollB=settings.cursorMoveNoScroll; + if (ImGui::Checkbox("Don't scroll when moving cursor",&cursorMoveNoScrollB)) { + settings.cursorMoveNoScroll=cursorMoveNoScrollB; + } + + bool cursorFollowsWheelB=settings.cursorFollowsWheel; + if (ImGui::Checkbox("Move cursor with scroll wheel",&cursorFollowsWheelB)) { + settings.cursorFollowsWheel=cursorFollowsWheelB; + } + + END_SECTION; } - if (ImGui::BeginTabItem("Audio/MIDI")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { -#ifdef HAVE_JACK - ImGui::Text("Backend"); - ImGui::SameLine(); - int prevAudioEngine=settings.audioEngine; - if (ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2)) { - if (settings.audioEngine!=prevAudioEngine) { - if (!isProAudio[settings.audioEngine]) settings.audioChans=2; - } - } -#endif - - if (settings.audioEngine==DIV_AUDIO_SDL) { - ImGui::Text("Driver"); - ImGui::SameLine(); - if (ImGui::BeginCombo("##SDLADriver",settings.sdlAudioDriver.empty()?"Automatic":settings.sdlAudioDriver.c_str())) { - if (ImGui::Selectable("Automatic",settings.sdlAudioDriver.empty())) { - settings.sdlAudioDriver=""; - } - for (String& i: availAudioDrivers) { - if (ImGui::Selectable(i.c_str(),i==settings.sdlAudioDriver)) { - settings.sdlAudioDriver=i; - } - } - ImGui::EndCombo(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); - } - } - - ImGui::Text("Device"); - ImGui::SameLine(); - String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; - if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { - if (ImGui::Selectable("",settings.audioDevice.empty())) { - settings.audioDevice=""; - } - for (String& i: e->getAudioDevices()) { - if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { - settings.audioDevice=i; - } - } - ImGui::EndCombo(); - } - - ImGui::Text("Sample rate"); - ImGui::SameLine(); - String sr=fmt::sprintf("%d",settings.audioRate); - if (ImGui::BeginCombo("##SampleRate",sr.c_str())) { - SAMPLE_RATE_SELECTABLE(8000); - SAMPLE_RATE_SELECTABLE(16000); - SAMPLE_RATE_SELECTABLE(22050); - SAMPLE_RATE_SELECTABLE(32000); - SAMPLE_RATE_SELECTABLE(44100); - SAMPLE_RATE_SELECTABLE(48000); - SAMPLE_RATE_SELECTABLE(88200); - SAMPLE_RATE_SELECTABLE(96000); - SAMPLE_RATE_SELECTABLE(192000); - ImGui::EndCombo(); - } - - if (isProAudio[settings.audioEngine]) { - ImGui::Text("Outputs"); - ImGui::SameLine(); - if (ImGui::InputInt("##AudioChansI",&settings.audioChans,1,1)) { - if (settings.audioChans<1) settings.audioChans=1; - if (settings.audioChans>16) settings.audioChans=16; - } + CONFIG_SECTION("Appearance") { + // SUBSECTION INTERFACE + CONFIG_SUBSECTION("Scaling"); + bool dpiScaleAuto=(settings.dpiScale<0.5f); + if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) { + if (dpiScaleAuto) { + settings.dpiScale=0.0f; } else { - ImGui::Text("Channels"); - ImGui::SameLine(); - String chStr=(settings.audioChans<1 || settings.audioChans>8)?"What?":nonProAudioOuts[settings.audioChans-1]; - if (ImGui::BeginCombo("##AudioChans",chStr.c_str())) { - CHANS_SELECTABLE(1); - CHANS_SELECTABLE(2); - CHANS_SELECTABLE(4); - CHANS_SELECTABLE(6); - CHANS_SELECTABLE(8); - ImGui::EndCombo(); - } - } - - ImGui::Text("Buffer size"); - ImGui::SameLine(); - String bs=fmt::sprintf("%d (latency: ~%.1fms)",settings.audioBufSize,2000.0*(double)settings.audioBufSize/(double)MAX(1,settings.audioRate)); - if (ImGui::BeginCombo("##BufferSize",bs.c_str())) { - BUFFER_SIZE_SELECTABLE(64); - BUFFER_SIZE_SELECTABLE(128); - BUFFER_SIZE_SELECTABLE(256); - BUFFER_SIZE_SELECTABLE(512); - BUFFER_SIZE_SELECTABLE(1024); - BUFFER_SIZE_SELECTABLE(2048); - ImGui::EndCombo(); - } - - ImGui::Text("Quality"); - ImGui::SameLine(); - ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); - - ImGui::Text("Metronome volume"); - ImGui::SameLine(); - if (ImGui::SliderInt("##MetroVol",&settings.metroVol,0,200,"%d%%")) { - if (settings.metroVol<0) settings.metroVol=0; - if (settings.metroVol>200) settings.metroVol=200; - e->setMetronomeVol(((float)settings.metroVol)/100.0f); - } - - bool lowLatencyB=settings.lowLatency; - if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) { - settings.lowLatency=lowLatencyB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less)."); - } - - bool forceMonoB=settings.forceMono; - if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { - settings.forceMono=forceMonoB; - } - - bool clampSamplesB=settings.clampSamples; - if (ImGui::Checkbox("Software clipping",&clampSamplesB)) { - settings.clampSamples=clampSamplesB; - } - - TAAudioDesc& audioWant=e->getAudioDescWant(); - TAAudioDesc& audioGot=e->getAudioDescGot(); - - ImGui::Text("want: %d samples @ %.0fHz (%d channels)",audioWant.bufsize,audioWant.rate,audioWant.outChans); - ImGui::Text("got: %d samples @ %.0fHz (%d channels)",audioGot.bufsize,audioGot.rate,audioWant.outChans); - - ImGui::Separator(); - - ImGui::Text("MIDI input"); - ImGui::SameLine(); - String midiInName=settings.midiInDevice.empty()?"":settings.midiInDevice; - bool hasToReloadMidi=false; - if (ImGui::BeginCombo("##MidiInDevice",midiInName.c_str())) { - if (ImGui::Selectable("",settings.midiInDevice.empty())) { - settings.midiInDevice=""; - hasToReloadMidi=true; - } - for (String& i: e->getMidiIns()) { - if (ImGui::Selectable(i.c_str(),i==settings.midiInDevice)) { - settings.midiInDevice=i; - hasToReloadMidi=true; - } - } - ImGui::EndCombo(); - } - - if (hasToReloadMidi) { - midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); - midiMap.compile(); - } - - ImGui::Text("MIDI output"); - ImGui::SameLine(); - String midiOutName=settings.midiOutDevice.empty()?"":settings.midiOutDevice; - if (ImGui::BeginCombo("##MidiOutDevice",midiOutName.c_str())) { - if (ImGui::Selectable("",settings.midiOutDevice.empty())) { - settings.midiOutDevice=""; - } - for (String& i: e->getMidiIns()) { - if (ImGui::Selectable(i.c_str(),i==settings.midiOutDevice)) { - settings.midiOutDevice=i; - } - } - ImGui::EndCombo(); - } - - if (ImGui::TreeNode("MIDI input settings")) { - ImGui::Checkbox("Note input",&midiMap.noteInput); - ImGui::Checkbox("Velocity input",&midiMap.volInput); - // TODO - //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); - //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); - ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel); - ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse); - ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange); - //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); - //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); - ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7); - if (midiMap.valueInputStyle>3) { - if (midiMap.valueInputStyle==6) { - if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputControlSingle,1,16)) { - if (midiMap.valueInputControlSingle<0) midiMap.valueInputControlSingle=0; - if (midiMap.valueInputControlSingle>127) midiMap.valueInputControlSingle=127; - } - } else { - if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) { - if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0; - if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127; - } - if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) { - if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0; - if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127; - } - } - } - if (ImGui::TreeNode("Per-column control change")) { - for (int i=0; i<18; i++) { - ImGui::PushID(i); - ImGui::Combo(specificControls[i],&midiMap.valueInputSpecificStyle[i],valueSInputStyles,4); - if (midiMap.valueInputSpecificStyle[i]>0) { - ImGui::Indent(); - if (midiMap.valueInputSpecificStyle[i]==3) { - if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputSpecificSingle[i],1,16)) { - if (midiMap.valueInputSpecificSingle[i]<0) midiMap.valueInputSpecificSingle[i]=0; - if (midiMap.valueInputSpecificSingle[i]>127) midiMap.valueInputSpecificSingle[i]=127; - } - } else { - if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputSpecificMSB[i],1,16)) { - if (midiMap.valueInputSpecificMSB[i]<0) midiMap.valueInputSpecificMSB[i]=0; - if (midiMap.valueInputSpecificMSB[i]>127) midiMap.valueInputSpecificMSB[i]=127; - } - if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputSpecificLSB[i],1,16)) { - if (midiMap.valueInputSpecificLSB[i]<0) midiMap.valueInputSpecificLSB[i]=0; - if (midiMap.valueInputSpecificLSB[i]>127) midiMap.valueInputSpecificLSB[i]=127; - } - } - ImGui::Unindent(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { - if (midiMap.volExp<0.01) midiMap.volExp=0.01; - if (midiMap.volExp>8.0) midiMap.volExp=8.0; - } rightClickable - float curve[128]; - for (int i=0; i<128; i++) { - curve[i]=(int)(pow((double)i/127.0,midiMap.volExp)*127.0); - } - ImGui::PlotLines("##VolCurveDisplay",curve,128,0,"Volume curve",0.0,127.0,ImVec2(200.0f*dpiScale,200.0f*dpiScale)); - - ImGui::Text("Actions:"); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS "##AddAction")) { - midiMap.binds.push_back(MIDIBind()); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EXTERNAL_LINK "##AddLearnAction")) { - midiMap.binds.push_back(MIDIBind()); - learning=midiMap.binds.size()-1; - } - if (learning!=-1) { - ImGui::SameLine(); - ImGui::Text("(learning! press a button or move a slider/knob/something on your device.)"); - } - - if (ImGui::BeginTable("MIDIActions",7)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.2); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.1); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.3); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.2); - ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.5); - ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - ImGui::TableNextColumn(); - ImGui::Text("Type"); - ImGui::TableNextColumn(); - ImGui::Text("Channel"); - ImGui::TableNextColumn(); - ImGui::Text("Note/Control"); - ImGui::TableNextColumn(); - ImGui::Text("Velocity/Value"); - ImGui::TableNextColumn(); - ImGui::Text("Action"); - ImGui::TableNextColumn(); - ImGui::Text("Learn"); - ImGui::TableNextColumn(); - ImGui::Text("Remove"); - - for (size_t i=0; i0 && (bind.data1+60)<180) { - nName=noteNames[bind.data1+60]; - } - snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,nName); - } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BValue1",bindID)) { - if (ImGui::Selectable("Any",bind.data1==128)) { - bind.data1=128; - } - for (int j=0; j<128; j++) { - const char* nName="???"; - if ((j+60)>0 && (j+60)<180) { - nName=noteNames[j+60]; - } - snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,nName,j); - if (ImGui::Selectable(bindID,bind.data1==j)) { - bind.data1=j; - } - } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - if (bind.data2==128) { - snprintf(bindID,1024,"Any"); - } else { - snprintf(bindID,1024,"%d (0x%.2X)",bind.data2,bind.data2); - } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BValue2",bindID)) { - if (ImGui::Selectable("Any",bind.data2==128)) { - bind.data2=128; - } - for (int j=0; j<128; j++) { - snprintf(bindID,1024,"%d (0x%.2X)##BV2_%d",j,j,j); - if (ImGui::Selectable(bindID,bind.data2==j)) { - bind.data2=j; - } - } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BAction",(bind.action==0)?"--none--":guiActions[bind.action].friendlyName)) { - if (ImGui::Selectable("--none--",bind.action==0)) { - bind.action=0; - } - for (int j=0; j3.0f) settings.dpiScale=3.0f; + } rightClickable + } + + if (ImGui::InputInt("Icon size",&settings.iconSize)) { + if (settings.iconSize<3) settings.iconSize=3; + if (settings.iconSize>48) settings.iconSize=48; + } + + // SUBSECTION TEXT + CONFIG_SUBSECTION("Text"); + ImGui::Text("Main font"); + ImGui::SameLine(); + ImGui::Combo("##MainFont",&settings.mainFont,mainFonts,7); + if (settings.mainFont==6) { + ImGui::InputText("##MainFontPath",&settings.mainFontPath); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##MainFontLoad")) { + openFileDialog(GUI_FILE_LOAD_MAIN_FONT); + } + } + if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize)) { + if (settings.mainFontSize<3) settings.mainFontSize=3; + if (settings.mainFontSize>96) settings.mainFontSize=96; + } + ImGui::Text("Header font"); + ImGui::SameLine(); + ImGui::Combo("##HeadFont",&settings.headFont,headFonts,7); + if (settings.headFont==6) { + ImGui::InputText("##HeadFontPath",&settings.headFontPath); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##HeadFontLoad")) { + openFileDialog(GUI_FILE_LOAD_HEAD_FONT); + } + } + if (ImGui::InputInt("Size##HeadFontSize",&settings.headFontSize)) { + if (settings.headFontSize<3) settings.headFontSize=3; + if (settings.headFontSize>96) settings.headFontSize=96; + } + ImGui::Text("Pattern font"); + ImGui::SameLine(); + ImGui::Combo("##PatFont",&settings.patFont,patFonts,7); + if (settings.patFont==6) { + ImGui::InputText("##PatFontPath",&settings.patFontPath); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##PatFontLoad")) { + openFileDialog(GUI_FILE_LOAD_PAT_FONT); + } + } + if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize)) { + if (settings.patFontSize<3) settings.patFontSize=3; + if (settings.patFontSize>96) settings.patFontSize=96; + } + + bool loadJapaneseB=settings.loadJapanese; + if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) { + settings.loadJapanese=loadJapaneseB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "このオプションは、十分なグラフィックメモリがある場合にのみ切り替えてください。\n" + "これは、Dear ImGuiにダイナミックフォントアトラスが実装されるまでの一時的な解決策です。" + ); + } + + bool loadChineseB=settings.loadChinese; + if (ImGui::Checkbox("Display Chinese (Simplified) characters",&loadChineseB)) { + settings.loadChinese=loadChineseB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "请在确保你有足够的显存后再启动此设定\n" + "这是一个在ImGui实现动态字体加载之前的临时解决方案" + ); + } + + bool loadChineseTraditionalB=settings.loadChineseTraditional; + if (ImGui::Checkbox("Display Chinese (Traditional) characters",&loadChineseTraditionalB)) { + settings.loadChineseTraditional=loadChineseTraditionalB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "請在確保你有足夠的顯存后再啟動此設定\n" + "這是一個在ImGui實現動態字體加載之前的臨時解決方案" + ); + } + + bool loadKoreanB=settings.loadKorean; + if (ImGui::Checkbox("Display Korean characters",&loadKoreanB)) { + settings.loadKorean=loadKoreanB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "그래픽 메모리가 충분한 경우에만 이 옵션을 선택하십시오.\n" + "이 옵션은 Dear ImGui에 동적 글꼴 아틀라스가 구현될 때까지 임시 솔루션입니다." + ); + } + + // SUBSECTION PROGRAM + CONFIG_SUBSECTION("Program"); + ImGui::Text("Title bar:"); + if (ImGui::RadioButton("Furnace##tbar0",settings.titleBarInfo==0)) { + settings.titleBarInfo=0; + updateWindowTitle(); + } + if (ImGui::RadioButton("Song Name - Furnace##tbar1",settings.titleBarInfo==1)) { + settings.titleBarInfo=1; + updateWindowTitle(); + } + if (ImGui::RadioButton("file_name.fur - Furnace##tbar2",settings.titleBarInfo==2)) { + settings.titleBarInfo=2; + updateWindowTitle(); + } + if (ImGui::RadioButton("/path/to/file.fur - Furnace##tbar3",settings.titleBarInfo==3)) { + settings.titleBarInfo=3; + updateWindowTitle(); + } + + bool titleBarSysB=settings.titleBarSys; + if (ImGui::Checkbox("Display system name on title bar",&titleBarSysB)) { + settings.titleBarSys=titleBarSysB; + updateWindowTitle(); + } + + bool noMultiSystemB=settings.noMultiSystem; + if (ImGui::Checkbox("Display chip names instead of \"multi-system\" in title bar",&noMultiSystemB)) { + settings.noMultiSystem=noMultiSystemB; + updateWindowTitle(); + } + + ImGui::Text("Status bar:"); + if (ImGui::RadioButton("Cursor details##sbar0",settings.statusDisplay==0)) { + settings.statusDisplay=0; + } + if (ImGui::RadioButton("File path##sbar1",settings.statusDisplay==1)) { + settings.statusDisplay=1; + } + if (ImGui::RadioButton("Cursor details or file path##sbar2",settings.statusDisplay==2)) { + settings.statusDisplay=2; + } + if (ImGui::RadioButton("Nothing##sbar3",settings.statusDisplay==3)) { + settings.statusDisplay=3; + } + + // SUBSECTION ORDERS + CONFIG_SUBSECTION("Orders"); + // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. + /*bool sysSeparatorsB=settings.sysSeparators; + if (ImGui::Checkbox("Add separators between systems in Orders",&sysSeparatorsB)) { + settings.sysSeparators=sysSeparatorsB; + }*/ + + bool ordersCursorB=settings.ordersCursor; + if (ImGui::Checkbox("Highlight channel at cursor in Orders",&ordersCursorB)) { + settings.ordersCursor=ordersCursorB; + } + + ImGui::Text("Orders row number format:"); + if (ImGui::RadioButton("Decimal##orbD",settings.orderRowsBase==0)) { + settings.orderRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##orbH",settings.orderRowsBase==1)) { + settings.orderRowsBase=1; + } + + // SUBSECTION PATTERN + CONFIG_SUBSECTION("Pattern"); + bool centerPatternB=settings.centerPattern; + if (ImGui::Checkbox("Center pattern view",¢erPatternB)) { + settings.centerPattern=centerPatternB; + } + + bool overflowHighlightB=settings.overflowHighlight; + if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { + settings.overflowHighlight=overflowHighlightB; + } + + bool viewPrevPatternB=settings.viewPrevPattern; + if (ImGui::Checkbox("Display previous/next pattern",&viewPrevPatternB)) { + settings.viewPrevPattern=viewPrevPatternB; + } + + ImGui::Text("Pattern row number format:"); + if (ImGui::RadioButton("Decimal##prbD",settings.patRowsBase==0)) { + settings.patRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##prbH",settings.patRowsBase==1)) { + settings.patRowsBase=1; + } + + ImGui::Text("Pattern view labels:"); + ImGui::InputTextWithHint("Note off (3-char)","OFF",&settings.noteOffLabel); + ImGui::InputTextWithHint("Note release (3-char)","===",&settings.noteRelLabel); + ImGui::InputTextWithHint("Macro release (3-char)","REL",&settings.macroRelLabel); + ImGui::InputTextWithHint("Empty field (3-char)","...",&settings.emptyLabel); + ImGui::InputTextWithHint("Empty field (2-char)","..",&settings.emptyLabel2); + + ImGui::Text("Pattern view spacing after:"); + + if (CWSliderInt("Note",&settings.noteCellSpacing,0,32)) { + if (settings.noteCellSpacing<0) settings.noteCellSpacing=0; + if (settings.noteCellSpacing>32) settings.noteCellSpacing=32; + } + + if (CWSliderInt("Instrument",&settings.insCellSpacing,0,32)) { + if (settings.insCellSpacing<0) settings.insCellSpacing=0; + if (settings.insCellSpacing>32) settings.insCellSpacing=32; + } + + if (CWSliderInt("Volume",&settings.volCellSpacing,0,32)) { + if (settings.volCellSpacing<0) settings.volCellSpacing=0; + if (settings.volCellSpacing>32) settings.volCellSpacing=32; + } + + if (CWSliderInt("Effect",&settings.effectCellSpacing,0,32)) { + if (settings.effectCellSpacing<0) settings.effectCellSpacing=0; + if (settings.effectCellSpacing>32) settings.effectCellSpacing=32; + } + + if (CWSliderInt("Effect value",&settings.effectValCellSpacing,0,32)) { + if (settings.effectValCellSpacing<0) settings.effectValCellSpacing=0; + if (settings.effectValCellSpacing>32) settings.effectValCellSpacing=32; + } + + bool oneDigitEffectsB=settings.oneDigitEffects; + if (ImGui::Checkbox("Single-digit effects for 00-0F",&oneDigitEffectsB)) { + settings.oneDigitEffects=oneDigitEffectsB; + } + + bool flatNotesB=settings.flatNotes; + if (ImGui::Checkbox("Use flats instead of sharps",&flatNotesB)) { + settings.flatNotes=flatNotesB; + } + + bool germanNotationB=settings.germanNotation; + if (ImGui::Checkbox("Use German notation",&germanNotationB)) { + settings.germanNotation=germanNotationB; + } + + // SUBSECTION CHANNEL + CONFIG_SUBSECTION("Channel"); + + ImGui::Text("Channel style:"); + if (ImGui::RadioButton("Classic##CHS0",settings.channelStyle==0)) { + settings.channelStyle=0; + } + if (ImGui::RadioButton("Line##CHS1",settings.channelStyle==1)) { + settings.channelStyle=1; + } + if (ImGui::RadioButton("Round##CHS2",settings.channelStyle==2)) { + settings.channelStyle=2; + } + if (ImGui::RadioButton("Split button##CHS3",settings.channelStyle==3)) { + settings.channelStyle=3; + } + if (ImGui::RadioButton("Square border##CH42",settings.channelStyle==4)) { + settings.channelStyle=4; + } + if (ImGui::RadioButton("Round border##CHS5",settings.channelStyle==5)) { + settings.channelStyle=5; + } + + ImGui::Text("Channel volume bar:"); + if (ImGui::RadioButton("None##CHV0",settings.channelVolStyle==0)) { + settings.channelVolStyle=0; + } + if (ImGui::RadioButton("Simple##CHV1",settings.channelVolStyle==1)) { + settings.channelVolStyle=1; + } + if (ImGui::RadioButton("Stereo##CHV2",settings.channelVolStyle==2)) { + settings.channelVolStyle=2; + } + if (ImGui::RadioButton("Real##CHV3",settings.channelVolStyle==3)) { + settings.channelVolStyle=3; + } + if (ImGui::RadioButton("Real (stereo)##CHV4",settings.channelVolStyle==4)) { + settings.channelVolStyle=4; + } + + ImGui::Text("Channel feedback style:"); + + if (ImGui::RadioButton("Off##CHF0",settings.channelFeedbackStyle==0)) { + settings.channelFeedbackStyle=0; + } + if (ImGui::RadioButton("Note##CHF1",settings.channelFeedbackStyle==1)) { + settings.channelFeedbackStyle=1; + } + if (ImGui::RadioButton("Volume##CHF2",settings.channelFeedbackStyle==2)) { + settings.channelFeedbackStyle=2; + } + if (ImGui::RadioButton("Active##CHF3",settings.channelFeedbackStyle==3)) { + settings.channelFeedbackStyle=3; + } + + ImGui::Text("Channel font:"); + + if (ImGui::RadioButton("Regular##CHFont0",settings.channelFont==0)) { + settings.channelFont=0; + } + if (ImGui::RadioButton("Monospace##CHFont1",settings.channelFont==1)) { + settings.channelFont=1; + } + + bool channelTextCenterB=settings.channelTextCenter; + if (ImGui::Checkbox("Center channel name",&channelTextCenterB)) { + settings.channelTextCenter=channelTextCenterB; + } + + ImGui::Text("Channel colors:"); + if (ImGui::RadioButton("Single##CHC0",settings.channelColors==0)) { + settings.channelColors=0; + } + if (ImGui::RadioButton("Channel type##CHC1",settings.channelColors==1)) { + settings.channelColors=1; + } + if (ImGui::RadioButton("Instrument type##CHC2",settings.channelColors==2)) { + settings.channelColors=2; + } + + ImGui::Text("Channel name colors:"); + if (ImGui::RadioButton("Single##CTC0",settings.channelTextColors==0)) { + settings.channelTextColors=0; + } + if (ImGui::RadioButton("Channel type##CTC1",settings.channelTextColors==1)) { + settings.channelTextColors=1; + } + if (ImGui::RadioButton("Instrument type##CTC2",settings.channelTextColors==2)) { + settings.channelTextColors=2; + } + + // SUBSECTION ASSETS + CONFIG_SUBSECTION("Assets"); + bool unifiedDataViewB=settings.unifiedDataView; + if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { + settings.unifiedDataView=unifiedDataViewB; + } + if (settings.unifiedDataView) { + settings.horizontalDataView=0; + } + + ImGui::BeginDisabled(settings.unifiedDataView); + bool horizontalDataViewB=settings.horizontalDataView; + if (ImGui::Checkbox("Horizontal instrument list",&horizontalDataViewB)) { + settings.horizontalDataView=horizontalDataViewB; + } + ImGui::EndDisabled(); + + bool insEditColorizeB=settings.insEditColorize; + if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) { + settings.insEditColorize=insEditColorizeB; + } + + // SUBSECTION MACRO EDITOR + CONFIG_SUBSECTION("Macro Editor"); + ImGui::Text("Macro editor layout:"); + if (ImGui::RadioButton("Unified##mel0",settings.macroLayout==0)) { + settings.macroLayout=0; + } + if (ImGui::RadioButton("Mobile##mel1",settings.macroLayout==1)) { + settings.macroLayout=1; + } + if (ImGui::RadioButton("Grid##mel2",settings.macroLayout==2)) { + settings.macroLayout=2; + } + if (ImGui::RadioButton("Single (with list)##mel3",settings.macroLayout==3)) { + settings.macroLayout=3; + } + if (ImGui::RadioButton("Single (combo box)##mel4",settings.macroLayout==4)) { + settings.macroLayout=4; + } + + bool oldMacroVSliderB=settings.oldMacroVSlider; + if (ImGui::Checkbox("Use classic macro editor vertical slider",&oldMacroVSliderB)) { + settings.oldMacroVSlider=oldMacroVSliderB; + } + + // SUBSECTION WAVE EDITOR + CONFIG_SUBSECTION("Wave Editor"); + bool waveLayoutB=settings.waveLayout; + if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { + settings.waveLayout=waveLayoutB; + } + + // SUBSECTION FM EDITOR + CONFIG_SUBSECTION("FM Editor"); + ImGui::Text("FM parameter names:"); + if (ImGui::RadioButton("Friendly##fmn0",settings.fmNames==0)) { + settings.fmNames=0; + } + if (ImGui::RadioButton("Technical##fmn1",settings.fmNames==1)) { + settings.fmNames=1; + } + if (ImGui::RadioButton("Technical (alternate)##fmn2",settings.fmNames==2)) { + settings.fmNames=2; + } + + bool oplStandardWaveNamesB=settings.oplStandardWaveNames; + if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { + settings.oplStandardWaveNames=oplStandardWaveNamesB; + } + + ImGui::Text("FM parameter editor layout:"); + if (ImGui::RadioButton("Modern##fml0",settings.fmLayout==0)) { + settings.fmLayout=0; + } + if (ImGui::RadioButton("Compact (2x2, classic)##fml1",settings.fmLayout==1)) { + settings.fmLayout=1; + } + if (ImGui::RadioButton("Compact (1x4)##fml2",settings.fmLayout==2)) { + settings.fmLayout=2; + } + if (ImGui::RadioButton("Compact (4x1)##fml3",settings.fmLayout==3)) { + settings.fmLayout=3; + } + if (ImGui::RadioButton("Alternate (2x2)##fml4",settings.fmLayout==4)) { + settings.fmLayout=4; + } + if (ImGui::RadioButton("Alternate (1x4)##fml5",settings.fmLayout==5)) { + settings.fmLayout=5; + } + if (ImGui::RadioButton("Alternate (4x1)##fml5",settings.fmLayout==6)) { + settings.fmLayout=6; + } + + ImGui::Text("Position of Sustain in FM editor:"); + if (ImGui::RadioButton("Between Decay and Sustain Rate##susp0",settings.susPosition==0)) { + settings.susPosition=0; + } + if (ImGui::RadioButton("After Release Rate##susp1",settings.susPosition==1)) { + settings.susPosition=1; + } + + bool separateFMColorsB=settings.separateFMColors; + if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { + settings.separateFMColors=separateFMColorsB; + } + + bool unsignedDetuneB=settings.unsignedDetune; + if (ImGui::Checkbox("Unsigned FM detune values",&unsignedDetuneB)) { + settings.unsignedDetune=unsignedDetuneB; + } + + // SUBSECTION STATISTICS + CONFIG_SUBSECTION("Statistics"); + ImGui::Text("Chip memory usage unit:"); + if (ImGui::RadioButton("Bytes##MUU0",settings.memUsageUnit==0)) { + settings.memUsageUnit=0; + } + if (ImGui::RadioButton("Kilobytes##MUU1",settings.memUsageUnit==1)) { + settings.memUsageUnit=1; + } + + // SUBSECTION OSCILLOSCOPE + CONFIG_SUBSECTION("Oscilloscope"); + bool oscRoundedCornersB=settings.oscRoundedCorners; + if (ImGui::Checkbox("Rounded corners",&oscRoundedCornersB)) { + settings.oscRoundedCorners=oscRoundedCornersB; + } + + bool oscBorderB=settings.oscBorder; + if (ImGui::Checkbox("Border",&oscBorderB)) { + settings.oscBorder=oscBorderB; + } + + bool oscTakesEntireWindowB=settings.oscTakesEntireWindow; + if (ImGui::Checkbox("Fill entire window",&oscTakesEntireWindowB)) { + settings.oscTakesEntireWindow=oscTakesEntireWindowB; + } + + bool oscEscapesBoundaryB=settings.oscEscapesBoundary; + if (ImGui::Checkbox("Waveform goes out of bounds",&oscEscapesBoundaryB)) { + settings.oscEscapesBoundary=oscEscapesBoundaryB; + } + + // SUBSECTION WINDOWS + CONFIG_SUBSECTION("Windows"); + bool roundedWindowsB=settings.roundedWindows; + if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { + settings.roundedWindows=roundedWindowsB; + } + + bool roundedButtonsB=settings.roundedButtons; + if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) { + settings.roundedButtons=roundedButtonsB; + } + + bool roundedMenusB=settings.roundedMenus; + if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) { + settings.roundedMenus=roundedMenusB; + } + + bool frameBordersB=settings.frameBorders; + if (ImGui::Checkbox("Borders around widgets",&frameBordersB)) { + settings.frameBorders=frameBordersB; + } + + END_SECTION; } - if (ImGui::BeginTabItem("Emulation")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { - ImGui::Text("Arcade/YM2151 core"); - ImGui::SameLine(); - ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2); - - ImGui::Text("Genesis/YM2612 core"); - ImGui::SameLine(); - ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2); - - ImGui::Text("SN76489 core"); - ImGui::SameLine(); - ImGui::Combo("##SNCore",&settings.snCore,snCores,2); - - ImGui::Text("NES core"); - ImGui::SameLine(); - ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); - - ImGui::Text("FDS core"); - ImGui::SameLine(); - ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); - - ImGui::Text("SID core"); - ImGui::SameLine(); - ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,3); - - ImGui::Text("POKEY core"); - ImGui::SameLine(); - ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2); - - ImGui::Text("OPN/OPNA/OPNB cores"); - ImGui::SameLine(); - ImGui::Combo("##OPNCore",&settings.opnCore,opnCores,2); - - ImGui::Separator(); - - ImGui::Text("PC Speaker strategy"); - ImGui::SameLine(); - ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); - - ImGui::Separator(); - ImGui::Text("Sample ROMs:"); - - ImGui::Text("OPL4 YRW801 path"); - ImGui::SameLine(); - ImGui::InputText("##YRW801Path",&settings.yrw801Path); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##YRW801Load")) { - openFileDialog(GUI_FILE_YRW801_ROM_OPEN); - } - - ImGui::Text("MultiPCM TG100 path"); - ImGui::SameLine(); - ImGui::InputText("##TG100Path",&settings.tg100Path); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##TG100Load")) { - openFileDialog(GUI_FILE_TG100_ROM_OPEN); - } - - ImGui::Text("MultiPCM MU5 path"); - ImGui::SameLine(); - ImGui::InputText("##MU5Path",&settings.mu5Path); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) { - openFileDialog(GUI_FILE_MU5_ROM_OPEN); - } + CONFIG_SECTION("Color") { + // SUBSECTION COLOR SCHEME + CONFIG_SUBSECTION("Color scheme"); + if (ImGui::Button("Import")) { + openFileDialog(GUI_FILE_IMPORT_COLORS); } - ImGui::EndChild(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Appearance")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { - String curRenderBackend=settings.renderBackend.empty()?GUI_BACKEND_DEFAULT_NAME:settings.renderBackend; - if (ImGui::BeginCombo("Render backend",curRenderBackend.c_str())) { -#ifdef HAVE_RENDER_SDL - if (ImGui::Selectable("SDL Renderer",curRenderBackend=="SDL")) { - settings.renderBackend="SDL"; - } -#endif -#ifdef HAVE_RENDER_DX11 - if (ImGui::Selectable("DirectX 11",curRenderBackend=="DirectX 11")) { - settings.renderBackend="DirectX 11"; - } -#endif -#ifdef HAVE_RENDER_GL - if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) { - settings.renderBackend="OpenGL"; - } -#endif - ImGui::EndCombo(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); - } - if (curRenderBackend=="SDL") { - if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) { - if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) { - settings.renderDriver=""; - } - for (String& i: availRenderDrivers) { - if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) { - settings.renderDriver=i; - } - } - ImGui::EndCombo(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); - } - } - - bool dpiScaleAuto=(settings.dpiScale<0.5f); - if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) { - if (dpiScaleAuto) { - settings.dpiScale=0.0f; - } else { - settings.dpiScale=1.0f; - } - } - if (!dpiScaleAuto) { - if (ImGui::SliderFloat("UI scaling factor",&settings.dpiScale,1.0f,3.0f,"%.2fx")) { - if (settings.dpiScale<0.5f) settings.dpiScale=0.5f; - if (settings.dpiScale>3.0f) settings.dpiScale=3.0f; - } rightClickable - } - ImGui::Text("Main font"); - ImGui::SameLine(); - ImGui::Combo("##MainFont",&settings.mainFont,mainFonts,7); - if (settings.mainFont==6) { - ImGui::InputText("##MainFontPath",&settings.mainFontPath); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##MainFontLoad")) { - openFileDialog(GUI_FILE_LOAD_MAIN_FONT); - } - } - if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize)) { - if (settings.mainFontSize<3) settings.mainFontSize=3; - if (settings.mainFontSize>96) settings.mainFontSize=96; - } - ImGui::Text("Pattern font"); - ImGui::SameLine(); - ImGui::Combo("##PatFont",&settings.patFont,patFonts,7); - if (settings.patFont==6) { - ImGui::InputText("##PatFontPath",&settings.patFontPath); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##PatFontLoad")) { - openFileDialog(GUI_FILE_LOAD_PAT_FONT); - } - } - if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize)) { - if (settings.patFontSize<3) settings.patFontSize=3; - if (settings.patFontSize>96) settings.patFontSize=96; - } - if (ImGui::InputInt("Icon size",&settings.iconSize)) { - if (settings.iconSize<3) settings.iconSize=3; - if (settings.iconSize>48) settings.iconSize=48; - } - - bool loadJapaneseB=settings.loadJapanese; - if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) { - settings.loadJapanese=loadJapaneseB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Only toggle this option if you have enough graphics memory.\n" - "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" - "このオプションは、十分なグラフィックメモリがある場合にのみ切り替えてください。\n" - "これは、Dear ImGuiにダイナミックフォントアトラスが実装されるまでの一時的な解決策です。" - ); - } - - bool loadChineseB=settings.loadChinese; - if (ImGui::Checkbox("Display Chinese (Simplified) characters",&loadChineseB)) { - settings.loadChinese=loadChineseB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Only toggle this option if you have enough graphics memory.\n" - "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" - "请在确保你有足够的显存后再启动此设定\n" - "这是一个在ImGui实现动态字体加载之前的临时解决方案" - ); - } - - bool loadChineseTraditionalB=settings.loadChineseTraditional; - if (ImGui::Checkbox("Display Chinese (Traditional) characters",&loadChineseTraditionalB)) { - settings.loadChineseTraditional=loadChineseTraditionalB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Only toggle this option if you have enough graphics memory.\n" - "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" - "請在確保你有足夠的顯存后再啟動此設定\n" - "這是一個在ImGui實現動態字體加載之前的臨時解決方案" - ); - } - - bool loadKoreanB=settings.loadKorean; - if (ImGui::Checkbox("Display Korean characters",&loadKoreanB)) { - settings.loadKorean=loadKoreanB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Only toggle this option if you have enough graphics memory.\n" - "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" - "그래픽 메모리가 충분한 경우에만 이 옵션을 선택하십시오.\n" - "이 옵션은 Dear ImGui에 동적 글꼴 아틀라스가 구현될 때까지 임시 솔루션입니다." - ); - } - - ImGui::Separator(); - - if (ImGui::InputInt("Number of recent files",&settings.maxRecentFile)) { - if (settings.maxRecentFile<0) settings.maxRecentFile=0; - if (settings.maxRecentFile>30) settings.maxRecentFile=30; - } - - ImGui::Separator(); - - ImGui::Text("Pattern view labels:"); - ImGui::InputTextWithHint("Note off (3-char)","OFF",&settings.noteOffLabel); - ImGui::InputTextWithHint("Note release (3-char)","===",&settings.noteRelLabel); - ImGui::InputTextWithHint("Macro release (3-char)","REL",&settings.macroRelLabel); - ImGui::InputTextWithHint("Empty field (3-char)","...",&settings.emptyLabel); - ImGui::InputTextWithHint("Empty field (2-char)","..",&settings.emptyLabel2); - - ImGui::Separator(); - - ImGui::Text("Orders row number format:"); - if (ImGui::RadioButton("Decimal##orbD",settings.orderRowsBase==0)) { - settings.orderRowsBase=0; - } - if (ImGui::RadioButton("Hexadecimal##orbH",settings.orderRowsBase==1)) { - settings.orderRowsBase=1; - } - - ImGui::Text("Pattern row number format:"); - if (ImGui::RadioButton("Decimal##prbD",settings.patRowsBase==0)) { - settings.patRowsBase=0; - } - if (ImGui::RadioButton("Hexadecimal##prbH",settings.patRowsBase==1)) { - settings.patRowsBase=1; - } - - ImGui::Text("FM parameter names:"); - if (ImGui::RadioButton("Friendly##fmn0",settings.fmNames==0)) { - settings.fmNames=0; - } - if (ImGui::RadioButton("Technical##fmn1",settings.fmNames==1)) { - settings.fmNames=1; - } - if (ImGui::RadioButton("Technical (alternate)##fmn2",settings.fmNames==2)) { - settings.fmNames=2; - } - - ImGui::Separator(); - - ImGui::Text("Title bar:"); - if (ImGui::RadioButton("Furnace##tbar0",settings.titleBarInfo==0)) { - settings.titleBarInfo=0; - updateWindowTitle(); - } - if (ImGui::RadioButton("Song Name - Furnace##tbar1",settings.titleBarInfo==1)) { - settings.titleBarInfo=1; - updateWindowTitle(); - } - if (ImGui::RadioButton("file_name.fur - Furnace##tbar2",settings.titleBarInfo==2)) { - settings.titleBarInfo=2; - updateWindowTitle(); - } - if (ImGui::RadioButton("/path/to/file.fur - Furnace##tbar3",settings.titleBarInfo==3)) { - settings.titleBarInfo=3; - updateWindowTitle(); - } - - bool titleBarSysB=settings.titleBarSys; - if (ImGui::Checkbox("Display system name on title bar",&titleBarSysB)) { - settings.titleBarSys=titleBarSysB; - updateWindowTitle(); - } - - bool noMultiSystemB=settings.noMultiSystem; - if (ImGui::Checkbox("Display chip names instead of \"multi-system\" in title bar",&noMultiSystemB)) { - settings.noMultiSystem=noMultiSystemB; - updateWindowTitle(); - } - - ImGui::Text("Status bar:"); - if (ImGui::RadioButton("Cursor details##sbar0",settings.statusDisplay==0)) { - settings.statusDisplay=0; - } - if (ImGui::RadioButton("File path##sbar1",settings.statusDisplay==1)) { - settings.statusDisplay=1; - } - if (ImGui::RadioButton("Cursor details or file path##sbar2",settings.statusDisplay==2)) { - settings.statusDisplay=2; - } - if (ImGui::RadioButton("Nothing##sbar3",settings.statusDisplay==3)) { - settings.statusDisplay=3; - } - - ImGui::Text("Play/edit controls layout:"); - if (ImGui::RadioButton("Classic##ecl0",settings.controlLayout==0)) { - settings.controlLayout=0; - } - if (ImGui::RadioButton("Compact##ecl1",settings.controlLayout==1)) { - settings.controlLayout=1; - } - if (ImGui::RadioButton("Compact (vertical)##ecl2",settings.controlLayout==2)) { - settings.controlLayout=2; - } - if (ImGui::RadioButton("Split##ecl3",settings.controlLayout==3)) { - settings.controlLayout=3; - } - - ImGui::Text("Position of buttons in Orders:"); - if (ImGui::RadioButton("Top##obp0",settings.orderButtonPos==0)) { - settings.orderButtonPos=0; - } - if (ImGui::RadioButton("Left##obp1",settings.orderButtonPos==1)) { - settings.orderButtonPos=1; - } - if (ImGui::RadioButton("Right##obp2",settings.orderButtonPos==2)) { - settings.orderButtonPos=2; - } - - ImGui::Text("FM parameter editor layout:"); - if (ImGui::RadioButton("Modern##fml0",settings.fmLayout==0)) { - settings.fmLayout=0; - } - if (ImGui::RadioButton("Compact (2x2, classic)##fml1",settings.fmLayout==1)) { - settings.fmLayout=1; - } - if (ImGui::RadioButton("Compact (1x4)##fml2",settings.fmLayout==2)) { - settings.fmLayout=2; - } - if (ImGui::RadioButton("Compact (4x1)##fml3",settings.fmLayout==3)) { - settings.fmLayout=3; - } - if (ImGui::RadioButton("Alternate (2x2)##fml4",settings.fmLayout==4)) { - settings.fmLayout=4; - } - if (ImGui::RadioButton("Alternate (1x4)##fml5",settings.fmLayout==5)) { - settings.fmLayout=5; - } - if (ImGui::RadioButton("Alternate (4x1)##fml5",settings.fmLayout==6)) { - settings.fmLayout=6; - } - - ImGui::Text("Position of Sustain in FM editor:"); - if (ImGui::RadioButton("Between Decay and Sustain Rate##susp0",settings.susPosition==0)) { - settings.susPosition=0; - } - if (ImGui::RadioButton("After Release Rate##susp1",settings.susPosition==1)) { - settings.susPosition=1; - } - - ImGui::Text("Macro editor layout:"); - if (ImGui::RadioButton("Unified##mel0",settings.macroLayout==0)) { - settings.macroLayout=0; - } - if (ImGui::RadioButton("Mobile##mel1",settings.macroLayout==1)) { - settings.macroLayout=1; - } - if (ImGui::RadioButton("Grid##mel2",settings.macroLayout==2)) { - settings.macroLayout=2; - } - if (ImGui::RadioButton("Single (with list)##mel3",settings.macroLayout==3)) { - settings.macroLayout=3; - } - if (ImGui::RadioButton("Single (combo box)##mel4",settings.macroLayout==4)) { - settings.macroLayout=4; - } - - ImGui::Separator(); - - ImGui::Text("Chip memory usage unit:"); - if (ImGui::RadioButton("Bytes##MUU0",settings.memUsageUnit==0)) { - settings.memUsageUnit=0; - } - if (ImGui::RadioButton("Kilobytes##MUU1",settings.memUsageUnit==1)) { - settings.memUsageUnit=1; - } - - ImGui::Separator(); - - ImGui::Text("Channel colors:"); - if (ImGui::RadioButton("Single##CHC0",settings.channelColors==0)) { - settings.channelColors=0; - } - if (ImGui::RadioButton("Channel type##CHC1",settings.channelColors==1)) { - settings.channelColors=1; - } - if (ImGui::RadioButton("Instrument type##CHC2",settings.channelColors==2)) { - settings.channelColors=2; - } - - ImGui::Text("Channel name colors:"); - if (ImGui::RadioButton("Single##CTC0",settings.channelTextColors==0)) { - settings.channelTextColors=0; - } - if (ImGui::RadioButton("Channel type##CTC1",settings.channelTextColors==1)) { - settings.channelTextColors=1; - } - if (ImGui::RadioButton("Instrument type##CTC2",settings.channelTextColors==2)) { - settings.channelTextColors=2; - } - - ImGui::Text("Channel style:"); - if (ImGui::RadioButton("Classic##CHS0",settings.channelStyle==0)) { - settings.channelStyle=0; - } - if (ImGui::RadioButton("Line##CHS1",settings.channelStyle==1)) { - settings.channelStyle=1; - } - if (ImGui::RadioButton("Round##CHS2",settings.channelStyle==2)) { - settings.channelStyle=2; - } - if (ImGui::RadioButton("Split button##CHS3",settings.channelStyle==3)) { - settings.channelStyle=3; - } - if (ImGui::RadioButton("Square border##CH42",settings.channelStyle==4)) { - settings.channelStyle=4; - } - if (ImGui::RadioButton("Round border##CHS5",settings.channelStyle==5)) { - settings.channelStyle=5; - } - - ImGui::Text("Channel volume bar:"); - if (ImGui::RadioButton("None##CHV0",settings.channelVolStyle==0)) { - settings.channelVolStyle=0; - } - if (ImGui::RadioButton("Simple##CHV1",settings.channelVolStyle==1)) { - settings.channelVolStyle=1; - } - if (ImGui::RadioButton("Stereo##CHV2",settings.channelVolStyle==2)) { - settings.channelVolStyle=2; - } - if (ImGui::RadioButton("Real##CHV3",settings.channelVolStyle==3)) { - settings.channelVolStyle=3; - } - if (ImGui::RadioButton("Real (stereo)##CHV4",settings.channelVolStyle==4)) { - settings.channelVolStyle=4; - } - - ImGui::Text("Channel feedback style:"); - - if (ImGui::RadioButton("Off##CHF0",settings.channelFeedbackStyle==0)) { - settings.channelFeedbackStyle=0; - } - if (ImGui::RadioButton("Note##CHF1",settings.channelFeedbackStyle==1)) { - settings.channelFeedbackStyle=1; - } - if (ImGui::RadioButton("Volume##CHF2",settings.channelFeedbackStyle==2)) { - settings.channelFeedbackStyle=2; - } - if (ImGui::RadioButton("Active##CHF3",settings.channelFeedbackStyle==3)) { - settings.channelFeedbackStyle=3; - } - - ImGui::Text("Channel font:"); - - if (ImGui::RadioButton("Regular##CHFont0",settings.channelFont==0)) { - settings.channelFont=0; - } - if (ImGui::RadioButton("Monospace##CHFont1",settings.channelFont==1)) { - settings.channelFont=1; - } - - bool channelTextCenterB=settings.channelTextCenter; - if (ImGui::Checkbox("Center channel name",&channelTextCenterB)) { - settings.channelTextCenter=channelTextCenterB; - } - - ImGui::Separator(); - - bool insEditColorizeB=settings.insEditColorize; - if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) { - settings.insEditColorize=insEditColorizeB; - } - - bool separateFMColorsB=settings.separateFMColors; - if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { - settings.separateFMColors=separateFMColorsB; - } - - bool unifiedDataViewB=settings.unifiedDataView; - if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { - settings.unifiedDataView=unifiedDataViewB; - } - if (settings.unifiedDataView) { - settings.horizontalDataView=0; - } - - ImGui::BeginDisabled(settings.unifiedDataView); - bool horizontalDataViewB=settings.horizontalDataView; - if (ImGui::Checkbox("Horizontal instrument list",&horizontalDataViewB)) { - settings.horizontalDataView=horizontalDataViewB; - } - ImGui::EndDisabled(); - - bool oplStandardWaveNamesB=settings.oplStandardWaveNames; - if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { - settings.oplStandardWaveNames=oplStandardWaveNamesB; - } - - bool overflowHighlightB=settings.overflowHighlight; - if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { - settings.overflowHighlight=overflowHighlightB; - } - - bool viewPrevPatternB=settings.viewPrevPattern; - if (ImGui::Checkbox("Display previous/next pattern",&viewPrevPatternB)) { - settings.viewPrevPattern=viewPrevPatternB; - } - - bool flatNotesB=settings.flatNotes; - if (ImGui::Checkbox("Use flats instead of sharps",&flatNotesB)) { - settings.flatNotes=flatNotesB; - } - - bool germanNotationB=settings.germanNotation; - if (ImGui::Checkbox("Use German notation",&germanNotationB)) { - settings.germanNotation=germanNotationB; - } - - bool oneDigitEffectsB=settings.oneDigitEffects; - if (ImGui::Checkbox("Single-digit effects for 00-0F",&oneDigitEffectsB)) { - settings.oneDigitEffects=oneDigitEffectsB; - } - - bool centerPatternB=settings.centerPattern; - if (ImGui::Checkbox("Center pattern view",¢erPatternB)) { - settings.centerPattern=centerPatternB; - } - - bool unsignedDetuneB=settings.unsignedDetune; - if (ImGui::Checkbox("Unsigned FM detune values",&unsignedDetuneB)) { - settings.unsignedDetune=unsignedDetuneB; - } - - // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. - /*bool sysSeparatorsB=settings.sysSeparators; - if (ImGui::Checkbox("Add separators between systems in Orders",&sysSeparatorsB)) { - settings.sysSeparators=sysSeparatorsB; - }*/ - - bool ordersCursorB=settings.ordersCursor; - if (ImGui::Checkbox("Highlight channel at cursor in Orders",&ordersCursorB)) { - settings.ordersCursor=ordersCursorB; - } - - bool partyTimeB=settings.partyTime; - if (ImGui::Checkbox("About screen party time",&partyTimeB)) { - settings.partyTime=partyTimeB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Warning: may cause epileptic seizures."); - } - - ImGui::Separator(); - - bool waveLayoutB=settings.waveLayout; - if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { - settings.waveLayout=waveLayoutB; - } - - bool oldMacroVSliderB=settings.oldMacroVSlider; - if (ImGui::Checkbox("Use classic macro editor vertical slider",&oldMacroVSliderB)) { - settings.oldMacroVSlider=oldMacroVSliderB; - } - - bool roundedWindowsB=settings.roundedWindows; - if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { - settings.roundedWindows=roundedWindowsB; - } - - bool roundedButtonsB=settings.roundedButtons; - if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) { - settings.roundedButtons=roundedButtonsB; - } - - bool roundedMenusB=settings.roundedMenus; - if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) { - settings.roundedMenus=roundedMenusB; - } - - bool frameBordersB=settings.frameBorders; - if (ImGui::Checkbox("Borders around widgets",&frameBordersB)) { - settings.frameBorders=frameBordersB; - } - - bool disableFadeInB=settings.disableFadeIn; - if (ImGui::Checkbox("Disable fade-in during start-up",&disableFadeInB)) { - settings.disableFadeIn=disableFadeInB; - } - - ImGui::Separator(); - - ImGui::Text("Oscilloscope settings:"); - - bool oscRoundedCornersB=settings.oscRoundedCorners; - if (ImGui::Checkbox("Rounded corners",&oscRoundedCornersB)) { - settings.oscRoundedCorners=oscRoundedCornersB; - } - - bool oscTakesEntireWindowB=settings.oscTakesEntireWindow; - if (ImGui::Checkbox("Fill entire window",&oscTakesEntireWindowB)) { - settings.oscTakesEntireWindow=oscTakesEntireWindowB; - } - - bool oscEscapesBoundaryB=settings.oscEscapesBoundary; - if (ImGui::Checkbox("Waveform goes out of bounds",&oscEscapesBoundaryB)) { - settings.oscEscapesBoundary=oscEscapesBoundaryB; - } - - bool oscBorderB=settings.oscBorder; - if (ImGui::Checkbox("Border",&oscBorderB)) { - settings.oscBorder=oscBorderB; - } - - ImGui::Separator(); - - ImGui::Text("Pattern view spacing after:"); - - if (CWSliderInt("Note",&settings.noteCellSpacing,0,32)) { - if (settings.noteCellSpacing<0) settings.noteCellSpacing=0; - if (settings.noteCellSpacing>32) settings.noteCellSpacing=32; - } - - if (CWSliderInt("Instrument",&settings.insCellSpacing,0,32)) { - if (settings.insCellSpacing<0) settings.insCellSpacing=0; - if (settings.insCellSpacing>32) settings.insCellSpacing=32; - } - - if (CWSliderInt("Volume",&settings.volCellSpacing,0,32)) { - if (settings.volCellSpacing<0) settings.volCellSpacing=0; - if (settings.volCellSpacing>32) settings.volCellSpacing=32; - } - - if (CWSliderInt("Effect",&settings.effectCellSpacing,0,32)) { - if (settings.effectCellSpacing<0) settings.effectCellSpacing=0; - if (settings.effectCellSpacing>32) settings.effectCellSpacing=32; - } - - if (CWSliderInt("Effect value",&settings.effectValCellSpacing,0,32)) { - if (settings.effectValCellSpacing<0) settings.effectValCellSpacing=0; - if (settings.effectValCellSpacing>32) settings.effectValCellSpacing=32; - } - - ImGui::Separator(); - - if (ImGui::TreeNode("Color scheme")) { - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_COLORS); - } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_COLORS); - } - ImGui::SameLine(); - 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:"); - if (ImGui::RadioButton("Dark##gcb0",settings.guiColorsBase==0)) { - settings.guiColorsBase=0; - applyUISettings(false); - } - if (ImGui::RadioButton("Light##gcb1",settings.guiColorsBase==1)) { - settings.guiColorsBase=1; - applyUISettings(false); - } - 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); - } - 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"); - UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_POPUP,"Pop-up background"); - 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_TITLE_INACTIVE,"Title bar (inactive)"); - UI_COLOR_CONFIG(GUI_COLOR_TITLE_COLLAPSED,"Title bar (collapsed)"); - UI_COLOR_CONFIG(GUI_COLOR_MENU_BAR,"Menu bar"); - UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border"); - UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow"); - UI_COLOR_CONFIG(GUI_COLOR_SCROLL,"Scroll bar"); - UI_COLOR_CONFIG(GUI_COLOR_SCROLL_HOVER,"Scroll bar (hovered)"); - UI_COLOR_CONFIG(GUI_COLOR_SCROLL_ACTIVE,"Scroll bar (clicked)"); - UI_COLOR_CONFIG(GUI_COLOR_SCROLL_BACKGROUND,"Scroll bar background"); - UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR,"Separator"); - UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_HOVER,"Separator (hover)"); - UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_ACTIVE,"Separator (active)"); - UI_COLOR_CONFIG(GUI_COLOR_DOCKING_PREVIEW,"Docking preview"); - UI_COLOR_CONFIG(GUI_COLOR_DOCKING_EMPTY,"Docking empty"); - UI_COLOR_CONFIG(GUI_COLOR_TABLE_HEADER,"Table header"); - UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_HARD,"Table border (hard)"); - UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_SOFT,"Table border (soft)"); - 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"); - 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_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"); - UI_COLOR_CONFIG(GUI_COLOR_ERROR,"Error hint"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("File Picker (built-in)")) { - UI_COLOR_CONFIG(GUI_COLOR_FILE_DIR,"Directory"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_NATIVE,"Song (native)"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_IMPORT,"Song (import)"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_INSTR,"Instrument"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_AUDIO,"Audio"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_WAVE,"Wavetable"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_VGM,"VGM"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_ZSM,"ZSM"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_FONT,"Font"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_OTHER,"Other"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Oscilloscope")) { - UI_COLOR_CONFIG(GUI_COLOR_OSC_BORDER,"Border"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG1,"Background (top-left)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG2,"Background (top-right)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG3,"Background (bottom-left)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG4,"Background (bottom-right)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE,"Waveform"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_PEAK,"Waveform (clip)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_REF,"Reference"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_GUIDE,"Guide"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Volume Meter")) { - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_LOW,"Low"); - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_HIGH,"High"); - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_PEAK,"Clip"); - ImGui::TreePop(); - } - 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_ORDER_SELECTED,"Selected order"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_SIMILAR,"Similar patterns"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Envelope View")) { - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("FM Editor")) { - UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); - UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); - UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); - - UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); - UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); - - ImGui::TextWrapped("(the following colors only apply when \"Use separate colors for carriers/modulators in FM editor\" is on!)"); - - UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_MOD,"Mod. accent (primary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); - - UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); - UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Macro Editor")) { - UI_COLOR_CONFIG(GUI_COLOR_MACRO_VOLUME,"Volume"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_PITCH,"Pitch"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_WAVE,"Wave"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_OTHER,"Other"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Instrument Types")) { - UI_COLOR_CONFIG(GUI_COLOR_INSTR_FM,"FM (OPN)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_STD,"SN76489/Sega PSG"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_T6W28,"T6W28"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_GB,"Game Boy"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_C64,"C64"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AMIGA,"Amiga/Generic Sample"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_PCE,"PC Engine"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY,"AY-3-8910/SSG"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY8930,"AY8930"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_TIA,"TIA"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SAA1099,"SAA1099"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VIC,"VIC"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_PET,"PET"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6,"VRC6"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6_SAW,"VRC6 (saw)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPLL,"FM (OPLL)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL,"FM (OPL)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_FDS,"FDS"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VBOY,"Virtual Boy"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_N163,"Namco 163"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SCC,"Konami SCC"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPZ,"FM (OPZ)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_NAMCO,"Namco WSG"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL_DRUMS,"FM (OPL Drums)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPM,"FM (OPM)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_NES,"NES"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM6258,"MSM6258"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM6295,"MSM6295"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_ADPCMA,"ADPCM-A"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_ADPCMB,"ADPCM-B"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SEGAPCM,"Sega PCM"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_QSOUND,"QSound"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_YMZ280B,"YMZ280B"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_RF5C68,"RF5C68"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM5232,"MSM5232"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_K007232,"K007232"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_GA20,"GA20"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Channel")) { - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_BG,"Single color (background)"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FG,"Single color (text)"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PCM,"PCM"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_WAVE,"Wave"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_OP,"FM operator"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_MUTED,"Muted"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Pattern")) { - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_PLAY_HEAD,"Playhead"); - 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)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION,"Selection"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_HOVER,"Selection (hovered)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_ACTIVE,"Selection (clicked)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_1,"Highlight 1"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_2,"Highlight 2"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX,"Row number"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI1,"Row number (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI2,"Row number (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE,"Note"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI1,"Note (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI2,"Note (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE,"Blank"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI1,"Blank (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI2,"Blank (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS,"Instrument"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_WARN,"Instrument (invalid type)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_ERROR,"Instrument (out of range)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MIN,"Volume (0%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_HALF,"Volume (50%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MAX,"Volume (100%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_INVALID,"Invalid effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PITCH,"Pitch effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_VOLUME,"Volume effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PANNING,"Panning effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SONG,"Song effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_TIME,"Time effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SPEED,"Speed effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,"Primary specific effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"Secondary specific effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_MISC,"Miscellaneous"); - UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Sample Editor")) { - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_BG,"Background"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_FG,"Waveform"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_BG,"Time background"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_FG,"Time text"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP,"Loop region"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CENTER,"Center guide"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_GRID,"Grid"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL,"Selection"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL_POINT,"Selection points"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE,"Preview needle"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE_PLAYING,"Playing needles"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP_POINT,"Loop markers"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_DISABLED,"Chip select: disabled"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_ENABLED,"Chip select: enabled"); - UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_WARNING,"Chip select: enabled (failure)"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Pattern Manager")) { - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_NULL,"Unallocated"); - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_UNUSED,"Unused"); - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_USED,"Used"); - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_OVERUSED,"Overused"); - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED,"Really overused"); - UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_COMBO_BREAKER,"Combo Breaker"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Piano")) { - UI_COLOR_CONFIG(GUI_COLOR_PIANO_BACKGROUND,"Background"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP,"Upper key"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP_HIT,"Upper key (feedback)"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"Upper key (pressed)"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM,"Lower key"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM_HIT,"Lower key (feedback)"); - UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"Lower key (pressed)"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Clock")) { - UI_COLOR_CONFIG(GUI_COLOR_CLOCK_TEXT,"Clock text"); - UI_COLOR_CONFIG(GUI_COLOR_CLOCK_BEAT_LOW,"Beat (off)"); - UI_COLOR_CONFIG(GUI_COLOR_CLOCK_BEAT_HIGH,"Beat (on)"); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Patchbay")) { - UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORTSET,"PortSet"); - UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORT,"Port"); - UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORT_HIDDEN,"Port (hidden/unavailable)"); - UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_CONNECTION,"Connection (selected)"); - UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_CONNECTION_BG,"Connection (other)"); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Log Viewer")) { - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_ERROR,"Log level: Error"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_WARNING,"Log level: Warning"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_INFO,"Log level: Info"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_DEBUG,"Log level: Debug"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_TRACE,"Log level: Trace/Verbose"); - ImGui::TreePop(); - } - ImGui::TreePop(); - } + ImGui::SameLine(); + if (ImGui::Button("Export")) { + openFileDialog(GUI_FILE_EXPORT_COLORS); } - ImGui::EndChild(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Keyboard")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_KEYBINDS); - } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_KEYBINDS); - } - ImGui::SameLine(); - if (ImGui::Button("Reset defaults")) { - showWarning("Are you sure you want to reset the keyboard settings?",GUI_WARN_RESET_KEYBINDS); - } - if (ImGui::TreeNode("Global hotkeys")) { - KEYBIND_CONFIG_BEGIN("keysGlobal"); - - UI_KEYBIND_CONFIG(GUI_ACTION_NEW); - UI_KEYBIND_CONFIG(GUI_ACTION_OPEN); - 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_UNDO); - UI_KEYBIND_CONFIG(GUI_ACTION_REDO); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_TOGGLE); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY); - UI_KEYBIND_CONFIG(GUI_ACTION_STOP); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_START); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_REPEAT); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_CURSOR); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_ONE); - UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_INS_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_INS_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_TOGGLE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_METRONOME); - UI_KEYBIND_CONFIG(GUI_ACTION_REPEAT_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_ORDERS); - UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_FULLSCREEN); - UI_KEYBIND_CONFIG(GUI_ACTION_PANIC); - - KEYBIND_CONFIG_END; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Window activation")) { - KEYBIND_CONFIG_BEGIN("keysWindow"); - - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EDIT_CONTROLS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ORDERS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SUBSONGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ABOUT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SETTINGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_MIXER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_OSCILLOSCOPE); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHAN_OSC); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EFFECT_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_VOL_METER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PIANO); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_NOTES); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHANNELS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_REGISTER_VIEW); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_LOG); - - UI_KEYBIND_CONFIG(GUI_ACTION_COLLAPSE_WINDOW); - UI_KEYBIND_CONFIG(GUI_ACTION_CLOSE_WINDOW); - - KEYBIND_CONFIG_END; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Note input")) { - std::vector sorted; - if (ImGui::BeginTable("keysNoteInput",4)) { - for (std::map::value_type& i: noteKeys) { - std::vector::iterator j; - for (j=sorted.begin(); j!=sorted.end(); j++) { - if (j->val>i.second) { - break; - } - } - sorted.insert(j,MappedInput(i.first,i.second)); - } - - static char id[4096]; - - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - ImGui::TableNextColumn(); - ImGui::Text("Key"); - ImGui::TableNextColumn(); - ImGui::Text("Type"); - ImGui::TableNextColumn(); - ImGui::Text("Value"); - ImGui::TableNextColumn(); - ImGui::Text("Remove"); - - for (MappedInput& i: sorted) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%s",SDL_GetScancodeName((SDL_Scancode)i.scan)); - ImGui::TableNextColumn(); - if (i.val==102) { - snprintf(id,4095,"Macro release##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=0; - } - } else if (i.val==101) { - snprintf(id,4095,"Note release##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=102; - } - } else if (i.val==100) { - snprintf(id,4095,"Note off##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=101; - } - } else { - snprintf(id,4095,"Note##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=100; - } - } - ImGui::TableNextColumn(); - if (i.val<100) { - snprintf(id,4095,"##SNValue_%d",i.scan); - if (ImGui::InputInt(id,&i.val,1,1)) { - if (i.val<0) i.val=0; - if (i.val>96) i.val=96; - noteKeys[i.scan]=i.val; - } - } - ImGui::TableNextColumn(); - snprintf(id,4095,ICON_FA_TIMES "##SNRemove_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys.erase(i.scan); - } - } - ImGui::EndTable(); - - if (ImGui::BeginCombo("##SNAddNew","Add...")) { - for (int i=0; i100) settings.guiColorsShading=100; + applyUISettings(false); + } + 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"); + UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_POPUP,"Pop-up background"); + 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_TITLE_INACTIVE,"Title bar (inactive)"); + UI_COLOR_CONFIG(GUI_COLOR_TITLE_COLLAPSED,"Title bar (collapsed)"); + UI_COLOR_CONFIG(GUI_COLOR_MENU_BAR,"Menu bar"); + UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border"); + UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow"); + UI_COLOR_CONFIG(GUI_COLOR_SCROLL,"Scroll bar"); + UI_COLOR_CONFIG(GUI_COLOR_SCROLL_HOVER,"Scroll bar (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_SCROLL_ACTIVE,"Scroll bar (clicked)"); + UI_COLOR_CONFIG(GUI_COLOR_SCROLL_BACKGROUND,"Scroll bar background"); + UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR,"Separator"); + UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_HOVER,"Separator (hover)"); + UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_ACTIVE,"Separator (active)"); + UI_COLOR_CONFIG(GUI_COLOR_DOCKING_PREVIEW,"Docking preview"); + UI_COLOR_CONFIG(GUI_COLOR_DOCKING_EMPTY,"Docking empty"); + UI_COLOR_CONFIG(GUI_COLOR_TABLE_HEADER,"Table header"); + UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_HARD,"Table border (hard)"); + UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_SOFT,"Table border (soft)"); + 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"); + 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_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"); + UI_COLOR_CONFIG(GUI_COLOR_ERROR,"Error hint"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("File Picker (built-in)")) { + UI_COLOR_CONFIG(GUI_COLOR_FILE_DIR,"Directory"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_NATIVE,"Song (native)"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_IMPORT,"Song (import)"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_INSTR,"Instrument"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_AUDIO,"Audio"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_WAVE,"Wavetable"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_VGM,"VGM"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_ZSM,"ZSM"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_FONT,"Font"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_OTHER,"Other"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Oscilloscope")) { + UI_COLOR_CONFIG(GUI_COLOR_OSC_BORDER,"Border"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG1,"Background (top-left)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG2,"Background (top-right)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG3,"Background (bottom-left)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG4,"Background (bottom-right)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE,"Waveform"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_PEAK,"Waveform (clip)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_REF,"Reference"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_GUIDE,"Guide"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Volume Meter")) { + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_LOW,"Low"); + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_HIGH,"High"); + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_PEAK,"Clip"); + ImGui::TreePop(); + } + 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_ORDER_SELECTED,"Selected order"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_SIMILAR,"Similar patterns"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Envelope View")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("FM Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); + UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); + UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); + UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); + + ImGui::TextWrapped("(the following colors only apply when \"Use separate colors for carriers/modulators in FM editor\" is on!)"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_MOD,"Mod. accent (primary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Macro Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_MACRO_VOLUME,"Volume"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_PITCH,"Pitch"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_WAVE,"Wave"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_OTHER,"Other"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Instrument Types")) { + UI_COLOR_CONFIG(GUI_COLOR_INSTR_FM,"FM (OPN)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_STD,"SN76489/Sega PSG"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_T6W28,"T6W28"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_GB,"Game Boy"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_C64,"C64"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AMIGA,"Amiga/Generic Sample"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_PCE,"PC Engine"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY,"AY-3-8910/SSG"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY8930,"AY8930"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_TIA,"TIA"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SAA1099,"SAA1099"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VIC,"VIC"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_PET,"PET"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6,"VRC6"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6_SAW,"VRC6 (saw)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPLL,"FM (OPLL)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL,"FM (OPL)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_FDS,"FDS"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VBOY,"Virtual Boy"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_N163,"Namco 163"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SCC,"Konami SCC"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPZ,"FM (OPZ)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_NAMCO,"Namco WSG"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL_DRUMS,"FM (OPL Drums)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPM,"FM (OPM)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_NES,"NES"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM6258,"MSM6258"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM6295,"MSM6295"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ADPCMA,"ADPCM-A"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ADPCMB,"ADPCM-B"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SEGAPCM,"Sega PCM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_QSOUND,"QSound"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_YMZ280B,"YMZ280B"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_RF5C68,"RF5C68"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM5232,"MSM5232"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_K007232,"K007232"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_GA20,"GA20"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Channel")) { + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_BG,"Single color (background)"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FG,"Single color (text)"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PCM,"PCM"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_WAVE,"Wave"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_OP,"FM operator"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_MUTED,"Muted"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Pattern")) { + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_PLAY_HEAD,"Playhead"); + 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)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION,"Selection"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_HOVER,"Selection (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_ACTIVE,"Selection (clicked)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_1,"Highlight 1"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_2,"Highlight 2"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX,"Row number"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI1,"Row number (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI2,"Row number (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE,"Note"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI1,"Note (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI2,"Note (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE,"Blank"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI1,"Blank (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI2,"Blank (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS,"Instrument"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_WARN,"Instrument (invalid type)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_ERROR,"Instrument (out of range)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MIN,"Volume (0%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_HALF,"Volume (50%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MAX,"Volume (100%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_INVALID,"Invalid effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PITCH,"Pitch effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_VOLUME,"Volume effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PANNING,"Panning effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SONG,"Song effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_TIME,"Time effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SPEED,"Speed effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,"Primary specific effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"Secondary specific effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_MISC,"Miscellaneous"); + UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Sample Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_BG,"Background"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_FG,"Waveform"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_BG,"Time background"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_FG,"Time text"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP,"Loop region"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CENTER,"Center guide"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_GRID,"Grid"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL,"Selection"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL_POINT,"Selection points"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE,"Preview needle"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE_PLAYING,"Playing needles"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP_POINT,"Loop markers"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_DISABLED,"Chip select: disabled"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_ENABLED,"Chip select: enabled"); + UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_WARNING,"Chip select: enabled (failure)"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Pattern Manager")) { + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_NULL,"Unallocated"); + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_UNUSED,"Unused"); + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_USED,"Used"); + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_OVERUSED,"Overused"); + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED,"Really overused"); + UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_COMBO_BREAKER,"Combo Breaker"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Piano")) { + UI_COLOR_CONFIG(GUI_COLOR_PIANO_BACKGROUND,"Background"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP,"Upper key"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP_HIT,"Upper key (feedback)"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"Upper key (pressed)"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM,"Lower key"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM_HIT,"Lower key (feedback)"); + UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"Lower key (pressed)"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Clock")) { + UI_COLOR_CONFIG(GUI_COLOR_CLOCK_TEXT,"Clock text"); + UI_COLOR_CONFIG(GUI_COLOR_CLOCK_BEAT_LOW,"Beat (off)"); + UI_COLOR_CONFIG(GUI_COLOR_CLOCK_BEAT_HIGH,"Beat (on)"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Patchbay")) { + UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORTSET,"PortSet"); + UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORT,"Port"); + UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_PORT_HIDDEN,"Port (hidden/unavailable)"); + UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_CONNECTION,"Connection (selected)"); + UI_COLOR_CONFIG(GUI_COLOR_PATCHBAY_CONNECTION_BG,"Connection (other)"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Log Viewer")) { + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_ERROR,"Log level: Error"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_WARNING,"Log level: Warning"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_INFO,"Log level: Info"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_DEBUG,"Log level: Debug"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_TRACE,"Log level: Trace/Verbose"); + ImGui::TreePop(); + } + END_SECTION; } if (nonLatchNibble) { // ok, so you decided to read the code. @@ -2598,44 +2667,41 @@ void FurnaceGUI::drawSettings() { // "Nice Amiga cover of the song!" - enables hidden systems (YMU759/SoundUnit/Dummy) // "42 63" - enables all instrument types // "????" - enables stuff - if (ImGui::BeginTabItem("Cheat Codes")) { - ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); - settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; - if (ImGui::BeginChild("SettingsView",settingsViewSize)) { - ImGui::Text("Enter code:"); - ImGui::InputText("##CheatCode",&mmlString[31]); - if (ImGui::Button("Submit")) { - unsigned int checker=0x11111111; - unsigned int checker1=0; - int index=0; - mmlString[30]="invalid code"; + CONFIG_SECTION("Cheat Codes") { + // SUBSECTION ENTER CODE: + CONFIG_SUBSECTION("Enter code:"); + ImGui::InputText("##CheatCode",&mmlString[31]); + if (ImGui::Button("Submit")) { + unsigned int checker=0x11111111; + unsigned int checker1=0; + int index=0; + mmlString[30]="invalid code"; - for (char& i: mmlString[31]) { - checker^=((unsigned int)i)<>1|(((checker)^(checker>>2)^(checker>>3)^(checker>>5))&1)<<31); - checker1<<=1; - index=(index+1)&31; - } - if (checker==0x90888b65 && checker1==0x1482) { - mmlString[30]="toggled alternate UI"; - toggleMobileUI(!mobileUI); - } - if (checker==0x5a42a113 && checker1==0xe4ef451e) { - mmlString[30]=":smile: :star_struck: :sunglasses: :ok_hand:"; - settings.hiddenSystems=!settings.hiddenSystems; - } - if (checker==0xe888896b && checker1==0xbde) { - mmlString[30]="enabled all instrument types"; - settings.displayAllInsTypes=!settings.displayAllInsTypes; - } - - mmlString[31]=""; + for (char& i: mmlString[31]) { + checker^=((unsigned int)i)<>1|(((checker)^(checker>>2)^(checker>>3)^(checker>>5))&1)<<31); + checker1<<=1; + index=(index+1)&31; } - ImGui::Text("%s",mmlString[30].c_str()); + if (checker==0x90888b65 && checker1==0x1482) { + mmlString[30]="toggled alternate UI"; + toggleMobileUI(!mobileUI); + } + if (checker==0x5a42a113 && checker1==0xe4ef451e) { + mmlString[30]=":smile: :star_struck: :sunglasses: :ok_hand:"; + settings.hiddenSystems=!settings.hiddenSystems; + } + if (checker==0xe888896b && checker1==0xbde) { + mmlString[30]="enabled all instrument types"; + settings.displayAllInsTypes=!settings.displayAllInsTypes; + } + + mmlString[31]=""; } - ImGui::EndChild(); - ImGui::EndTabItem(); + ImGui::Text("%s",mmlString[30].c_str()); + + END_SECTION; } } ImGui::EndTabBar(); @@ -2670,6 +2736,7 @@ void FurnaceGUI::drawSettings() { 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; @@ -2695,8 +2762,10 @@ void FurnaceGUI::syncSettings() { 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); @@ -2821,8 +2890,10 @@ void FurnaceGUI::syncSettings() { settings.newSongBehavior=e->getConfInt("newSongBehavior",0); settings.memUsageUnit=e->getConfInt("memUsageUnit",1); settings.cursorFollowsWheel=e->getConfInt("cursorFollowsWheel",0); + settings.noDMFCompat=e->getConfInt("noDMFCompat",0); clampSetting(settings.mainFontSize,2,96); + clampSetting(settings.headFontSize,2,96); clampSetting(settings.patFontSize,2,96); clampSetting(settings.iconSize,2,48); clampSetting(settings.audioEngine,0,1); @@ -2950,6 +3021,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.newSongBehavior,0,1); clampSetting(settings.memUsageUnit,0,1); clampSetting(settings.cursorFollowsWheel,0,1); + clampSetting(settings.noDMFCompat,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -3023,6 +3095,7 @@ void FurnaceGUI::commitSettings() { ); 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])); @@ -3048,8 +3121,10 @@ void FurnaceGUI::commitSettings() { e->setConf("tg100Path",settings.tg100Path); e->setConf("mu5Path",settings.mu5Path); e->setConf("mainFont",settings.mainFont); + e->setConf("headFont",settings.mainFont); 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); @@ -3175,6 +3250,7 @@ void FurnaceGUI::commitSettings() { e->setConf("newSongBehavior",settings.newSongBehavior); e->setConf("memUsageUnit",settings.memUsageUnit); e->setConf("cursorFollowsWheel",settings.cursorFollowsWheel); + e->setConf("noDMFCompat",settings.noDMFCompat); // colors for (int i=0; iAddFontDefault(); patFont=mainFont; bigFont=mainFont; + headFont=mainFont; if (rend) rend->destroyFontsTexture(); if (!ImGui::GetIO().Fonts->Build()) { logE("error again while building font atlas!"); @@ -3605,6 +3682,11 @@ void FurnaceGUI::popWarningColor() { // TODO! #define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" // TODO! +#define SYSTEM_HEAD_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" +#define SYSTEM_HEAD_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! +#define SYSTEM_HEAD_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! #define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf" #define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf" // GOOD LUCK WITH THIS ONE - UNTESTED @@ -3613,6 +3695,9 @@ void FurnaceGUI::popWarningColor() { #define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" #define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" #define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" +#define SYSTEM_HEAD_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" +#define SYSTEM_HEAD_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" +#define SYSTEM_HEAD_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" #define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" #define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" #define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" @@ -3621,6 +3706,9 @@ void FurnaceGUI::popWarningColor() { #define SYSTEM_FONT_PATH_2 "/system/fonts/DroidSans.ttf" #define SYSTEM_FONT_PATH_3 "/system/fonts/DroidSans.ttf" // ??? +#define SYSTEM_HEAD_FONT_PATH_1 "/system/fonts/Roboto-Regular.ttf" +#define SYSTEM_HEAD_FONT_PATH_2 "/system/fonts/DroidSans.ttf" +#define SYSTEM_HEAD_FONT_PATH_3 "/system/fonts/DroidSans.ttf" #define SYSTEM_PAT_FONT_PATH_1 "/system/fonts/RobotoMono-Regular.ttf" #define SYSTEM_PAT_FONT_PATH_2 "/system/fonts/DroidSansMono.ttf" #define SYSTEM_PAT_FONT_PATH_3 "/system/fonts/CutiveMono.ttf" @@ -3628,6 +3716,9 @@ void FurnaceGUI::popWarningColor() { #define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" #define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" #define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" +#define SYSTEM_HEAD_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" +#define SYSTEM_HEAD_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" +#define SYSTEM_HEAD_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" #define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" #define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf" #define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf" @@ -3868,12 +3959,17 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; + if (settings.headFont<0 || settings.headFont>6) settings.headFont=0; if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; if (settings.mainFont==6 && settings.mainFontPath.empty()) { logW("UI font path is empty! reverting to default font"); settings.mainFont=0; } + if (settings.headFont==6 && settings.headFontPath.empty()) { + logW("UI font path is empty! reverting to default font"); + settings.headFont=0; + } if (settings.patFont==6 && settings.patFontPath.empty()) { logW("pattern font path is empty! reverting to default font"); settings.patFont=0; @@ -3959,10 +4055,46 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { // 0x39B = Λ static const ImWchar bigFontRange[]={0x20,0xFF,0x39b,0x39b,0}; + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,MAX(1,40*dpiScale),NULL,bigFontRange))==NULL) { logE("could not load big UI font!"); } + if (settings.mainFontSize==settings.headFontSize && settings.headFont<5 && builtinFont[settings.headFont]==builtinFont[settings.mainFont]) { + logD("using main font for header font."); + headFont=mainFont; + } else { + if (settings.headFont==6) { // custom font + if ((headFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.headFontPath.c_str(),MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + logW("could not load header font! reverting to default font"); + settings.headFont=0; + if ((headFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.headFont],builtinFontLen[settings.headFont],MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + logE("could not load header font! falling back to IBM Plex Sans."); + headFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } else if (settings.headFont==5) { // system font + if ((headFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_HEAD_FONT_PATH_1,MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + if ((headFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_HEAD_FONT_PATH_2,MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + if ((headFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_HEAD_FONT_PATH_3,MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + logW("could not load header font! reverting to default font"); + settings.headFont=0; + if ((headFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.headFont],builtinFontLen[settings.headFont],MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + logE("could not load header font! falling back to IBM Plex Sans."); + headFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + } + } else { + if ((headFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.headFont],builtinFontLen[settings.headFont],MAX(1,e->getConfInt("headFontSize",27)*dpiScale),NULL,upTo800))==NULL) { + logE("could not load header font!"); + headFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + + mainFont->FallbackChar='?'; mainFont->EllipsisChar='.'; mainFont->EllipsisCharCount=3; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 0a746e61f..283fad98d 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -22,7 +22,7 @@ #include "misc/cpp/imgui_stdlib.h" #include -bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) { +bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu) { bool altered=false; bool restart=settings.restartOnFlagChange && modifyOnChange; bool supportsCustomRate=true; @@ -1564,7 +1564,13 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } altered=true; } - if (i<7) ImGui::SameLine(); + if (i<7) { + if (fromMenu) { + ImGui::SameLine(); + } else { + sameLineMaybe(); + } + } } if (CWSliderInt("Delay##EchoDelay",&echoDelay,0,15)) {