diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fd84220..1bf22a33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,6 +283,13 @@ if (USE_SDL2) # If you link SDL statically, you also need to define HAVE_LIBC so it builds with the C runtime that your application uses. # This should probably go in a FAQ. set(SDL_LIBC ON CACHE BOOL "Tell SDL that we want it to use our C runtime (required for proper static linking)" FORCE) + + # https://github.com/tildearrow/furnace/issues/1237 + # enabling this will result in SDL finding the Direct3D headers, forcing _WIN32_WINNT to an undesirable value (which makes the Wine headers define GetTickCount64) + if (SUPPORT_XP) + set(SDL_RENDER_D3D OFF CACHE BOOL "Enable the Direct3D render driver" FORCE) + endif() + add_subdirectory(extern/SDL EXCLUDE_FROM_ALL) list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2) list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include) @@ -498,6 +505,8 @@ src/engine/platform/sound/sm8521.c src/engine/platform/sound/d65modified.c +src/engine/platform/sound/ted-sound.c + src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp src/engine/platform/ym2610Interface.cpp @@ -589,6 +598,7 @@ src/engine/platform/ga20.cpp src/engine/platform/sm8521.cpp src/engine/platform/pv1000.cpp src/engine/platform/k053260.cpp +src/engine/platform/ted.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp diff --git a/README.md b/README.md index 6ae675de..f4d4af3c 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ some people have provided packages for Unix/Unix-like distributions. here's a li - **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!). - **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608. - **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari. +- **Void Linux**: [furnace](https://github.com/void-linux/void-packages/tree/master/srcpkgs/furnace) is available in the official repository. --- # developer info diff --git a/demos/a2600/Coconut_Mall.fur b/demos/a2600/Coconut_Mall.fur deleted file mode 100644 index 471fcbec..00000000 Binary files a/demos/a2600/Coconut_Mall.fur and /dev/null differ diff --git a/demos/arcade/Maximum_Overdrive_NamcoWSG.fur b/demos/arcade/Maximum_Overdrive_NamcoWSG.fur index 0fdbc113..2f25c28e 100644 Binary files a/demos/arcade/Maximum_Overdrive_NamcoWSG.fur and b/demos/arcade/Maximum_Overdrive_NamcoWSG.fur differ diff --git a/demos/arcade/Physics_Exam_TaitoArcade.fur b/demos/arcade/Physics_Exam_TaitoArcade.fur new file mode 100644 index 00000000..5cb97e54 Binary files /dev/null and b/demos/arcade/Physics_Exam_TaitoArcade.fur differ diff --git a/demos/arcade/Point_of_No_Return_SegaPCM.fur b/demos/arcade/Point_of_No_Return_SegaPCM.fur new file mode 100644 index 00000000..846d3680 Binary files /dev/null and b/demos/arcade/Point_of_No_Return_SegaPCM.fur differ diff --git a/demos/arcade/physics_exam.fur b/demos/arcade/physics_exam.fur deleted file mode 100644 index b26e3f6e..00000000 Binary files a/demos/arcade/physics_exam.fur and /dev/null differ diff --git a/demos/ay8910/vibe_zone.fur b/demos/ay8910/vibe_zone.fur new file mode 100644 index 00000000..ffd97b27 Binary files /dev/null and b/demos/ay8910/vibe_zone.fur differ diff --git a/demos/ay8930/PlayingOnTheStairs.fur b/demos/ay8930/PlayingOnTheStairs.fur index 0391fb4f..80c5fd82 100644 Binary files a/demos/ay8930/PlayingOnTheStairs.fur and b/demos/ay8930/PlayingOnTheStairs.fur differ diff --git a/demos/ay8930/joyful_.fur b/demos/ay8930/joyful.fur similarity index 100% rename from demos/ay8930/joyful_.fur rename to demos/ay8930/joyful.fur diff --git a/demos/gameboy/dtect.fur b/demos/gameboy/dtect.fur new file mode 100644 index 00000000..17c5f5bc Binary files /dev/null and b/demos/gameboy/dtect.fur differ diff --git a/demos/gameboy/freedom.fur b/demos/gameboy/freedom.fur new file mode 100644 index 00000000..024bafde Binary files /dev/null and b/demos/gameboy/freedom.fur differ diff --git a/demos/gameboy/ice_3_advert.fur b/demos/gameboy/ice_3_advert.fur new file mode 100644 index 00000000..cb0d61a9 Binary files /dev/null and b/demos/gameboy/ice_3_advert.fur differ diff --git a/demos/gameboy/minos.fur b/demos/gameboy/minos.fur new file mode 100644 index 00000000..14cb6244 Binary files /dev/null and b/demos/gameboy/minos.fur differ diff --git a/demos/gameboy/spreadtro.fur b/demos/gameboy/spreadtro.fur new file mode 100644 index 00000000..f8804b39 Binary files /dev/null and b/demos/gameboy/spreadtro.fur differ diff --git a/demos/genesis/inside_the_computer.fur b/demos/genesis/inside_the_computer.fur index b2682019..b3bd29c0 100644 Binary files a/demos/genesis/inside_the_computer.fur and b/demos/genesis/inside_the_computer.fur differ diff --git a/demos/genesis/mm5_boss.fur b/demos/genesis/mm5_boss.fur new file mode 100644 index 00000000..d3eb6caf Binary files /dev/null and b/demos/genesis/mm5_boss.fur differ diff --git a/demos/misc/FiveTwoThreeTwo_MSM5232.fur b/demos/misc/FiveTwoThreeTwo_MSM5232.fur new file mode 100644 index 00000000..4c5efae9 Binary files /dev/null and b/demos/misc/FiveTwoThreeTwo_MSM5232.fur differ diff --git a/demos/a2600/atari breakbeat.fur b/demos/misc/atari_breakbeat_TIA.fur similarity index 100% rename from demos/a2600/atari breakbeat.fur rename to demos/misc/atari_breakbeat_TIA.fur diff --git a/demos/misc/teddy_bear_midnight_jam_ted.fur b/demos/misc/teddy_bear_midnight_jam_ted.fur new file mode 100644 index 00000000..7ee9d067 Binary files /dev/null and b/demos/misc/teddy_bear_midnight_jam_ted.fur differ diff --git a/demos/a2600/the_erfngjt.fur b/demos/misc/the_erfngjt_TIA.fur similarity index 100% rename from demos/a2600/the_erfngjt.fur rename to demos/misc/the_erfngjt_TIA.fur diff --git a/demos/msx/21492413.fur b/demos/msx/21492413.fur new file mode 100644 index 00000000..95669441 Binary files /dev/null and b/demos/msx/21492413.fur differ diff --git a/demos/msx/Il_ambreSong.fur b/demos/msx/ranburu_song.fur similarity index 100% rename from demos/msx/Il_ambreSong.fur rename to demos/msx/ranburu_song.fur diff --git a/demos/multichip/track8_n163_k053260.fur b/demos/multichip/track8_n163_k053260.fur new file mode 100644 index 00000000..d5bc373a Binary files /dev/null and b/demos/multichip/track8_n163_k053260.fur differ diff --git a/demos/nes/the_best-1990.fur b/demos/nes/the_best-1990.fur index c5b62f15..ca599260 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 8e9289fc..e5430504 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 cb1588dd..3d6067af 100644 --- a/doc/2-interface/README.md +++ b/doc/2-interface/README.md @@ -13,6 +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-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/asset-list.md b/doc/2-interface/asset-list.md index 936c4a32..836d9b69 100644 --- a/doc/2-interface/asset-list.md +++ b/doc/2-interface/asset-list.md @@ -33,6 +33,6 @@ Everything from the instrument list applies here also, with one major difference ![samples window](samples.png) -Everything from the wavetables list applies here also, with the addition of two buttons: +Everything from the wavetables list applies here also, with the addition of two buttons before the Delete button: - **Preview**: Plays the selected sample at its default note. - **Stop preview**: Stops the sample playback. diff --git a/doc/2-interface/effect-list-window.md b/doc/2-interface/effect-list-window.md new file mode 100644 index 00000000..28a1c313 --- /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/effect-list.png b/doc/2-interface/effect-list.png new file mode 100644 index 00000000..dd5d0cc8 Binary files /dev/null and b/doc/2-interface/effect-list.png differ diff --git a/doc/2-interface/instruments-folder.png b/doc/2-interface/instruments-folder.png index a2fbb5ab..0afe5e0e 100644 Binary files a/doc/2-interface/instruments-folder.png and b/doc/2-interface/instruments-folder.png differ diff --git a/doc/2-interface/instruments.png b/doc/2-interface/instruments.png index 00102e54..56f99495 100644 Binary files a/doc/2-interface/instruments.png and b/doc/2-interface/instruments.png differ diff --git a/doc/2-interface/menu-bar.md b/doc/2-interface/menu-bar.md index b53aa5f7..9a5c2d17 100644 --- a/doc/2-interface/menu-bar.md +++ b/doc/2-interface/menu-bar.md @@ -2,6 +2,8 @@ the menu bar allows you to select from five menus: file, edit, settings, window and help. +items in _italics_ don't appear in basic mode and are only available in advanced mode. + # file - **new...**: create a new song. @@ -38,12 +40,12 @@ the menu bar allows you to select from five menus: file, edit, settings, window - **export command stream...**: export song data to a command stream file. see next section for more details. - this option is for developers. -- **add chip...**: add a chip to the current song. -- **configure chip...**: set a chip's parameters. +- _**add chip...**:_ add a chip to the current song. +- _**configure chip...**:_ set a chip's parameters. - for a list of parameters, see [7-systems](../7-systems/README.md). -- **change chip...**: change a chip to another. +- _**change chip...**:_ change a chip to another. - **Preserve channel positions**: enable this option to make sure Furnace does not auto-arrange/delete channels to compensate for differing channel counts. this can be useful for doing ports, e.g. from Genesis to PC-98. -- **remove chip...**: remove a chip. +- _**remove chip...**_: remove a chip. - **Preserve channel positions**: same thing as above. - **restore backup**: restore a previously saved backup. @@ -99,10 +101,10 @@ the following settings exist: - some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version. - **add pattern change hints**: this option adds a "hint" when a pattern change occurs. only useful if you're a developer. - the format of the "hint" data block that gets written is: `67 66 FE ll ll ll ll 01 oo rr pp pp pp ...` - - ll: length, a 32-bit little-endian number - - oo: order - - rr: initial row (a 0Dxx effect is able to select a different row) - - pp: pattern index (one per channel) + - `ll`: length, a 32-bit little-endian number + - `oo`: order + - `rr`: initial row (a 0Dxx effect is able to select a different row) + - `pp`: pattern index (one per channel) - **direct stream mode**: this option allows DualPCM to work. don't use this for other chips. - may or may not play well with hardware VGM players. @@ -140,7 +142,7 @@ it's not really useful, unless you're a developer and want to use a command stre - **cut**: moves the current selection in the pattern view to clipboard. - **copy**: copies the current selection in the pattern view to clipboard. - **paste**: inserts the clipboard's contents in the cursor position. -- **paste special...**: variants of the paste feature. +- _**paste special...**:_ variants of the paste feature. - **paste mix**: inserts the clipboard's contents in the cursor position, but does not erase the occupied region. - **paste mix (background)**: does the same thing as paste mix, but doesn't alter content which is already there. - **paste with ins (foreground)**: same thing as paste mix, but changes the instrument. @@ -154,8 +156,8 @@ it's not really useful, unless you're a developer and want to use a command stre - if a column is already selected, it will select the entire channel. - if a channel is already selected, it will select the entire pattern. -- **operation mask**: toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md) -- **input latch**: determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md) +- _**operation mask**:_ toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md) +- _**input latch**:_ determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md) - **note/octave up/down**: transposes notes in the current selection. @@ -188,7 +190,7 @@ it's not really useful, unless you're a developer and want to use a command stre - **expand song**: same as expand, but affects the entire song. - it also changes speeds and pattern length to compensate. -- **find/replace**: shows [the Find/Replace window](../8-advanced/find-replace.md). +- _**find/replace**:_ shows [the Find/Replace window](../8-advanced/find-replace.md). - **clear**: allows you to mass-delete things like songs, instruments and the like. @@ -213,12 +215,12 @@ all these menu items show or hide their associated windows. - [samples](../6-sample/README.md) - [orders](order-list.md) - [pattern](../3-pattern/README.md) -- [mixer](mixer.md) -- [grooves](grooves.md) -- [channels](channels.md) -- [pattern manager](pat-manager.md) -- [chip manager](chip-manager.md) -- [compatibility flags](compat-flags.md) +- _[mixer](mixer.md)_ +- _[grooves](grooves.md)_ +- _[channels](channels.md)_ +- _[pattern manager](pat-manager.md)_ +- _[chip manager](chip-manager.md)_ +- _[compatibility flags](compat-flags.md)_ - [song comments](comments.md) - [piano](piano.md) @@ -246,5 +248,5 @@ at the end of the menu bar, more information may be shown: - volume in decimal, hex, and percentage. - effect type and description. - during playback, the current values of the following will be listed:\ - speed/groove @ tick rate (BPM) | order | row | elapsed time. + > speed/groove @ tick rate (BPM) | order | row | elapsed time. - if any changes or edits have been made but not yet saved, "modified" will appear. diff --git a/doc/2-interface/samples.png b/doc/2-interface/samples.png index 2be62a9b..654ff878 100644 Binary files a/doc/2-interface/samples.png and b/doc/2-interface/samples.png differ diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index 06c21f1c..4cfbc854 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -4,214 +4,179 @@ 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!)**: 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**: uses native OS file dialog instead of Furnace's. +- **Number of recent files**: number of files to show in the _open recent..._ menu. +- **Compress when saving** + - uses 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. - - **Randomize**: set default to a random system. + - **Randomize**: sets default to a random system. - this will not choose a random system at each start. - **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). + - **Configure:**: 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 -- **Backend**: select SDL or JACK for audio output. +### Output + +- **Backend**: selects 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. +- **Outputs**: 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 -- **Arcade/YM2151 core** - - **ymfm** - - **Nuked-OPM** -- **Genesis/YM2612 core** - - **Nuked-OPN2** - - **ymfm** -- **SN76489 core** - - **MAME** - - **Nuked-PSG Mod** -- **NES core** - - **puNES** - - **NSFplay** -- **FDS core** - - **puNES** - - **NSFplay** -- **SID core** - - **reSID** - - **reSIDfp** -- **POKEY core** - - **Atari800 (mzpokeysnd)** - - **ASAP (C++ port)** -- **OPN/OPNA/OPNB cores** - - **ymfm only** - - **Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)** +- **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)** -- **PC Speaker strategy:** - - **evdev SND_TONE** - - **KIOCSOUND on /dev/tty1** - - **/dev/port** - - **KIOCSOUND on standard output** - - **outb()** + + +## Emulation + +### Cores + +- **Arcade/YM2151 core**\ + **Genesis/YM2612 core**\ + **SN76489 core**\ + **NES core**\ + **FDS core**\ + **SID core**\ + **POKEY core**\ + **OPN/OPNA/OPNB cores**: all of these are covered in the [guide to choosing emulation cores](../9-guides/emulation-cores.md). + +- **PC Speaker strategy**: this is covered in the [PC speaker system doc](../7-systems/pcspkr.md). - **Sample ROMs:** - **OPL4 YRW801 path** @@ -220,16 +185,124 @@ settings are saved when clicking the **OK** button at the bottom of the dialog. -# Appearance +## Keyboard -- **Render driver** -- **Automatic UI scaling factor**: automatically match the OS's UI scaling. -- **UI scaling factor**: only if "Automatic UI scaling factor" is off. -- **Main font**: if "Custom...", a file path selector will appear beneath. -- **Size** -- **Pattern font**: if "Custom...", a file path selector will appear beneath. -- **Size** +### 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:** selects 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: moving the cursor onto 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)** +- **Remove instrument value when inserting note off/release** +- **Remove volume value when inserting note off/release** + + +### 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 + +- **Automatic UI scaling factor**: automatically matches the OS's UI scaling. +- **UI scaling factor**: only appears if "Automatic UI scaling factor" is off. - **Icon size** + +### Text + +- **Main font**: overall interface font.\ + **Header font**: font for section headers.\ + **Pattern font** font for the pattern view, the order list, and related. + - if "Custom...", a file path selector will appear. + - **Size**: font size. - **Display Japanese characters**\ **Display Chinese (Simplified) characters**\ **Display Chinese (Traditional) characters**\ @@ -237,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** @@ -269,44 +324,41 @@ 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 -- **Channel colors:** - - **Single** - - **Channel type** - - **Instrument type** -- **Channel name colors:** - - **Single** - - **Channel type** - - **Instrument type** -- **Channel style:** +- **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 style:** sets the appearance of channel headers in pattern view. - **Classic** - **Line** - **Round** @@ -328,60 +380,98 @@ 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 + +- **Unified instrument/wavetable/sample list**: combines all three types of assets into one list. + - the buttons act as appropriate to the currently selected asset or header. +- **Horizontal instrument list**: when there are more instruments than there is room to display them... + - if on, scroll horizontally through multiple columns. + - if off, scroll vertically in one long column. - **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. +- **Display instrument type menu when adding instrument** + - if turned off, the menu can still be opened by right-clicking the add button. + +### 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**: uses the internal representation of detune values, such that detune amounts of -1, -2, and -3 are shown as 5, 6, and 7. + +### Statistics + +- **Chip memory usage unit:** unit for displaying memory usage in the Statistics window. + - **Bytes** + - **Kilobytes** + +### Oscilloscope + +- **Rounded corners** +- **Border** +- **Mono**: displays a single monaural waveform of all sound mixed together. + - if turned off, waves will be drawn on top of each other for each output channel. + - all colors are configurable via _Settings > Color > Color scheme > Oscilloscope > Wave (non-mono)._ +- **Anti-aliased**: smoothes the lines of the waveform. + - slight performance cost and slightly buggy. +- **Fill entire window**: removes the gap between the waveform and the edge of the window. +- **Waveform goes out of bounds**: allows the waveform to draw past the top and bottom of the oscilloscope. + +### 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... +- **Borders around widgets**: draws thin borders on buttons, checkboxes, text widgets, and the like. -# 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/2-interface/song-info.md b/doc/2-interface/song-info.md index 7919e7be..03d69857 100644 --- a/doc/2-interface/song-info.md +++ b/doc/2-interface/song-info.md @@ -1,13 +1,13 @@ # song info - **Name**: The track's title. -- **Author**: List of contributors to a song. If the song is a cover of someone else's track, it's customary to list their name first, followed by `[cv. YourName]`. +- **Author**: List of contributors to a song. If the song is a cover of someone else's work, it's customary to list their name first, followed by `[cv. YourName]`. - **Album**: The associated album name, the name of the game the song is from, or whatever. -- **System**: The game console or computer the track is designed for. This is automatically set when creating a new tune, but it can be changed to anything one wants. The **Auto** button will provide a guess based on the chips in use. +- **System**: The game console or computer the track is designed for. This is automatically set when creating a new tune, but in advanced mode, it can be changed to anything one wants. The **Auto** button will provide a guess based on the chips in use. All of this metadata will be included in a VGM export. This isn't the case for a WAV export, however. -**Tuning (A-4)**: Set tuning based on the note A-4, which should be 440 in most cases. Opening an Amiga MOD will set it to 436 for hardware compatibility. +- _**Tuning (A-4)**:_ Set tuning based on the note A-4, which should be 440 in most cases. Opening an Amiga MOD will set it to 436 for hardware compatibility. Available only in advanced mode. # subsongs @@ -23,6 +23,8 @@ This window allows one to create **subsongs** - multiple individual songs within There are multiple ways to set the tempo of a song. +Items in _italics_ don't appear in basic mode and are only available in advanced mode. + **Tick Rate**: The frequency of ticks per second, thus the rate at which notes and effects are processed. - All values are allowed for all chips, though most chips have hardware limitations that mean they should stay at either 60 (approximately NTSC) or 50 (exactly PAL). - Clicking the Tick Rate button switches to a more traditional **Base Tempo** BPM setting. @@ -30,14 +32,14 @@ There are multiple ways to set the tempo of a song. **Speed**: The number of ticks per row. - Clicking the "Speed" button changes to more complex modes covered in the [grooves] page. -**Virtual Tempo**: Simulates any arbitrary tempo without altering the tick rate. It does this by adding or skipping ticks to approximate the tempo. The two numbers represent a ratio applied to the actual tick rate. Example: +_**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rate. It does this by adding or skipping ticks to approximate the tempo. The two numbers represent a ratio applied to the actual tick rate. Example: - Set tick rate to 150 BPM (60 Hz) and speed to 6. - Set the first virtual tempo number (numerator) to 200. - Set the second virtual tempo number (denominator) to 150. - The track will play at 200 BPM. - The ratio doesn't have to match BPM numbers. Set the numerator to 4 and the denominator to 5, and the virtual BPM becomes 150 × 4/5 = 120. -**Divider**: Changes the effective tick rate. A tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each! +_**Divider**:_ Changes the effective tick rate. A tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each! **Highlight**: Sets the pattern row highlights: - The first value represents the number of rows per beat. @@ -46,4 +48,4 @@ There are multiple ways to set the tempo of a song. **Pattern Length**: The length of each pattern in rows. This affects all patterns in the song, and every pattern must be the same length. (Individual patterns can be cut short by `0Bxx`, `0Dxx`, and `FFxx` commands.) -**Song Length**: How many orders are in the order list. Decreasing it will hide the orders at the bottom. Increasing it will restore those orders; increasing it further will add new orders of all `00` patterns. +_**Song Length**:_ How many orders are in the order list. Decreasing it will hide the orders at the bottom. Increasing it will restore those orders; increasing it further will add new orders of all `00` patterns. diff --git a/doc/2-interface/wavetables.png b/doc/2-interface/wavetables.png index 4ac45f2c..3de7413c 100644 Binary files a/doc/2-interface/wavetables.png and b/doc/2-interface/wavetables.png differ diff --git a/doc/3-pattern/README.md b/doc/3-pattern/README.md index d3e0b86f..d7e1dd7e 100644 --- a/doc/3-pattern/README.md +++ b/doc/3-pattern/README.md @@ -4,41 +4,85 @@ the pattern view allows you to edit the song. ![pattern view](pattern.png) -a pattern consists of columns ("channels") and rows. +a pattern consists of columns ("channels") and numbered rows. each column has several subcolumns in this order: 1. note 2. instrument 3. volume -4. effect and effect value (several) +4. effects, split into effect type and effect value all columns are represented in hexadecimal, except for the note column. -# managing channels +row highlights show beats and measures, and are configured in the [the Speed window](../2-interface/song-info.md). -you may mute channels, toggle solo mode, collapse channels or even hide them. -clicking on a channel name mutes that channel. -double-clicking or right-clicking it enables solo mode, in where only that channel will be audible. - -clicking the `++` at the top left corner of the pattern view displays additional buttons for channel configuration: - -![channel bar](channelbar.png) - -to rename and/or hide channels, see the Channels window (window > channels). - -![channels](channels.png) - -# cursor and selection +## cursor and selection you may change the cursor position by clicking anywhere on the pattern. -to select, press and hold the left mouse button. then drag the mouse and release the button to finish selection. +to select an area, press and hold the left mouse button. then drag the mouse and release the button to finish selection. -# keyboard layout +right-clicking within the pattern view brings up a pop-up menu with everything in the [edit menu](../2-interface/menu-bar.md) that makes sense for entering data or altering a selected area. -## shortcuts + + +## channel bar + +using the channel bar, you may adjust several aspects of the channel display. + +![channel bar](channelbar.png) + +clicking on a channel name mutes that channel. + +double-clicking or right-clicking it enables solo mode, in which only that channel will be audible. + +clicking the `++` at the top left corner of the pattern view cycles through three channel bar view modes: +- **Compact**: shows only channel names. +- **Expanded**: as shown above. adds buttons: + - **-**: collapse visible columns. changes to **+** when columns are hidden; click to expand them. + - **<**: disables the last effect column and hides it. effects are not deleted... + - **>**: adds an effects column. if one previously existed, its contents will be preserved. +- **Pattern names**: adds a text field with which one can name the current pattern. pattern names are also visible when hovering over a pattern in the order list. + +right-clicking the `++` toggles the visualizer, which is active only during playback. + +to rename and/or hide channels, open [the Channels window](../8-advanced/channels.md) via the window menu. + + +# input + +## note input + +![keyboard](keyboard.png) + +- pressing any of the respective keys will insert a note at the cursor's location, then advance to the next row (or otherwise according to the Edit Step.) +- **note off** turns off the last played note in that channel (key off for FM; note cut otherwise). +- **note release** triggers macro release (and in FM channels it also triggers key off). +- **macro release** does the same as above, but does not trigger key off in FM channels. +- **toggle edit** enables and disables editing. when editing is enabled, the cursor's row will be shaded red. + +## instrument/volume input + +type any hexadecimal number (0-9 and A-F). the cursor will move by the Edit Step when a suitable value is entered. + +## effect input + +works like the instrument/volume input. + +each effect column has two subcolumns: effect and effect value. +if the effect value is not present, it is treated as `00`. + +most effects run until canceled using an effect of the same type with effect value `00`, with some exceptions. + +here's [a list of effect types](effects.md). + + + +# keyboard shortcuts + +these are the default key functions. all keys are configurable in the Keyboard tab of the Settings window. key | action ------------|----------------------------------------------------------------- @@ -68,27 +112,3 @@ Ctrl-F2 | transpose selection (+1 semitone) Ctrl-F3 | transpose selection (-1 octave) Ctrl-F4 | transpose selection (+1 octave) Space | toggle note input (edit) - -## note input - -![keyboard](keyboard.png) - -- pressing any of the respective keys will insert a note at the cursor's location, and then advance it by the Edit Step. -- note off turns off the last played note in that channel (key off on FM; note cut otherwise). -- note release triggers macro release (and in FM channels it also triggers key off). -- macro release does the same as above, but does not trigger key off in FM channels. - -## instrument/volume input - -type any hexadecimal number (0-9 and A-F). the cursor will move by the Edit Step when a suitable value is entered. - -## effect input - -works like the instrument/volume input. - -each effect column has two subcolumns: effect and effect value. -if the effect value is not present, it is treated as `00`. - -most effects run until canceled using an effect of the same type with effect value `00`, with some exceptions. - -for a list of effects [click here](effects.md). diff --git a/doc/3-pattern/channelbar.png b/doc/3-pattern/channelbar.png index 6ef20cb4..dc7193e3 100644 Binary files a/doc/3-pattern/channelbar.png and b/doc/3-pattern/channelbar.png differ diff --git a/doc/3-pattern/channels.png b/doc/3-pattern/channels.png deleted file mode 100644 index ab4c985f..00000000 Binary files a/doc/3-pattern/channels.png and /dev/null differ diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index 91e31147..6a9515d6 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -14,7 +14,7 @@ however, effects are continuous, which means you only need to type it once and t - `F4xx`: **Fine volume slide down.** same as `0A0x` but 64× slower. - `F8xx`: **Single tick volume slide up.** adds `x` to volume on first tick only. - `F9xx`: **Single tick volume slide down.** subtracts `x` from volume on first tick only. - + - --- - `07xy`: **Tremolo.** changes volume to be "wavy" with a sine LFO. `x` is the speed. `y` is the depth. - Tremolo is downward only. - Maximum tremolo depth is -60 volume steps. @@ -26,16 +26,17 @@ however, effects are continuous, which means you only need to type it once and t - `02xx`: **Pitch slide down.** - `F1xx`: **Single tick pitch slide up.** - `F2xx`: **Single tick pitch slide down.** - -- `03xx`: **Portamento.** slides the current note's pitch to the specified note. `x` is the slide speed. - - A note _must_ be present for this effect to work. + - --- +- `03xx`: **Portamento.** slides the currently playing note's pitch toward the new note. `x` is the slide speed. + - a note _must_ be present with this effect for it to work. + - the effect stops automatically when it reaches the new note. - `E1xy`: **Note slide up.** `x` is the speed, while `y` is how many semitones to slide up. - `E2xy`: **Note slide down.** `x` is the speed, while `y` is how many semitones to slide down. - -- `EAxx`: **Toggle legato.** while on, notes instantly change the pitch of the currrently playing sound instead of starting it over. + - --- +- `EAxx`: **Toggle legato.** while on, new notes instantly change the pitch of the currently playing sound instead of starting it over. - `00xy`: **Arpeggio.** after using this effect the channel will rapidly switch between semitone values of `note`, `note + x` and `note + y`. - `E0xx`: **Set arpeggio speed.** this sets the number of ticks between arpeggio values. default is 1. - + - --- - `04xy`: **Vibrato.** changes pitch to be "wavy" with a sine LFO. `x` is the speed, while `y` is the depth. - Maximum vibrato depth is ±1 semitone. - `E3xx`: **Set vibrato direction.** `xx` may be one of the following: @@ -54,7 +55,7 @@ not all chips support these effects. - `82xx`: **Set volume of right channel** (from `00` to `FF`). - `89xx`: **Set volume of rear left channel** (from `00` to `FF`). - `8Axx`: **Set volume of rear right channel** (from `00` to `FF`). - + - --- - `80xx`: **Set panning (linear).** this effect behaves more like other trackers: - `00` is left. - `80` is center. @@ -64,15 +65,15 @@ not all chips support these effects. - `09xx`: **Set speed/groove.** if no grooves are defined, this sets speed. If alternating speeds are active, this sets the first speed. - `0Fxx`: **Set speed 2.** during alternating speeds or a groove, this sets the second speed. - + - --- - `Cxxx`: **Set tick rate.** changes tick rate to `xxx` Hz (ticks per second). - `xxx` may be from `000` to `3FF`. - `F0xx`: **Set BPM.** changes tick rate according to beats per minute. range is `01` to `FF`. - + - --- - `0Bxx`: **Jump to order.** `x` is the order to play after the current row. - this marks the end of a loop with order `x` as the loop start. -- `0Dxx`: **Jump to next pattern.** skips the current row and remainder of current order. - - this can be used to shorten the current order. +- `0Dxx`: **Jump to next pattern.** skips the current row and remainder of current order. `x` is the row at which to start playing the next pattern. + - this can be used to shorten the current order as though it had a different pattern length. - `FFxx`: **Stop song.** stops playback and ends the song. `x` is ignored. ## note @@ -86,12 +87,12 @@ not all chips support these effects. - `9xxx`: **Set sample position.** jumps current sample to position `xxx * 0x100`. - Not all chips support this effect. -- `EBxx`: **Set sample bank.** - - Does not apply on Amiga. +- `EBxx`: **Set LEGACY sample mode bank.** selects sample bank. used only for compatibility. + - does not apply on Amiga. - `EExx`: **Send external command.** - - This effect is currently incomplete. -- `F5xx`: **Disable macro.** see macro table at the end of this document for possible values. -- `F6xx`: **Enable macro.** + - this effect is currently incomplete. +- `F5xx`: **Disable macro.**\ + `F6xx`: **Enable macro.** see macro table at the end of this document for possible values. additionally, [each chip has its own effects](../7-systems/README.md). diff --git a/doc/3-pattern/keyboard.png b/doc/3-pattern/keyboard.png index 7042af1c..f16f9f69 100644 Binary files a/doc/3-pattern/keyboard.png and b/doc/3-pattern/keyboard.png differ diff --git a/doc/3-pattern/pattern.png b/doc/3-pattern/pattern.png index ca059faa..0d31fcf1 100644 Binary files a/doc/3-pattern/pattern.png and b/doc/3-pattern/pattern.png differ diff --git a/doc/4-instrument/FM-ADSRchart.png b/doc/4-instrument/FM-ADSRchart.png new file mode 100644 index 00000000..407239c8 Binary files /dev/null and b/doc/4-instrument/FM-ADSRchart.png differ diff --git a/doc/4-instrument/README.md b/doc/4-instrument/README.md index bd00b6b8..7ab7898e 100644 --- a/doc/4-instrument/README.md +++ b/doc/4-instrument/README.md @@ -1,11 +1,3 @@ -# instrument list - -![instrument list](list.png) - -click on an instrument to select it. - -double-click to open the instrument editor. - # instrument editor every instrument can be renamed and have its type changed. @@ -66,14 +58,16 @@ Some macros are "bitmap" style. They represent a number of "bits" that can be to The number between the macro type label and the macro type button is the macro length in steps. The `-` and `+` buttons change the length of the macro. Start out by adding at least a few steps. -The values of the macro can be drawn in the "bar graph box". Just beneath the box is shorter bar graph. -- Click to set the start point of a loop; the end point is the last value or release point. Right-click to remove the loop. -- Shift-click to set the release point. When played, the macro will hold here until the note is released. Right-click to remove the release point. +The values of the macro can be drawn in the "bar graph" box. + +Just beneath the box is a shorter bar that controls looping. +- Click to set the start point of a loop; the end point is the last value or release point. It appears as half-height bars. Right-click to remove the loop. +- Shift-click to set the release point. When played, the macro will hold here until the note is released. It appears as a full-height bar. Right-click to remove the release point. Finally, the sequence of values can be directly edited in the text box at the bottom. - The loop start is entered as a `|`. - The release point is entered as a `/`. -- In arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). No matter the note played, `@` values will be played at that exact note. This is especially useful for noise instruments with preset periods. +- In arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). No matter the note entered in the pattern, `@` values will be played at that exact note. This is especially useful for noise instruments with preset periods. ### ADSR @@ -100,15 +94,29 @@ Finally, the sequence of values can be directly edited in the text box at the bo - **Phase** is which part of the waveform the macro will start at, measured in 1/1024 increments. - **Shape** is the waveform used. Triangle is the default, and Saw and Square are exactly as they say. -# samples + + +# wavetable + +This tab appears for PC Engine, FDS, Namco WSG, and other wavetable-based instruments. + +![wavetable tab](wavetable.png) + +When **Enable synthesizer** is off, the only option is to select a wavetable entry with the text entry box beneath the **Wave 1** preview. + +To use the wavetable synthesizer, refer to the bottom part of [the wavetable documentation](../5-wave/README.md). + + +# sample This tab appears for Generic PCM, SNES, Amiga, and other sample-based instruments. -![](sample-map.png) +![sample tab](sample-map.png) - **Initial Sample**: the sample that the instrument will use. - **Use wavetable**: instead of samples, use wavetables. this causes the [Wavetables](../5-wave/README.md) tab to appear next to Sample. - depending on the system and use of the wavetable synthesizer, this may or may not be reproducible on hardware. - **Use sample map**: assigns a sample to each note. - - samples will be played at their default pitch. - - to set a note's sample, click the list entry in the `#` column then type the number of the sample. + - to set a note's sample, click the list entry in the "#" column then type the number of the sample. + - to set the pitch at which a sample is played, click the list entry in the "note" column and press the key for the new note. + diff --git a/doc/4-instrument/fm.md b/doc/4-instrument/fm.md index 08542eb3..72c0ba78 100644 --- a/doc/4-instrument/fm.md +++ b/doc/4-instrument/fm.md @@ -1,47 +1,77 @@ # FM synthesis instrument editor -FM editor is divided into 7 tabs: +The FM editor is divided into 7 tabs: - **FM**: for controlling the basic parameters of FM sound source. - **Macros (FM)**: for macros controlling algorithm, feedback and LFO -- **Macros (OP1)**: for macros controlling FM paramets of operator 1 -- **Macros (OP2)**: for macros controlling FM paramets of operator 2 -- **Macros (OP3)**: for macros controlling FM paramets of operator 3 -- **Macros (OP4)**: for macros controlling FM paramets of operator 4 -- **Macros**: for miscellaneous macros controlling volume, argeggio and YM2151 noise generator. +- **Macros (OP1)**: for macros controlling FM parameters of operator 1 +- **Macros (OP2)**: for macros controlling FM parameters of operator 2 +- **Macros (OP3)**: for macros controlling FM parameters of operator 3 +- **Macros (OP4)**: for macros controlling FM parameters of operator 4 +- **Macros**: for miscellaneous macros controlling volume, arpeggio, and YM2151 noise generator. ## FM -FM synthesizers Furnace supports are four-operator, meaning it takes four oscillators to produce a single sound. Each operator is controlled by a dozen sliders: +The FM synthesizers Furnace supports are four-operator, meaning it takes four oscillators to produce a single sound. +These apply to the instrument as a whole: +- **Feedback (FB)**: Determines how many times operator 1 returns its output to itself. (0-7 range) +- **Algorithm (AL)**: Determines how operators are connected to each other. (0-7 range) + - Left-click pops up a small "operators changes with volume?" dialog where each operator can be toggled to scale with volume level. + - Right-click to switch to a preview display of the waveform generated on a new note: + - Left-click restarts the preview. + - Middle-click pauses and unpauses the preview. + - Right-click returns to algorithm view. + +- **LFO Frequency Sensitivity**: Determines the amount of LFO frequency changes. (0-7 range) +- **LFO Amplitude Sensitivity (AM)**: Determines the amount of LFO amplitude changes. (0-3 range) + +These apply to each operator: +- The crossed-arrows button can be dragged to rearrange operators. +- The **OP1**, **OP2**, **OP3**, and **OP4** buttons enable or disable those operators. +- **Amplitude Modulation (AM)**: Makes the operator affected by LFO. +- **Hardware Envelope Generator (SSG-EG)**: Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. YM2610/YM2612 sound source only. - **Attack Rate (AR)**: determines the rising time for the sound. The bigger the value, the faster the attack. (0-31 range) - **Decay Rate (DR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. It's the initial amplitude decay rate. (0-31 range) -- **Secondary Decay Rate (DR2)/Sustain Rate (SR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. This is the long "tail" of the sound that continues as long as the key is depressed. (0-31 range) +- **Sustain Level (SL)**: Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range) +- **Secondary Decay Rate (DR2) / Sustain Rate (SR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. This is the long "tail" of the sound that continues as long as the key is depressed. (0-31 range) - **Release Rate (RR)**: Determines the rate at which the sound disappears after KEY-OFF. The higher the value, the shorter the release. (0-15 range) -- **Sustain Level(SL)**: Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range) - **Total Level (TL)**: Represents the envelope’s highest amplitude, with 0 being the largest and 127 (decimal) the smallest. A change of one unit is about 0.75 dB. -- **Envelope Scale (KSR)**: A parameter that determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range) + +![FM ADSR chart](FM-ADSRchart.png) + +- **Envelope Scale (KSR)**: Also known as "Key Scale". Determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range) - **Frequency Multiplier (MULT)**: Determines the operator frequency in relation to the pitch. (0-15 range) -- **Fine Detune (DT)**: Shifts the pitch a little (0-7 range) -- **Coarse Detune (DT2)**: Shifts the pitch by tens of cents (0-3 range) WARNING: this parameter affects only YM2151 sound source!!! -- **Hardware Envelope Generator (SSG-EG)**: Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. WARNING: this parameter affects only YM2610/YM2612 sound source!!! -- **Algorithm (AL)**: Determines how operators are connected to each other. (0-7 range) -- **Feedback (FB)**: Determines the amount of signal whick operator 1 returns to itself. (0-7 range) -- **Amplitude Modulation (AM)**: Makes the operator affected by LFO. -- **LFO Frequency Sensitivity**: Determines the amount of LFO frequency changes. (0-7 range) -- **LFO Amplitude Sensitivity (AM)**: Determines the amount of LFO frequency changes. (0-3 range) +- **Fine Detune (DT)**: Shifts the pitch a little. (0-7 range) +- **Coarse Detune (DT2)**: Shifts the pitch by tens of cents. (0-3 range) YM2151 sound source only. + + +## macros + +Macros define the sequence of values passed to the given parameter. Via macro, along with the previously mentioned parameters, the following can be controlled: + +## FM Macros + +- **AM Depth**: amplitude modulation depth. YM2151 sound source only. +- **PM Depth**: pitch modulation depth. YM2151 sound source only. +- **LFO Speed**: LFO frequency. +- **LFO Shape**: LFO shape. Choose between saw, square, triangle, and random. +- **OpMask**: toggles each operator. + +## OP1-OP4 Macros + +All parameters are listed above. ## Macros -Macros define the sequence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled: - -- **LFO Frequency** -- **LFO Waveform**: _WARNING:_ this parameter affects only YM2151 sound source! -- **Amplitude Modulation Depth**: _WARNING:_ this parameter affects only YM2151 sound source! -- **Frequency Modulation Depth**: _WARNING:_ this parameter affects only YM2151 sound source! -- **Arpeggio Macro**: Pitch change sequence in semitones. Two modes are available: - - **Absolute** (default): Executes the pitch with absolute change based on the pitch of the actual note. - - **Fixed**: Executes at the pitch specified in the sequence regardless of the note pitch. +- **Arpeggio**: Pitch change sequence in semitones. - **Noise Frequency**: specifies the noise frequency in noise mode of YM2151's Channel 8 Operator 4 special mode. +- **Panning**: toggles output on left and right channels. +- **Pitch**: fine pitch. + - **Relative**: pitch changes are relative to the current pitch, not the note's base pitch. +- **Phase Reset**: Restarts all operators and resets the waveform to its start. Effectively the same as a `0Cxx` retrigger. -Looping: You can loop the execution of part of a sequence. Left-click anywhere on the Loop line at the bottom of the editor to create a loop. You can move the start and end points of the loop by dragging both ends of the loop. Rigkt-click to remove the loop. + +# links + +[FM instrument tutorial](https://www.youtube.com/watch?v=wS8edjurjDw): A great starting point to learn how create and work with FM sounds. This was made for DefleMask, but all the same principles apply. diff --git a/doc/4-instrument/list.png b/doc/4-instrument/list.png deleted file mode 100644 index e8af84a8..00000000 Binary files a/doc/4-instrument/list.png and /dev/null differ diff --git a/doc/4-instrument/macro-seq.png b/doc/4-instrument/macro-seq.png index 6ea2050b..e4bd6ee9 100644 Binary files a/doc/4-instrument/macro-seq.png and b/doc/4-instrument/macro-seq.png differ diff --git a/doc/4-instrument/macro.png b/doc/4-instrument/macro.png deleted file mode 100644 index 4e03bad9..00000000 Binary files a/doc/4-instrument/macro.png and /dev/null differ diff --git a/doc/4-instrument/n163.md b/doc/4-instrument/n163.md index 2e37b0d5..b12bfe35 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/4-instrument/sample-map.png b/doc/4-instrument/sample-map.png index 1d278712..fb1f9b51 100644 Binary files a/doc/4-instrument/sample-map.png and b/doc/4-instrument/sample-map.png differ diff --git a/doc/4-instrument/ted.md b/doc/4-instrument/ted.md new file mode 100644 index 00000000..47a5f6d6 --- /dev/null +++ b/doc/4-instrument/ted.md @@ -0,0 +1,11 @@ +# TED instrument editor + +TED instrument editor consists of these macros: + +- **Volume**: volume sequence. **global!** +- **Arpeggio**: pitch sequence +- **Square/Noise**: select whether square, noise or nothing will be output + - noise only available on channel 2 + - if square and noise are enabled, square takes precedence. +- **Pitch**: "fine" pitch +- **Phase Reset**: trigger restart of waveform. **global!** diff --git a/doc/4-instrument/wavetable.png b/doc/4-instrument/wavetable.png new file mode 100644 index 00000000..7c862d95 Binary files /dev/null and b/doc/4-instrument/wavetable.png differ diff --git a/doc/5-wave/README.md b/doc/5-wave/README.md index 59131d39..7c586ef7 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 63788437..9f5a6cf8 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -32,17 +32,16 @@ the following sound chips have sample support: - Ensoniq OTTO/ES5506 - Yamaha PCMD8/YMZ280B - MMC5 (last channel only) -- SNES/S-DSP -## compatible sample mode +## compatible sample mode (LEGACY) -effect `17xx` enables/disables compatible sample mode whether supported (e.g. on Sega Genesis or PC Engine). +**use of this mode is discouraged in favor of Sample type instruments.** + +effect `17xx` enables/disables compatible sample mode where supported (e.g. on Sega Genesis or PC Engine). in this mode, samples are mapped to notes in an octave from C to B, allowing you to use up to 12 samples. if you need to use more samples, you may change the sample bank using effect `EBxx`. -use of this mode is discouraged in favor of Sample type instruments. - ## notes due to limitations in some of those sound chips, some restrictions exist: diff --git a/doc/7-systems/README.md b/doc/7-systems/README.md index 294fdc9e..53be263e 100644 --- a/doc/7-systems/README.md +++ b/doc/7-systems/README.md @@ -1,56 +1,122 @@ -# sound chips +# systems -this is a list of sound chips that Furnace supports, including effects. +this is a list of some of the systems that Furnace supports, as shown in the new file dialog. each chip links to a page with information and a list of supported effects. +some systems have alternate chips, such as the Sega Genesis having a YM2612, a YM3438, or YMF276 depending on the model. this list shows the default configuration. + +- **Sega Genesis**: [YM3438](ym2612.md), [SN76489](sms.md) +- **Sega Genesis (with Sega CD)**: [YM3438](ym2612.md), [SN76489](sms.md), [RF5C164](ricoh.md) + +- **Sega Master System**: [SN76489](sms.md) +- **Sega Master System (with FM expansion)**: [SN76489](sms.md), [YM2413](opll.md) + +- **NES**: [2A03](nes.md) +- **Famicom with Konami VRC6**: [2A03](nes.md), [VRC6](vrc6.md) +- **Famicom with Konami VRC7**: [2A03](nes.md), [VRC7](opl.md) +- **Famicom with MMC5**: [2A03](nes.md), [MMC5](mmc5.md) +- **Famicom with Sunsoft 5B**: [2A03](nes.md), [5B](ay8910.md) +- **Famicom with Namco 163**: [2A03](nes.md), [N163](n163.md) +- **Famicom Disk System**: [2A03](nes.md), [FDS](fds.md) + +- **Game Boy**: [Game Boy](game-boy.md) + +- **SNES**: [SNES](snes.md) + +- **NEC PC Engine/TurboGrafx-16**: [HuC6280](pce.md) + +- **Commodore VIC-20**: [VIC](vic20.md) + +- **Commodore 64 (6581 SID)**: [MOS 6581](c64.md) +- **Commodore 64 (8580 SID)**: [MOS 8580](c64.md) + +- **Amiga**: [Amiga](amiga.md) + +- **Arcade (YM2151 and SegaPCM)**: [YM2151](ym2152.md), [SegaPCM](segapcm.md) + +- **Capcom CPS-1**: [YM2151](ym2152.md), [MSM6295](msm6295.md) +- **Capcom CPS-2 (QSound)**: [QSound](qsound.md) + +- **Neo Geo CD**: [YM2610](ym2610.md) +- **Neo Geo CD (extended channel 2)**: [YM2610](ym2610.md) + +- **Neo Geo Pocket**: [T6W28](t6w28.md), [DAC](dac.md) + +- **Atari 2600/7800**: [TIA](tia.md) +- **Atari 800**: [POKEY](pokey.md) + +- **Konami Bubble System**: [AY-3-8910](ay8910.md) × 2, [Konami WSG](bubblesystem.md) + +- **Sharp X68000**: [YM2151](ym2152.md), [MSM6258](msm6258.md) + +- **PC + Sound Blaster Pro**: [YM3812](opl.md) × 2, [DAC](dac.md), [PC Speaker](pcspkr.md) + +- **MSX**: [AY-3-8910](ay8910.md) +- **MSX + SCC**: [YM2149(F)](ay8910.md), [SCC](scc.md) + +- **Commander X16 (VERA only)**: [VERA](vera.md) + +- and many, many more! + + + +# chips + +this is the full list of chips that Furnace supports. + +- [2A03](nes.md) - [Amiga](amiga.md) -- [AY-3-8910](ay8910.md) -- [Microchip AY8930](ay8930.md) -- [Bubble System WSG](bubblesystem.md) -- [Casio PV-1000](pv1000.md) -- [Commodore 64](c64.md) -- [Commodore PET](pet.md) -- [Commodore VIC-20](vic20.md) -- [Generic PCM DAC](dac.md) -- [Famicom Disk System](fds.md) -- [Game Boy](game-boy.md) -- [Konami K007232](k007232.md) -- [Konami K056320](k056320.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) -- [Konami VRC6](vrc6.md) -- [Atari Lynx](lynx.md) +- [FDS](fds.md) +- [Game Boy](game-boy.md) +- [Generic PCM DAC](dac.md) +- [Irem GA20](ga20.md) +- [Bubble System WSG](bubblesystem.md) +- [K007232](k007232.md) +- [K053260](k056320.md) +- [Lynx](lynx.md) +- [MMC5](mmc5.md) +- [MSM5232](msm5232.md) +- [MSM6258](msm6258.md) +- [MSM6295](msm6295.md) - [Namco 163](n163.md) -- [Namco WSG](namco.md) -- [NES](nes.md) -- [Nintendo MMC5](mmc5.md) -- [OKI MSM5232](msm5232.md) -- [OKI MSM6258](msm6258.md) -- [OKI MSM6295](msm6295.md) -- [PC Engine/TurboGrafx-16](pce.md) +- [Namco WSG/C15/C30](namco.md) +- [HuC6280](pce.md) - [PC Speaker](pcspkr.md) -- [Philips SAA1099](saa1099.md) -- [Pokémon mini](pokemini.md) -- [Capcom QSound](qsound.md) -- [Ricoh RF5C68](ricoh.md) +- [PET](pet.md) +- [Pokémon Mini](pokemini.md) +- [POKEY](pokey.md) +- [PV-1000](pv1000.md) +- [QSound](qsound.md) +- [RF5C68/RF5C164](ricoh.md) +- [SAA1099](saa1099.md) - [SegaPCM](segapcm.md) -- [Seta/Allumer X1-010](x1-010.md) +- [SM8521](sm8521.md) +- [SN76489/Sega PSG](sms.md) - [SNES](snes.md) -- [Atari 2600 (TIA)](tia.md) - [tildearrow Sound Unit](soundunit.md) -- [TI SN76489](sms.md) -- [Toshiba T6W28](t6w28.md) +- [T6W28](t6w28.md) +- [TED](ted.md) +- [TIA](tia.md) - [VERA](vera.md) -- [WonderSwan](wonderswan.md) +- [VIC](vic20.md) - [Virtual Boy](virtual-boy.md) -- [Yamaha YM2413 (OPLL)](opll.md) -- [Yamaha OPL](opl.md) -- [Yamaha YM2151](ym2151.md) -- [Yamaha YM2203](ym2203.md) -- [Yamaha YM2414 (OPZ)](opz.md) -- [Yamaha YM2608](ym2608.md) -- [Neo Geo/YM2610](ym2610.md) -- [Taito Arcade/YM2610B](ym2610b.md) -- [Yamaha YM2612](ym2612.md) -- [Yamaha YMZ280B](ymz280b.md) +- [VRC6](vrc6.md) +- [WonderSwan](wonderswan.md) +- [X1-010](x1-010.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) - [ZX Spectrum Beeper](zxbeep.md) Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but... diff --git a/doc/7-systems/amiga.md b/doc/7-systems/amiga.md index ee197a90..c40d6eed 100644 --- a/doc/7-systems/amiga.md +++ b/doc/7-systems/amiga.md @@ -18,6 +18,8 @@ imported MOD files use this chip, and will set A-4 tuning to 436. # info +this chip uses the [Generic Sample](../4-instrument/amiga.md) instrument editor. + - the maximum rate for sample playback is technically 31469Hz but anything higher than 28867Hz will sound glitchy on hardware. - sample lengths and loop will be set to an even number. - samples can't be longer than 131070. \ No newline at end of file diff --git a/doc/7-systems/ay8910.md b/doc/7-systems/ay8910.md index 0b9d759a..8c88d704 100644 --- a/doc/7-systems/ay8910.md +++ b/doc/7-systems/ay8910.md @@ -47,7 +47,11 @@ as of Furnace 0.6pre7, AY-3-8910 supports software sample playback, where all 3 - `2Fxx`: **write to I/O port B.** - this changes the port's mode to "write". make sure you have connected something to it. -# chip config +# info + +this chip uses the [AY-3-8910](../4-instrument/ay8910.md) instrument editor. + + ## AY derivative modes AY-3-810 was an absurdly popular chip that was blessed with many third-party clones, licensed or not. diff --git a/doc/7-systems/ay8930.md b/doc/7-systems/ay8930.md index 1a926ebc..545e8fd0 100644 --- a/doc/7-systems/ay8930.md +++ b/doc/7-systems/ay8930.md @@ -55,3 +55,7 @@ as of Furnace 0.6pre7, AY8930 supports software PCM, where all 3 channels can pl - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. + +# info + +this chip uses the [AY8930](../4-instrument/8930.md) instrument editor. diff --git a/doc/7-systems/bubblesystem.md b/doc/7-systems/bubblesystem.md index c04aa098..946fd61d 100644 --- a/doc/7-systems/bubblesystem.md +++ b/doc/7-systems/bubblesystem.md @@ -11,3 +11,7 @@ Furnace emulates this configuration as a "chip" with 32×16 wavetables. # effects - `10xx`: **change wave.** + +# info + +this chip uses the [Konami SCC/ Bubble System WSG](../4-instrument/scc.md) instrument editor. diff --git a/doc/7-systems/c64.md b/doc/7-systems/c64.md index 7627f3d9..04035df5 100644 --- a/doc/7-systems/c64.md +++ b/doc/7-systems/c64.md @@ -57,3 +57,7 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro - `6`: disable channel 3 (`y` is `0` or `1`) - `3xxx`: **set duty cycle.** `xxx` range is `000` to `FFF`. - `4xxx`: **set cutoff.** `xxx` range is `000` to `7FF`. + +# info + +this chip uses the [C64](../4-instrument/c64.md) instrument editor. diff --git a/doc/7-systems/dac.md b/doc/7-systems/dac.md index 60bdd96c..3c6445db 100644 --- a/doc/7-systems/dac.md +++ b/doc/7-systems/dac.md @@ -7,3 +7,7 @@ with it, you can emulate PCM DACs found in Williams arcade boards, Sound Blaster # effects none yet. + +# info + +this chip uses the [Generic Sample](../4-instrument/amiga.md) instrument editor. diff --git a/doc/7-systems/es5506.md b/doc/7-systems/es5506.md index 436aca0c..e36eff1c 100644 --- a/doc/7-systems/es5506.md +++ b/doc/7-systems/es5506.md @@ -39,3 +39,7 @@ it supports a whopping 32 channels of 16-bit PCM and: - `8Axx`: **set panning (rear right channel).** - `9xxx`: **set sample offset.** resets sample position to `xxx * 0x100`. - `DFxx`: **set sample playback direction.** + +# info + +this chip uses the [ES5506](../4-instrument/es5506.md) instrument editor. diff --git a/doc/7-systems/fds.md b/doc/7-systems/fds.md index 8105d107..f26ccef9 100644 --- a/doc/7-systems/fds.md +++ b/doc/7-systems/fds.md @@ -25,3 +25,7 @@ it also offers an additional 6-bit, 64-byte wavetable sound channel with (somewh - 6: -2 - 7: -1 - why is this mapping so unnatural? because that's how DefleMask does it (yeah, as you may have guessed this effect is mostly for compatibility reasons)... + +# info + +this chip uses the [FDS](../4-instrument/fds.md) instrument editor. diff --git a/doc/7-systems/ga20.md b/doc/7-systems/ga20.md new file mode 100644 index 00000000..87556487 --- /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/game-boy.md b/doc/7-systems/game-boy.md index eed8241a..b2b624db 100644 --- a/doc/7-systems/game-boy.md +++ b/doc/7-systems/game-boy.md @@ -21,6 +21,11 @@ with stereo sound, two pulse channels, a wave channel and a noise channel, it pa - set to `0` to disable it. - `14xx`: **set sweep direction.** `0` is up and `1` is down. +# info + +this chip uses the [Game Boy](../4-instrument/game-boy.md) instrument editor. + + # links - [Gameboy sound hardware](https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware) - detailed technical information diff --git a/doc/7-systems/k007232.md b/doc/7-systems/k007232.md index f5571e1e..f7e9d69c 100644 --- a/doc/7-systems/k007232.md +++ b/doc/7-systems/k007232.md @@ -9,3 +9,7 @@ it has 7 bit digital output per each channel and no volume register on chip, so # effects - nothing for now. + +# info + +this chip uses the [K007232](../4-instrument/k007232.md) instrument editor. diff --git a/doc/7-systems/k056320.md b/doc/7-systems/k056320.md index bf659582..9e2e162f 100644 --- a/doc/7-systems/k056320.md +++ b/doc/7-systems/k056320.md @@ -1,9 +1,13 @@ # 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. # effects - `DFxx`: Set sample playback direction. `0` is normal; `1` is reverse. + +# info + +this chip uses the [K056320](../4-instrument/k056320.md) instrument editor. diff --git a/doc/7-systems/lynx.md b/doc/7-systems/lynx.md index 24f32634..e3bb087d 100644 --- a/doc/7-systems/lynx.md +++ b/doc/7-systems/lynx.md @@ -17,3 +17,7 @@ the Atari Lynx has a 6502-based CPU with a sound part (this chip is known as MIK - `3xxx`: **load LFSR.** this is a bitmask with values ranging from `000` to `FFF`. - for it to work, duty macro in instrument editor must be set to some value. without it LFSR will not be fed with any bits. + +# info + +this chip uses the [Atari Lynx](../4-instrument/lynx.md) instrument editor. diff --git a/doc/7-systems/mmc5.md b/doc/7-systems/mmc5.md index a838dfe8..90d8c569 100644 --- a/doc/7-systems/mmc5.md +++ b/doc/7-systems/mmc5.md @@ -10,3 +10,7 @@ additionally, it offers an 8-bit DAC which can be used to play samples. only one - `12xx`: **set duty cycle or noise mode of channel.** - may be `0` through `3` for the pulse channels. + +# info + +this chip uses the [NES](../4-instrument/nes.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/msm5232.md b/doc/7-systems/msm5232.md index 0f28cb78..17ee9ee0 100644 --- a/doc/7-systems/msm5232.md +++ b/doc/7-systems/msm5232.md @@ -24,3 +24,7 @@ Furnace implements this chip in a way that allows the following features: - only in internal (capacitor-based) envelope mode. - `13xx`: **set group decay.** range is `0` to `11`. - only in internal (capacitor-based) envelope mode. + +# info + +this chip uses the [MSM5232](../4-instrument/msm5232.md) instrument editor. diff --git a/doc/7-systems/msm6258.md b/doc/7-systems/msm6258.md index a7b8a21f..9cabf140 100644 --- a/doc/7-systems/msm6258.md +++ b/doc/7-systems/msm6258.md @@ -18,3 +18,7 @@ MSM6258 is an extremely basic ADPCM sound codec. it has no variable frequency ra | 4.096 MHz | 8000 Hz | | 8 MHz | 15625 Hz | | 8.192 MHz | 16000 Hz | + +# info + +this chip uses the [MSM6258](../4-instrument/msm6258.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/msm6295.md b/doc/7-systems/msm6295.md index c130854e..9906de6b 100644 --- a/doc/7-systems/msm6295.md +++ b/doc/7-systems/msm6295.md @@ -6,9 +6,15 @@ an upgrade from 6258 - it provides 4 ADPCM channels, at max 32 KHz (still no var - `20xx`: **set chip output rate.** -# chip config + +# info + +this chip uses the [MSM6295](../4-instrument/msm6295.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. + + ## chip clock rates -like MSM6258, MSM295 is an extremely basic ADPCM sound codec. it has no variable frequency rate, it depends on clock rate of a chip itself. Furnace supports following rates: + +like MSM6258, MSM6295 is an extremely basic ADPCM sound codec. it has no variable frequency rate, it depends on clock rate of a chip itself. Furnace supports following rates: | clock rate | sampling rate | |--------------------|---------------| diff --git a/doc/7-systems/n163.md b/doc/7-systems/n163.md index e6645637..af1bffbe 100644 --- a/doc/7-systems/n163.md +++ b/doc/7-systems/n163.md @@ -38,3 +38,7 @@ if the waveform changes (e.g. ins change, wave macro or wave synth), or the **lo - the length is determined by the wave's width (it will be snapped to a multiple of 4 if it isn't). - make sure to use `21xx` first! - `21xx`: **set position for 20xx.** + +# info + +this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor. diff --git a/doc/7-systems/namco.md b/doc/7-systems/namco.md index 54d8b964..ffa5817b 100644 --- a/doc/7-systems/namco.md +++ b/doc/7-systems/namco.md @@ -8,3 +8,7 @@ everything starts with Namco WSG, which is a simple 3-channel wavetable with no - `10xx`: **change waveform.** - `11xx`: **toggle noise mode.** _warning:_ only on C30. + +# info + +this chip uses the [Namco WSG](../4-instrument/namco.md) instrument editor. diff --git a/doc/7-systems/nes.md b/doc/7-systems/nes.md index 2b81c2a8..28df6067 100644 --- a/doc/7-systems/nes.md +++ b/doc/7-systems/nes.md @@ -56,7 +56,11 @@ also known as Famicom. it is a five-channel sound generator: first two channels - only works in DPCM mode. - see table below for possible values. -# tables + +# info + +this chip uses the [NES](../4-instrument/nes.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. + ## short noise frequencies (NTSC) diff --git a/doc/7-systems/opl.md b/doc/7-systems/opl.md index 5f7034cb..ac82ded0 100644 --- a/doc/7-systems/opl.md +++ b/doc/7-systems/opl.md @@ -78,3 +78,7 @@ afterwards everyone moved to Windows and software mixed PCM streaming... - `5Bxy`: **set KSR of operator.** - `x` is the operator from 1 to 4; the last 2 operators only work in 4-op mode. a value of `0` means "all operators". - `y` determines whether KSR is on. + +# info + +this chip uses the [FM (OPL)](../4-instrument/fm-opl.md) instrument editor. diff --git a/doc/7-systems/opll.md b/doc/7-systems/opll.md index c5dd4ff6..92a81490 100644 --- a/doc/7-systems/opll.md +++ b/doc/7-systems/opll.md @@ -65,3 +65,7 @@ the YM2413 is equipped with the following features: - `5Bxy`: **set KSR of operator.** - `x` is the operator, either 1 or 2. a value of `0` means "all operators". - `y` determines whether KSR is on. + +# info + +this chip uses the [FM (OPLL)](../4-instrument/fm-opll.md) instrument editor. diff --git a/doc/7-systems/opz.md b/doc/7-systems/opz.md index 1abef5c4..c4b0413a 100644 --- a/doc/7-systems/opz.md +++ b/doc/7-systems/opz.md @@ -113,3 +113,7 @@ no plans have been made for TX81Z MIDI passthrough, because: - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** + +# info + +this chip uses the [FM (OPZ)](../4-instrument/opz.md) instrument editor. diff --git a/doc/7-systems/pce.md b/doc/7-systems/pce.md index 80f4af10..308d42e4 100644 --- a/doc/7-systems/pce.md +++ b/doc/7-systems/pce.md @@ -18,5 +18,9 @@ furthermore, it has some PCM and LFO! - `03`: LFO enabled, shift 8. - when LFO is enabled, channel 2 is muted and its output is passed to channel 1's frequency. - `13xx`: **set LFO speed.** -- `17xx`: **toggle PCM mode.** - - _this effect is here for compatibility reasons_; it is otherwise recommended to use Sample type instruments (which automatically enable PCM mode when used). +- `17xx`: **toggle LEGACY sample mode.** + - **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.** + +# info + +this chip uses the [PC Engine](../4-instrument/pce.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/pcspkr.md b/doc/7-systems/pcspkr.md index a6a55ba6..d84181bf 100644 --- a/doc/7-systems/pcspkr.md +++ b/doc/7-systems/pcspkr.md @@ -29,3 +29,7 @@ oh, and of course you also need the beeper to be present in your machine. some l # effects ha! effects... + +# info + +this chip uses the [Beeper](../4-instrument/beeper.md) instrument editor. diff --git a/doc/7-systems/pet.md b/doc/7-systems/pet.md index 9b7e3b5b..e99e1792 100644 --- a/doc/7-systems/pet.md +++ b/doc/7-systems/pet.md @@ -9,3 +9,7 @@ some of these didn't even have sound... # effects - `10xx`: **set waveform.** `xx` is a bitmask. + +# info + +this chip uses the [PET](../4-instrument/pet.md) instrument editor. diff --git a/doc/7-systems/pokemini.md b/doc/7-systems/pokemini.md index 142d4965..7633f27a 100644 --- a/doc/7-systems/pokemini.md +++ b/doc/7-systems/pokemini.md @@ -5,3 +5,7 @@ the Pokémon Mini is a ridiculously small handheld system from 2001. its single # effects none. + +# info + +this chip uses the [Pokémon Mini/QuadTone](../4-instrument/quadtone.md) instrument editor. diff --git a/doc/7-systems/pokey.md b/doc/7-systems/pokey.md index 20d75f1e..31832e58 100644 --- a/doc/7-systems/pokey.md +++ b/doc/7-systems/pokey.md @@ -35,3 +35,7 @@ a sound and input chip developed by Atari for their 8-bit computers (Atari 400, - `12xx`: **toggle two-tone mode.** - when enabled, channel 2 modulates channel 1. I don't know how, but it does. - only on ASAP core. + +# info + +this chip uses the [POKEY](../4-instrument/pokey.md) instrument editor. diff --git a/doc/7-systems/pv1000.md b/doc/7-systems/pv1000.md index 43abbfea..97cb617b 100644 --- a/doc/7-systems/pv1000.md +++ b/doc/7-systems/pv1000.md @@ -5,3 +5,7 @@ released only in Japan, this console was pulled after only a few weeks on the ma # effects - `10xx`: **set ring modulation.** amplitude modulation by the previous channel's output. `0` turns it off and `1` turns it on. + +# info + +this chip uses the [PV-1000](../4-instrument/pv1000.md) instrument editor. diff --git a/doc/7-systems/qsound.md b/doc/7-systems/qsound.md index 07651a72..6aa57d36 100644 --- a/doc/7-systems/qsound.md +++ b/doc/7-systems/qsound.md @@ -17,3 +17,7 @@ there are also 3 ADPCM channels. ADPCM samples are fixed to 8012 Hz. - `11xx`: **set echo level.** - `12xx`: **toggle QSound algorithm.** on by default. - `3xxx`: **set echo delay buffer length.** + +# info + +this chip uses the [QSound](../4-instrument/qsound.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/ricoh.md b/doc/7-systems/ricoh.md index 51832602..1c3695ca 100644 --- a/doc/7-systems/ricoh.md +++ b/doc/7-systems/ricoh.md @@ -6,3 +6,6 @@ YM2612's sidekick - poor man's SNES DSP. 8-channel PCM sample-based synthesizer none so far. +# info + +this chip uses the [RF5C68](../4-instrument/ricoh.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/saa1099.md b/doc/7-systems/saa1099.md index 6b2624e4..8036ec71 100644 --- a/doc/7-systems/saa1099.md +++ b/doc/7-systems/saa1099.md @@ -27,3 +27,7 @@ this was used by the Game Blaster and SAM Coupé. it's pretty similar to the AY- - `111`: up loop (reverse saw) - bit 0 sets whether the right output will mirror the left one. - this effect affects either the first 3 or last 3 channels, depending on where it is placed. + +# info + +this chip uses the [SAA1099](../4-instrument/saa.md) instrument editor. diff --git a/doc/7-systems/scc.md b/doc/7-systems/scc.md index 19a0736c..8e5b0147 100644 --- a/doc/7-systems/scc.md +++ b/doc/7-systems/scc.md @@ -9,3 +9,7 @@ the SCC+ fixes this issue though (while being compatible with SCC games). # effects - `10xx`: **change wave.** + +# info + +this chip uses the [Konami SCC/Bubble System WSG](../4-instrument/scc.md) instrument editor. diff --git a/doc/7-systems/segapcm.md b/doc/7-systems/segapcm.md index 3272699d..296f2a88 100644 --- a/doc/7-systems/segapcm.md +++ b/doc/7-systems/segapcm.md @@ -15,3 +15,7 @@ Furnace also has a five channel version of this chip, but it only exists for Def - `20xx`: **set PCM frequency.** - `xx` is a 256th fraction of 31250Hz. - this effect exists mostly for DefleMask compatibility; it is otherwise recommended to use Sample type instruments. + +# info + +this chip uses the [SegaPCM](../4-instrument/segapcm.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/sm8521.md b/doc/7-systems/sm8521.md index a5a6e024..c29ff79a 100644 --- a/doc/7-systems/sm8521.md +++ b/doc/7-systems/sm8521.md @@ -15,9 +15,13 @@ the sound-related features and quirks of the SM8521 are as follows: - a low bit-depth output (which means it distorts a lot). - it phase resets when you switch waves - 12-bit pitch with a wide frequency range -- a software-controlled D/A register that (potentially) requires all other registers to be stopped to play. due to this, it is currently, it is not implemented in Furnace as of version 0.6pre4. +- a software-controlled D/A register that (potentially) requires all other registers to be stopped to play. due to this, it is currently not implemented in Furnace as of version 0.6pre4. ## effect commands - `10xx`: **set waveform.** - `xx` is a value between 0 and 255 that sets the waveform of the channel you place it on. + +# info + +this chip uses the [SM8521](../4-instrument/sm8521.md) instrument editor. diff --git a/doc/7-systems/sms.md b/doc/7-systems/sms.md index 528de420..36d13976 100644 --- a/doc/7-systems/sms.md +++ b/doc/7-systems/sms.md @@ -1,4 +1,4 @@ -# TI SN76489 (e.g. sega Master System) +# TI SN76489 (e.g. Sega Master System) a relatively simple sound chip made by Texas Instruments. a derivative of it is used in Sega's Master System, the predecessor to Genesis. @@ -14,7 +14,12 @@ the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624 - `0`: thin pulse. - `1`: noise. -# chip config + +# info + +this chip uses the [SN76489/Sega PSG](../4-instrument/psg.md) instrument editor. + + ## SN7 versions SN7 was extremely popular due to low cost. Therefore, it was cloned and copied to no end, often with minor differences between each other. Furnace supports several of these: - SN94624, can only produce tones as low as 100Hz, and is clocked at 447 KHz. diff --git a/doc/7-systems/snes.md b/doc/7-systems/snes.md index 9888291c..9c57d752 100644 --- a/doc/7-systems/snes.md +++ b/doc/7-systems/snes.md @@ -67,7 +67,10 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i - _Note:_ Be sure the sum of all coefficients is between -128 and 127. sums outside that may result in overflow and therefore clicking. - see [SnesLab](https://sneslab.net/wiki/FIR_Filter) for a full explanation and examples. -# tables +# info + +this chip uses the [SNES](../4-instrument/snes.md) instrument editor. + ## ADSR diff --git a/doc/7-systems/soundunit.md b/doc/7-systems/soundunit.md index 5b483813..00706c0f 100644 --- a/doc/7-systems/soundunit.md +++ b/doc/7-systems/soundunit.md @@ -54,3 +54,7 @@ it has the following capabilities: - bit 0-6: speed - bit 7: up direction - `4xxx`: **set cutoff.** range is `0` to `FFF`. + +# info + +this chip uses the [Sound Unit](../4-instrument/soundunit.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/t6w28.md b/doc/7-systems/t6w28.md index 886ea63b..26d7d977 100644 --- a/doc/7-systems/t6w28.md +++ b/doc/7-systems/t6w28.md @@ -9,3 +9,7 @@ this chip was used in Neo Geo Pocket. - `20xx`: **set noise mode.** - `0`: thin pulse. - `1`: noise. + +# info + +this chip uses the [T6W28](../4-instrument/t6w28.md) instrument editor. diff --git a/doc/7-systems/ted.md b/doc/7-systems/ted.md new file mode 100644 index 00000000..4333fcea --- /dev/null +++ b/doc/7-systems/ted.md @@ -0,0 +1,14 @@ +# MOS Technology TED + +also called 7360/8360, TED stands for Text Editing Device. it's both a video and audio chip of Commodore budget computers, like Plus/4 and 16. + +its audio portion is pretty barren - only 2 channels. one can output square wave and other may be either square or noise. +pitch range is limited as well, akin to that of SN76489, and volume control is global. + +# effects + +none so far. + +# info + +this chip uses the [TED](../4-instrument/ted.md) instrument editor. diff --git a/doc/7-systems/tia.md b/doc/7-systems/tia.md index b39d31db..8b89616d 100644 --- a/doc/7-systems/tia.md +++ b/doc/7-systems/tia.md @@ -25,4 +25,274 @@ Furnace isn't complete without this one... - `C`: low square - `D`: low square - `E`: low pure buzzy - - `F`: low reedy \ No newline at end of file + - `F`: low reedy + + + +# info + +this chip uses the [TIA](../4-instrument/tia.md) instrument editor. + + +- pitch number can be used for absolute notes in arpeggio macros. + +## shape 1 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 2096.0 | C-7 | +2 | 2080.0 | C-7 | -1 +| 1 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -1 +| 2 | 698.7 | F-5 | 0.0| 693.3 | F-5 | -1 +| 3 | 524.0 | C-5 | +2 | 520.0 | C-5 | -1 +| 4 | 419.2 | G#4 | +16 | 416.0 | G#4 | +3 +| 5 | 349.3 | F-4 | 0.0| 346.7 | F-4 | -13 +| 6 | 299.4 | D-4 | +33 | 297.1 | D-4 | +20 +| 7 | 262.0 | C-4 | +3 | 260.0 | C-4 | -11 +| 8 | 232.9 | A#3 | -2 | 231.1 | A#3 | -15 +| 9 | 209.6 | G#3 | +15 | 208.0 | G#3 | +2 +| 10 | 190.5 | F#3 | +50 | 189.1 | F#3 | +37 +| 11 | 174.7 | F-3 | +1 | 173.3 | F-3 | -13 +| 12 | 161.2 | E-3 | -39 | 160.0 | D#3 | +48 +| 13 | 149.7 | D-3 | +33 | 148.6 | D-3 | +20 +| 14 | 139.7 | C#3 | +13 | 138.7 | C#3 | +1 +| 15 | 131.0 | C-3 | +3 | 130.0 | C-3 | -11 +| 16 | 123.3 | B-2 | -3 | 122.4 | B-2 | -16 +| 17 | 116.4 | A#2 | 0.0| 115.6 | A#2 | -14 +| 18 | 110.3 | A-2 | +5 | 109.5 | A-2 | -8 +| 19 | 104.8 | G#2 | +16 | 104.0 | G#2 | +3 +| 20 | 99.8 | G-2 | +31 | 99.0 | G-2 | +17 +| 21 | 95.3 | G-2 | -49 | 94.5 | F#2 | +36 +| 22 | 91.1 | F#2 | -27 | 90.4 | F#2 | -40 +| 23 | 87.3 | F-2 | 0.0| 86.7 | F-2 | -12 +| 24 | 83.8 | E-2 | +29 | 83.2 | E-2 | +16 +| 25 | 80.6 | E-2 | -39 | 80.0 | D#2 | +48 +| 26 | 77.6 | D#2 | -5 | 77.0 | D#2 | -18 +| 27 | 74.9 | D-2 | +34 | 74.3 | D-2 | +20 +| 28 | 72.3 | D-2 | -27 | 71.7 | D-2 | -41 +| 29 | 69.9 | C#2 | +15 | 69.3 | C#2 | 0 +| 30 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44 +| 31 | 65.5 | C-2 | +3 | 65.0 | C-2 | -11 + +## shapes 2, 3 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44 +| 1 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42 +| 2 | 22.5 | F#0 | -46 | 22.4 | F-0 | +46 +| 3 | 16.9 | C#0 | -44 | 16.8 | C-0 | +44 +| 4 | 13.5 | | | 13.4 +| 5 | 11.3 | | | 11.2 +| 6 | 9.7 | | | 9.6 +| 7 | 8.5 | | | 8.4 +| 8 | 7.5 | | | 7.5 +| 9 | 6.8 | | | 6.7 +| 10 | 6.1 | | | 6.1 +| 11 | 5.6 | | | 5.6 +| 12 | 5.2 | | | 5.2 +| 13 | 4.8 | | | 4.8 +| 14 | 4.5 | | | 4.5 +| 15 | 4.2 | | | 4.2 +| 16 | 4.0 | | | 4.0 +| 17 | 3.8 | | | 3.7 +| 18 | 3.6 | | | 3.5 +| 19 | 3.4 | | | 3.4 +| 20 | 3.2 | | | 3.2 +| 21 | 3.1 | | | 3.0 +| 22 | 3.0 | | | 2.9 +| 23 | 2.8 | | | 2.8 +| 24 | 2.7 | | | 2.7 +| 25 | 2.6 | | | 2.6 +| 26 | 2.5 | | | 2.5 +| 27 | 2.4 | | | 2.4 +| 28 | 2.3 | | | 2.3 +| 29 | 2.3 | | | 2.2 +| 30 | 2.2 | | | 2.2 +| 31 | 2.1 | | | 2.1 + +## shapes 4, 5 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 15720.0 | B-9 | -9 | 15600.0 | B-9 | -23 +| 1 | 7860.0 | B-8 | -9 | 7800.0 | B-8 | -23 +| 2 | 5240.0 | E-8 | -11 | 5200.0 | E-8 | -25 +| 3 | 3930.0 | B-7 | -10 | 3900.0 | B-7 | -23 +| 4 | 3144.0 | G-7 | +4 | 3120.0 | G-7 | -9 +| 5 | 2620.0 | E-7 | -11 | 2600.0 | E-7 | -25 +| 6 | 2245.7 | C#7 | +21 | 2228.6 | C#7 | +8 +| 7 | 1965.0 | B-6 | -9 | 1950.0 | B-6 | -23 +| 8 | 1746.7 | A-6 | -13 | 1733.3 | A-6 | -27 +| 9 | 1572.0 | G-6 | +4 | 1560.0 | G-6 | -9 +| 10 | 1429.1 | F-6 | +39 | 1418.2 | F-6 | +25 +| 11 | 1310.0 | E-6 | -11 | 1300.0 | E-6 | -25 +| 12 | 1209.2 | D-6 | +49 | 1200.0 | D-6 | +36 +| 13 | 1122.9 | C#6 | +22 | 1114.3 | C#6 | +8 +| 14 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -11 +| 15 | 982.5 | B-5 | -10 | 975.0 | B-5 | -23 +| 16 | 924.7 | A#5 | -15 | 917.6 | A#5 | -28 +| 17 | 873.3 | A-5 | -14 | 866.7 | A-5 | -27 +| 18 | 827.4 | G#5 | -7 | 821.1 | G#5 | -20 +| 19 | 786.0 | G-5 | +4 | 780.0 | G-5 | -9 +| 20 | 748.6 | F#5 | +20 | 742.9 | F#5 | +7 +| 21 | 714.5 | F-5 | +39 | 709.1 | F-5 | +26 +| 22 | 683.5 | F-5 | -38 | 678.3 | E-5 | +48 +| 23 | 655.0 | E-5 | -12 | 650.0 | E-5 | -25 +| 24 | 628.8 | D#5 | +18 | 624.0 | D#5 | +5 +| 25 | 604.6 | D-5 | +49 | 600.0 | D-5 | +36 +| 26 | 582.2 | D-5 | -16 | 577.8 | D-5 | -29 +| 27 | 561.4 | C#5 | +21 | 557.1 | C#5 | +8 +| 28 | 542.1 | C#5 | -40 | 537.9 | C-5 | +47 +| 29 | 524.0 | C-5 | +2 | 520.0 | C-5 | -11 +| 30 | 507.1 | B-4 | +45 | 503.2 | B-4 | +32 +| 31 | 491.3 | B-4 | -9 | 487.5 | B-4 | -23 + +## shapes 6, 7, 9, 10 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 1014.2 | B-5 | +45 | 1006.5 | B-5 | +32 +| 1 | 507.1 | B-4 | +45 | 503.2 | B-4 | +32 +| 2 | 338.1 | E-4 | +43 | 335.5 | E-4 | +30 +| 3 | 253.5 | B-3 | +45 | 251.6 | B-3 | +32 +| 4 | 202.8 | G#3 | -42 | 201.3 | G-3 | +45 +| 5 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30 +| 6 | 144.9 | D-3 | -23 | 143.8 | D-3 | -37 +| 7 | 126.8 | B-2 | +42 | 125.8 | B-2 | +32 +| 8 | 112.7 | A-2 | +42 | 111.8 | A-2 | +28 +| 9 | 101.4 | G#2 | -41 | 100.6 | G-2 | +45 +| 10 | 92.2 | F#2 | -6 | 91.5 | F#2 | -19 +| 11 | 84.5 | E-2 | +43 | 83.9 | E-2 | +31 +| 12 | 78.0 | D#2 | +4 | 77.4 | D#2 | -9 +| 13 | 72.4 | D-2 | -24 | 71.9 | D-2 | -37 +| 14 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44 +| 15 | 63.4 | B-1 | +46 | 62.9 | B-1 | +32 +| 16 | 59.7 | A#1 | +41 | 59.2 | A#1 | +26 +| 17 | 56.3 | A-1 | +39 | 55.9 | A-1 | +27 +| 18 | 53.4 | G#1 | +48 | 53.0 | G#1 | +35 +| 19 | 50.7 | G#1 | -41 | 50.3 | G-1 | +45 +| 20 | 48.3 | G-1 | -25 | 47.9 | G-1 | -39 +| 21 | 46.1 | F#1 | -4 | 45.7 | F#1 | -20 +| 22 | 44.1 | F-1 | +16 | 43.8 | F-1 | +4 +| 23 | 42.3 | E-1 | +44 | 41.9 | E-1 | +28 +| 24 | 40.6 | E-1 | -26 | 40.3 | E-1 | -39 +| 25 | 39.0 | D#1 | +4 | 38.7 | D#1 | -9 +| 26 | 37.6 | D-1 | +41 | 37.3 | D-1 | +27 +| 27 | 36.2 | D-1 | -24 | 35.9 | D-1 | -38 +| 28 | 35.0 | C#1 | +19 | 34.7 | C#1 | +5 +| 29 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42 +| 30 | 32.7 | C-1 | 0.0 | 32.5 | C-1 | -11 +| 31 | 31.7 | B-0 | +44 | 31.5 | B-0 | +33 + +## shapes 8 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 61.5 | B-1 | -6| 61.1 | B-1 | -18 +| 1 | 30.8 | B-0 | -6| 30.5 | B-0 | -22 +| 2 | 20.5 | E-0 | -8| 20.4 | E-0 | -17 +| 3 | 15.4 | | | 15.3 +| 4 | 12.3 | | | 12.2 +| 5 | 10.3 | | | 10.2 +| 6 | 8.8 | | | 8.7 +| 7 | 7.7 | | | 7.6 +| 8 | 6.8 | | | 6.8 +| 9 | 6.2 | | | 6.1 +| 10 | 5.6 | | | 5.6 +| 11 | 5.1 | | | 5.1 +| 12 | 4.7 | | | 4.7 +| 13 | 4.4 | | | 4.4 +| 14 | 4.1 | | | 4.1 +| 15 | 3.8 | | | 3.8 +| 16 | 3.6 | | | 3.6 +| 17 | 3.4 | | | 3.4 +| 18 | 3.2 | | | 3.2 +| 19 | 3.1 | | | 3.1 +| 20 | 2.9 | | | 2.9 +| 21 | 2.8 | | | 2.8 +| 22 | 2.7 | | | 2.7 +| 23 | 2.6 | | | 2.5 +| 24 | 2.5 | | | 2.4 +| 25 | 2.4 | | | 2.3 +| 26 | 2.3 | | | 2.3 +| 27 | 2.2 | | | 2.2 +| 28 | 2.1 | | | 2.1 +| 29 | 2.0 | | | 2.0 +| 30 | 2.0 | | | 2.0 +| 31 | 1.9 | | | 1.9 + +## shapes 12, 13 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 5240.0 | E-8 | -11 | 5200.0 | E-8 | -25 +| 1 | 2620.0 | E-7 | -11 | 2600.0 | E-7 | -25 +| 2 | 1746.6 | A-6 | -14 | 1733.3 | A-6 | -27 +| 3 | 1310.0 | E-6 | -11 | 1300.0 | E-6 | -25 +| 4 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -11 +| 5 | 873.3 | A-5 | -14 | 866.7 | A-5 | -27 +| 6 | 748.6 | F#5 | +20 | 742.9 | F#5 | +7 +| 7 | 655.0 | E-5 | -12 | 650.0 | E-5 | -25 +| 8 | 582.2 | D-5 | -16 | 577.8 | D-5 | -29 +| 9 | 524.0 | C-5 | +2 | 520.0 | C-5 | -11 +| 10 | 476.4 | A#4 | +39 | 472.7 | A#4 | +23 +| 11 | 436.7 | A-4 | -13 | 433.3 | A-4 | -27 +| 12 | 403.1 | G-4 | +48 | 400.0 | G-4 | +34 +| 13 | 374.3 | F#4 | +20 | 371.4 | F#4 | +6 +| 14 | 349.3 | F-4 | 0.0 | 346.7 | F-4 | -13 +| 15 | 327.5 | E-4 | -11 | 325.0 | E-4 | -25 +| 16 | 308.2 | D#4 | -17 | 305.9 | D#4 | -30 +| 17 | 291.1 | D-4 | -16 | 288.9 | D-4 | -29 +| 18 | 275.8 | C#4 | -9 | 273.7 | C#4 | -22 +| 19 | 262.0 | C-4 | +3 | 260.0 | C-4 | -11 +| 20 | 249.5 | B-3 | +18 | 247.6 | B-3 | +5 +| 21 | 238.2 | A#3 | +37 | 236.4 | A#3 | +24 +| 22 | 227.8 | A#3 | -40 | 226.1 | A-3 | +47 +| 23 | 218.3 | A-3 | -14 | 216.7 | A-3 | -27 +| 24 | 209.6 | G#3 | +15 | 208.0 | G#3 | +2 +| 25 | 201.5 | G-3 | +47 | 200.0 | G-3 | +34 +| 26 | 194.1 | G-3 | -17 | 192.6 | G-3 | -31 +| 27 | 187.1 | F#3 | +19 | 185.7 | F#3 | +6 +| 28 | 180.7 | F#3 | -41 | 179.3 | F-3 | +45 +| 29 | 174.7 | F-3 | +1 | 173.3 | F-3 | -13 +| 30 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30 +| 31 | 163.8 | E-3 | -11 | 162.5 | E-3 | -25 + +## shapes 14, 15 + +| pitch | NTSC | note | cent | PAL | note | cent +|------:|--------:|:----:|-----:|--------:|:----:|-----: +| 0 | 338.1 | E-4 | +43 | 335.5 | E-4 | +30 +| 1 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30 +| 2 | 112.7 | A-2 | +42 | 111.8 | A-2 | +28 +| 3 | 84.5 | E-2 | +43 | 83.9 | E-2 | +31 +| 4 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44 +| 5 | 56.3 | A-1 | +39 | 55.9 | A-1 | +27 +| 6 | 48.3 | G-1 | -25 | 47.9 | G-1 | -39 +| 7 | 42.3 | E-1 | +44 | 41.9 | E-1 | +28 +| 8 | 37.6 | D-1 | +41 | 37.3 | D-1 | +27 +| 9 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42 +| 10 | 30.7 | B-0 | -11 | 30.5 | B-0 | -22 +| 11 | 28.2 | A-0 | +44 | 28.0 | A-0 | +31 +| 12 | 26.0 | G#0 | 0.0 | 25.8 | G#0 | -13 +| 13 | 24.1 | G-0 | -29 | 24.0 | G-0 | -36 +| 14 | 22.5 | F#0 | -46 | 22.4 | F-0 | +46 +| 15 | 21.1 | E-0 | +42 | 21.0 | E-0 | +33 +| 16 | 19.9 | D#0 | +42 | 19.7 | D#0 | +25 +| 17 | 18.8 | D-0 | +40 | 18.6 | D-0 | +20 +| 18 | 17.8 | C#0 | +45 | 17.7 | C#0 | +36 +| 19 | 16.9 | C#0 | -44 | 16.8 | C-0 | +44 +| 20 | 16.1 | C-0 | -30 | 16.0 | C-0 | -40 +| 21 | 15.4 | | | 15.2 +| 22 | 14.7 | | | 14.6 +| 23 | 14.1 | | | 14 +| 24 | 13.5 | | | 13.4 +| 25 | 13.0 | | | 12.9 +| 26 | 12.5 | | | 12.4 +| 27 | 12.1 | | | 12 +| 28 | 11.7 | | | 11.6 +| 29 | 11.3 | | | 11.2 +| 30 | 10.9 | | | 10.8 +| 31 | 10.6 | | | 10.5 + +reference: [Atari 2600 VCS Sound Frequency and Waveform Guide](http://7800.8bitdev.org/index.php/Atari_2600_VCS_Sound_Frequency_and_Waveform_Guide) \ No newline at end of file diff --git a/doc/7-systems/vera.md b/doc/7-systems/vera.md index e89744a9..d4189412 100644 --- a/doc/7-systems/vera.md +++ b/doc/7-systems/vera.md @@ -15,3 +15,7 @@ currently Furnace does not support the PCM channel's stereo mode, though (except - `22xx`: **set duty cycle.** range is `0` to `3F`. - `EExx`: **ZSM synchronization event.** - Where `xx` is the event payload. This has no effect in how the music is played in Furnace, but the ZSMKit library for the Commander X16 interprets these events inside ZSM files and optionally triggers a callback routine. This can be used, for instance, to cause game code to respond to beats or at certain points in the music. + +# info + +this chip uses the [VERA](../4-instrument/vera.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/vic20.md b/doc/7-systems/vic20.md index e5126c18..6d5a9bd4 100644 --- a/doc/7-systems/vic20.md +++ b/doc/7-systems/vic20.md @@ -16,3 +16,7 @@ these channels are not referred as "square" wave channels since a technique to p ## effect commands - `10xx`: **switch waveform.** range is `00` to `0F`. + +# info + +this chip uses the [VIC](../4-instrument/vic.md) instrument editor. diff --git a/doc/7-systems/virtual-boy.md b/doc/7-systems/virtual-boy.md index 9ef6c909..8dc11da9 100644 --- a/doc/7-systems/virtual-boy.md +++ b/doc/7-systems/virtual-boy.md @@ -41,3 +41,7 @@ additionally, channel 5 offers a modulation/sweep unit. the former is similar to - `15xx`: **set modulation wave.** - `xx` points to a wavetable. range is `0` to `FF`. - this is an alternative to setting the modulation wave through the instrument. + +# info + +this chip uses the [Virtual Boy](../4-instrument/virtual-boy.md) instrument editor. diff --git a/doc/7-systems/vrc6.md b/doc/7-systems/vrc6.md index d652cc26..ba6df1d2 100644 --- a/doc/7-systems/vrc6.md +++ b/doc/7-systems/vrc6.md @@ -15,4 +15,9 @@ Furnace supports this routine for PCM playback, but it consumes a lot of CPU tim these effects only are effective in the pulse channels. - `12xx`: **set duty cycle.** range is `0` to `7`. -- `17xx`: **toggle PCM mode.** +- `17xx`: **toggle LEGACY sample mode.** + - **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.** + +# info + +this chip uses the [VRC](../4-instrument/vrc6.md), [VRC (saw)](../4-instrument/vrc6.md), and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/wonderswan.md b/doc/7-systems/wonderswan.md index d4bff7c1..e386461b 100644 --- a/doc/7-systems/wonderswan.md +++ b/doc/7-systems/wonderswan.md @@ -19,3 +19,7 @@ it has 4 wavetable channels. some of them have additional capabilities: - 1-32: enable and set period. - `13xx`: **setup sweep amount.** channel 3 only. - `17xx`: **toggle PCM mode.** channel 2 only. + +# info + +this chip uses the [WonderSwan](../4-instrument/wonderswan.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/x1-010.md b/doc/7-systems/x1-010.md index 5f110aba..e4836cf8 100644 --- a/doc/7-systems/x1-010.md +++ b/doc/7-systems/x1-010.md @@ -27,7 +27,8 @@ in Furnace, you can enable the envelope shape split mode. when it is set, its wa - `10xx`: **change wave.** - `11xx`: **change envelope shape.** also wavetable. -- `17xx`: **toggle PCM mode.** +- `17xx`: **toggle LEGACY sample mode.** + - **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.** - `20xx`: **set PCM frequency.** range is `1` to `FF`. - PCM frequency formula: `step * (clock / 8192)`. - range is 1.95KHz to 498KHz if the chip clock is 16MHz. @@ -45,3 +46,7 @@ in Furnace, you can enable the envelope shape split mode. when it is set, its wa - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. + +# info + +this chip uses the [X1-010](../4-instrument/x1_010.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/ym2151.md b/doc/7-systems/ym2151.md index f9bd8751..58b4fd1f 100644 --- a/doc/7-systems/ym2151.md +++ b/doc/7-systems/ym2151.md @@ -68,3 +68,7 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** + +# info + +this chip uses the [FM (OPM)](../4-instrument/fm-opm.md) instrument editor. diff --git a/doc/7-systems/ym2203.md b/doc/7-systems/ym2203.md index f79f95d2..f8fd41fe 100644 --- a/doc/7-systems/ym2203.md +++ b/doc/7-systems/ym2203.md @@ -100,7 +100,12 @@ several variants of this chip were released as well, with more features. - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** -# system modes + +# info + +this chip uses the [FM (OPN)](../4-instrument/fm-opn.md) and [AY-3-8910/SSG](../4-instrument/ay8910.md) instrument editor. + + ## extended channel 3 in ExtCh mode, channel 3 is split into one column for each of its four operators and feedback are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. diff --git a/doc/7-systems/ym2608.md b/doc/7-systems/ym2608.md index 16f3cc7e..51ea15d9 100644 --- a/doc/7-systems/ym2608.md +++ b/doc/7-systems/ym2608.md @@ -100,7 +100,12 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built- - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** -# system modes + +# info + +this chip uses the [FM (OPN)](../4-instrument/fm-opn.md), [Generic Sample](../4-instrument/amiga.md), [AY-3-8910/SSG](../4-instrument/ay8910.md), [ADPCM-A](../4-instrument/adpcm-a.md), and [ADPCM-B](../4-instrument/adpcm-b.md) instrument editors. + + ## extended channel 3 in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. diff --git a/doc/7-systems/ym2610.md b/doc/7-systems/ym2610.md index 02a630b0..b56df33d 100644 --- a/doc/7-systems/ym2610.md +++ b/doc/7-systems/ym2610.md @@ -98,7 +98,11 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and [2 differen - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** -# system modes +# info + +this chip uses the [FM (OPN)](../4-instrument/fm-opn.md), [Generic Sample](../4-instrument/amiga.md), [AY-3-8910/SSG](../4-instrument/ay8910.md), [ADPCM-A](../4-instrument/adpcm-a.md), and [ADPCM-B](../4-instrument/adpcm-b.md) instrument editors. + + ## extended channel 2 in ExtCh mode, channel 2 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. diff --git a/doc/7-systems/ym2610b.md b/doc/7-systems/ym2610b.md index 2911c267..a670cfc2 100644 --- a/doc/7-systems/ym2610b.md +++ b/doc/7-systems/ym2610b.md @@ -97,8 +97,13 @@ it is backward compatible with the original chip. - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** -# system modes +# info + +this chip uses the [FM (OPN)](../4-instrument/fm-opn.md), [Generic Sample](../4-instrument/amiga.md), [AY-3-8910/SSG](../4-instrument/ay8910.md), [ADPCM-A](../4-instrument/adpcm-a.md), and [ADPCM-B](../4-instrument/adpcm-b.md) instrument editors. + + ## extended channel 3 + in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone. diff --git a/doc/7-systems/ym2612.md b/doc/7-systems/ym2612.md index ddb91b78..7804984e 100644 --- a/doc/7-systems/ym2612.md +++ b/doc/7-systems/ym2612.md @@ -16,8 +16,9 @@ as of Furnace 0.6pre5, Furnace offers DualPCM, a Z80 driver that splits channel - `16xy`: **set multiplier of operator.** - `x` is the operator (1-4). - `y` is the multiplier. -- `17xx`: **enable PCM channel.** +- `17xx`: **toggle LEGACY sample mode.** - this only works on channel 6. + - **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.** - `18xx`: **toggle extended channel 3 mode.** - 0 disables it and 1 enables it. - only in extended channel 3 chip. @@ -66,3 +67,24 @@ as of Furnace 0.6pre5, Furnace offers DualPCM, a Z80 driver that splits channel - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** + +# info + +this chip uses the [FM (OPN)](../4-instrument/fm-opn.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. + + +## extended channel 3 + +in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. + +all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone. + +## CSM + +CSM is short for "Composite Sinusoidal Modeling". CSM works by sending key-on and key-off commands to channel 3 at a specific frequency, controlled by the added "CSM Timer" channel. this can be used to create vocal formants (speech synthesis!) or other complex effects. + +CSM is beyond the scope of this documentation. for more information, see this [brief SSG-EG and CSM video tutorial](https://www.youtube.com/watch?v=IKOR0TUlnWU). + +## DualPCM + +thanks to the Z80 sound CPU, DualPCM can play two samples at once! this mode splits channel 6 into two individual PCM channels with variable pitch. these are mixed together in software and streamed to channel 6 with a mix rate of 13750 Hz. VGM export requires the "direct stream mode" option to be enabled, and resulting files will be very large. diff --git a/doc/7-systems/ymu759.md b/doc/7-systems/ymu759.md index 33984832..956b37ac 100644 --- a/doc/7-systems/ymu759.md +++ b/doc/7-systems/ymu759.md @@ -15,3 +15,7 @@ even DefleMask dropped support for the chip in version 0.11, which just shows ho hey, at least it was the spark that ignited the idea of DefleMask. + +# info + +this chip uses the [FM (OPL)](../4-instrument/fm-opl.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/ymz280b.md b/doc/7-systems/ymz280b.md index b3a82b86..982df8d2 100644 --- a/doc/7-systems/ymz280b.md +++ b/doc/7-systems/ymz280b.md @@ -7,3 +7,7 @@ it has 16-level stereo panning, up to 16-bit PCM and up to 16MB of external PCM # effects none so far. + +# info + +this chip uses the [YMZ280B](../4-instrument/ymz280b.md) and [Generic Sample](../4-instrument/amiga.md) instrument editors. diff --git a/doc/7-systems/zxbeep.md b/doc/7-systems/zxbeep.md index e724e094..183beb4f 100644 --- a/doc/7-systems/zxbeep.md +++ b/doc/7-systems/zxbeep.md @@ -14,3 +14,7 @@ not really - very soon talented programmers found out ways to output much more t - `xx` is the sample number. - overlay drums are 1-bit and always play at 55930Hz (NTSC) or 55420Hz (PAL). - the maximum length is 2048! + +# info + +this chip uses the [Beeper](../4-instrument/beeper.md), [Generic Sample](../4-instrument/amiga.md), and [Pokémon Mini/QuadTone](../4-instrument/quadtone.md) instrument editors. diff --git a/doc/8-advanced/chanosc-gradient.png b/doc/8-advanced/chanosc-gradient.png index 26bdb258..357bbf76 100644 Binary files a/doc/8-advanced/chanosc-gradient.png and b/doc/8-advanced/chanosc-gradient.png differ diff --git a/doc/8-advanced/chanosc.md b/doc/8-advanced/chanosc.md index 4e3962ce..f58ae296 100644 --- a/doc/8-advanced/chanosc.md +++ b/doc/8-advanced/chanosc.md @@ -8,6 +8,11 @@ right-clicking within the view will change it to the configuration view shown ab - **Columns**: arranges oscilloscopes into this many columns. - **Size (ms)**: sets what length of audio is visible in each oscilloscope. - **Center waveform**: does its best to latch the waveform to the channel's note frequency and centers the display. +- **Automatic columns**: sets the number of columns based on the number of channels. + - **Off**: use the Columns setting. + - **Mode 1**: always fewer columns than rows. + - **Mode 2**: bias slightly toward more columns. + - **Mode 3**: always more columns than rows. - **Amplitude**: scales amplitude for all oscilloscopes. - **Gradient**: see below. - the color selector sets the color for all waveforms. right-clicking on it pops up an option dialog: @@ -19,6 +24,7 @@ right-clicking within the view will change it to the configuration view shown ab - `%C`: channel short name - `%d`: channel number (starting from 0) - `%D`: channel number (starting from 1) + - `%n`: channel note - `%i`: instrument name - `%I`: instrument number (decimal) - `%x`: instrument number (hex) diff --git a/doc/8-advanced/chanosc.png b/doc/8-advanced/chanosc.png index 2f90435a..06dce19c 100644 Binary files a/doc/8-advanced/chanosc.png and b/doc/8-advanced/chanosc.png differ diff --git a/doc/8-advanced/clock.png b/doc/8-advanced/clock.png index c051c5fd..47ca4fd6 100644 Binary files a/doc/8-advanced/clock.png and b/doc/8-advanced/clock.png differ diff --git a/doc/8-advanced/piano.png b/doc/8-advanced/piano.png index 9bf53cf6..ee04dd44 100644 Binary files a/doc/8-advanced/piano.png and b/doc/8-advanced/piano.png differ diff --git a/doc/9-guides/README.md b/doc/9-guides/README.md index 66b46072..aa6d5cbf 100644 --- a/doc/9-guides/README.md +++ b/doc/9-guides/README.md @@ -3,3 +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) +- [guide on using OPLL patch macro](opllswitching.md) \ No newline at end of file diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md new file mode 100644 index 00000000..340f0838 --- /dev/null +++ b/doc/9-guides/emulation-cores.md @@ -0,0 +1,36 @@ +# choosing emulation cores + +Furnace achieves the authentic sound of videogame hardware by emulating sound chips accurately as possible, using **emulator cores**. in some cases there are multiple cores to choose from, each with different strengths and weaknesses. here are the major differences between them all. + +- **Arcade/YM2151 core**: + - **ymfm**: default. much less CPU usage than Nuked-OPM, but less accurate. recommended for users with last-gen or earlier hardware. + - **Nuked-OPM**: much more accurate than ymfm, due to the emulator being based on an image of the die map taken from a real YM2151. very CPU heavy, only recommended for users with recent hardware. + +- **Genesis/YM2612 core**: + - **Nuked-OPN2**: default. a little lighter on the CPU than Nuked-OPM. + - **ymfm**: same as ymfm above. + +- **SN76489 core**: + - **MAME**: default. less accurate than Nuked, but with lower CPU usage. comes from the MAME emulator project. + - **Nuked-PSG Mod**: more accurate, but not by that much. this originally started as an emulator for the YM7101 PSG sound generator, but was modified to emulate the SN7 as the MAME core was deemed unsatisfactory by some. + +- **NES core**: + - **puNES**: default. it comes from a dedicated NES emulator. + - **NSFplay**: higher CPU usage than puNES. + +- **FDS core**: + - **puNES**: default. lower CPU usage and far less accurate. + - **NSFplay**: higher CPU usage and much more accurate. + +- **SID core**: + - **reSID**: default. a high quality emulation core. somewhat CPU heavy. + - **reSIDfp**: improved version of reSID. the most accurate choice. _extremely_ CPU heavy. + - **dSID**: a lightweight open-source core used in DefleMask. not so accurate but it's very CPU light. + +- **POKEY core**: + - **Atari800 (mzpokeysnd)**: does not emulate two-tone mode. + - **ASAP (C++ port)**: default. the sound core used in the ASAP player. most accurate option. + +- **OPN/OPNA/OPNB cores**: + - **ymfm only**: lower CPU usage, less accurate FM. + - **Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)**: default. more accurate FM at the cost of more CPU load. diff --git a/doc/9-guides/opllswitching.md b/doc/9-guides/opllswitching.md new file mode 100644 index 00000000..157a153f --- /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/doc/README.md b/doc/README.md index 58dca5b8..90e6e905 100644 --- a/doc/README.md +++ b/doc/README.md @@ -25,8 +25,3 @@ writers: - WindowxDeveloper - polluks - Electric Keet - -other: - -- [3-pattern/keyboard.png](3-pattern/keyboard.png) licensed under CC-BY-SA 3.0. - - this is a derivative of [KB United States.svg](https://en.wikipedia.org/wiki/File:KB_United_States.svg) by Denelson83 under the same license. diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 2ccc3e9c..d66dadf7 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 93db26e9..0850cd68 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/imgui_patched/imconfig.h b/extern/imgui_patched/imconfig.h index 876cf32f..860c0b42 100644 --- a/extern/imgui_patched/imconfig.h +++ b/extern/imgui_patched/imconfig.h @@ -97,7 +97,9 @@ // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. -//#define ImDrawIdx unsigned int +#ifndef IS_MOBILE +#define ImDrawIdx unsigned int +#endif //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) //struct ImDrawList; diff --git a/extern/imgui_patched/imconfig_fur.h b/extern/imgui_patched/imconfig_fur.h index 9c62b396..30ca7473 100644 --- a/extern/imgui_patched/imconfig_fur.h +++ b/extern/imgui_patched/imconfig_fur.h @@ -96,7 +96,9 @@ // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. -//#define ImDrawIdx unsigned int +#ifndef IS_MOBILE +#define ImDrawIdx unsigned int +#endif //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) //struct ImDrawList; diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 8674f997..3ee53ac5 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1236,6 +1236,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). // tildearrow ImGuiTableFlags_NoBordersInFrozenArea = 1 << 28, // Disable vertical borders in frozen area. + ImGuiTableFlags_NoScrollWithMouse = 1 << 29, // Disable user scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. // [Internal] Combinations and masks ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, diff --git a/extern/imgui_patched/imgui_tables.cpp b/extern/imgui_patched/imgui_tables.cpp index 8850094d..4aae7a1e 100644 --- a/extern/imgui_patched/imgui_tables.cpp +++ b/extern/imgui_patched/imgui_tables.cpp @@ -401,6 +401,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Create scrolling region (without border and zero window padding) ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; + if (flags & ImGuiTableFlags_NoScrollWithMouse) { + child_flags |= ImGuiWindowFlags_NoScrollWithMouse; + } BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; diff --git a/extern/nfd-modified/src/nfd_win.cpp b/extern/nfd-modified/src/nfd_win.cpp index 9c12c348..c60f7a75 100644 --- a/extern/nfd-modified/src/nfd_win.cpp +++ b/extern/nfd-modified/src/nfd_win.cpp @@ -532,6 +532,9 @@ nfdresult_t NFD_OpenDialogMultiple( const std::vector& filterList, nfdselcallback_t selCallback ) { nfdresult_t nfdResult = NFD_ERROR; + NFDWinEvents* winEvents; + bool hasEvents=true; + DWORD eventID=0; HRESULT coResult = COMInit(); @@ -566,6 +569,16 @@ nfdresult_t NFD_OpenDialogMultiple( const std::vector& filterList, goto end; } + // Pass the callback + winEvents=new NFDWinEvents(selCallback); + if ( !SUCCEEDED(fileOpenDialog->Advise(winEvents,&eventID)) ) { + // error... ignore + hasEvents=false; + winEvents->Release(); + } else { + winEvents->Release(); + } + // Set a flag for multiple options DWORD dwFlags; result = fileOpenDialog->GetOptions(&dwFlags); @@ -613,8 +626,12 @@ nfdresult_t NFD_OpenDialogMultiple( const std::vector& filterList, } end: - if ( fileOpenDialog ) + if (fileOpenDialog) { + if (hasEvents) { + fileOpenDialog->Unadvise(eventID); + } fileOpenDialog->Release(); + } COMUninit(coResult); 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 59d8af8f..cd6fe8e8 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/papers/clipboard-format.md b/papers/clipboard-format.md new file mode 100644 index 00000000..d12af1cc --- /dev/null +++ b/papers/clipboard-format.md @@ -0,0 +1,98 @@ +# clipboard format + +when copying pattern data from Furnace, it's stored in the clipboard as plain text. + +``` +org.tildearrow.furnace - Pattern Data (144) +``` + +this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6pre7 is `162`. + +the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from. +- `0`: note. +- `1`: instrument. +- `2`: volume. +- `3`: effect 1 type. +- `4`: effect 1 value. effect type is always included in the clip, even if skipped over. +- `5`: effect 2 type. +- `6`: effect 2 value. effect type is always included in the clip, even if skipped over. +- `7`: effect 3 type... +- ...and so on. + +examples of the starting column: + +``` +org.tildearrow.furnace - Pattern Data (144) +0 +D-6007F08080706|...........| +...............|...........| +...............|A#500..080F| +...............|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +1 +007F08080706|...........| +............|...........| +............|A#500..080F| +............|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +2 +7F08080706|...........| +..........|...........| +..........|A#500..080F| +..........|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +3 +08080706|...........| +........|...........| +........|A#500..080F| +........|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +4 +08080706|...........| +........|...........| +........|A#500..080F| +........|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +5 +0706|...........| +....|...........| +....|A#500..080F| +....|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +6 +0706|...........| +....|...........| +....|A#500..080F| +....|...........| +``` + +``` +org.tildearrow.furnace - Pattern Data (144) +0 +...........| +...........| +A#500..080F| +...........| +``` + +each line following the column number is verbatim from the pattern view with channels separated by `|`. each line also ends in `|`. + +notes use the default settings for note display (no German notation), including note off `OFF`, note release `===`, and macro release `REL`. diff --git a/papers/format.md b/papers/format.md index 499efe48..46bb4c1f 100644 --- a/papers/format.md +++ b/papers/format.md @@ -218,6 +218,7 @@ size | description | - 0xca: ZX Spectrum (beeper, QuadTone engine) - 5 channels | - 0xcb: Casio PV-1000 - 3 channels | - 0xcc: K053260 - 4 channels + | - 0xcd: TED - 2 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel diff --git a/papers/newIns.md b/papers/newIns.md index bd4daaec..786a553c 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -117,6 +117,8 @@ the following instrument types are available: - 47: Pokémon Mini/QuadTone - 48: SM8521 - 49: PV-1000 +- 50: K053260 +- 52: TED the following feature codes are recognized: diff --git a/papers/zsm-format.md b/papers/zsm-format.md index e0fd2c02..682e63b4 100644 --- a/papers/zsm-format.md +++ b/papers/zsm-format.md @@ -162,11 +162,12 @@ There are currently no supported expansion HW IDs assigned. The purpose of this channel is to provide for music synchronization cues that applications may use to perform operations in sync with the music (such as when the Goombas jump in New Super Mario Bros in time with the BOP! BOP! notes in the music). It is intended for the reference player to provide a sync channel callback, passing the data bytes to the callback function, and then to proceed with playback. -The synchronization format currently defines this one event type: +The synchronization format currently defines these event types: Event Type|Description|Message Format --|--|-- `0x00`|Generic sync message|`xx` (any value from `0x00`-`0xff`) +`0x01`|Song tuning|a signed byte indicating an offset from A-440 tuning represented in 256ths of a semitone An example of an EXTCMD containing one sync event might look as follows: `0x40 0x82 0x00 0x05` diff --git a/res/icons.sfd b/res/icons.sfd new file mode 100644 index 00000000..aa8c8072 --- /dev/null +++ b/res/icons.sfd @@ -0,0 +1,1306 @@ +SplineFontDB: 3.2 +FontName: FurnaceIcons +FullName: Furnace Icons +FamilyName: Furnace Icons +Weight: Regular +Copyright: +UComments: "2023-8-8: Created with FontForge (http://fontforge.org)" +Version: 001.000 +ItalicAngle: 0 +UnderlinePosition: 0 +UnderlineWidth: 0 +Ascent: 1536 +Descent: 256 +InvalidEm: 0 +LayerCount: 2 +Layer: 0 0 "Back" 1 +Layer: 1 0 "Fore" 0 +XUID: [1021 230 235539655 3711] +FSType: 0 +OS2Version: 0 +OS2_WeightWidthSlopeOnly: 0 +OS2_UseTypoMetrics: 0 +CreationTime: 1691524879 +ModificationTime: 1691614885 +PfmFamily: 81 +TTFWeight: 400 +TTFWidth: 5 +LineGap: 0 +VLineGap: 0 +OS2TypoAscent: 1536 +OS2TypoAOffset: 0 +OS2TypoDescent: -256 +OS2TypoDOffset: 0 +OS2TypoLinegap: 0 +OS2WinAscent: 1536 +OS2WinAOffset: 0 +OS2WinDescent: 256 +OS2WinDOffset: 0 +HheadAscent: 1536 +HheadAOffset: 0 +HheadDescent: -256 +HheadDOffset: 0 +OS2Vendor: 'FurT' +MarkAttachClasses: 1 +DEI: 91125 +LangName: 1033 +Encoding: UnicodeBmp +UnicodeInterp: none +NameList: AGL For New Fonts +DisplaySize: -48 +AntiAlias: 1 +FitToEm: 0 +WinInfo: 61400 8 11 +BeginPrivate: 0 +EndPrivate +TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 +BeginChars: 65536 11 + +StartChar: uniEFF6 +Encoding: 61430 61430 0 +Width: 1792 +Flags: HW +HStem: -87.5 97.6641<15.6582 26.9082 329.322 340.572 735.658 746.908 1049.32 1060.57 1455.66 1466.91 1769.32 1780.57> 423.109 92.4609<171.189 181.596 891.189 901.596 1611.19 1621.6> 735.5 97.6641<15.6582 26.9082 329.322 340.572 735.658 746.908 1049.32 1060.57 1455.66 1466.91 1769.32 1780.57> 1246.11 92.4609<171.189 181.596 891.189 901.596 1611.19 1621.6> +VStem: -167.506 93.3047<422.266 515.57 1245.27 1338.57> 121.971 111.445<455.694 515.57 1278.69 1338.57> 278.346 109.758<-87.5 -30.2713 735.5 792.729> 430.432 89.8594<425.711 515.57 1248.71 1338.57> 552.494 93.3047<422.266 515.57 1245.27 1338.57> 841.971 111.445<455.694 515.57 1278.69 1338.57> 998.346 109.758<-87.5 -30.2713 735.5 792.729> 1150.43 89.8594<425.711 515.57 1248.71 1338.57> 1272.49 93.3047<422.266 515.57 1245.27 1338.57> 1561.97 111.445<455.694 515.57 1278.69 1338.57> 1718.35 109.758<-87.5 -30.2713 735.5 792.729> 1870.43 89.8594<425.711 515.57 1248.71 1338.57> +DStem2: -74.2012 1338.57 -167.506 1338.57 0.207465 -0.978243<0 372.236> -74.2012 515.57 -167.506 515.57 0.207465 -0.978243<0 372.236> 26.9082 833.164 152.205 1107.03 0.135247 0.990812<0 428.665> 26.9082 10.1641 152.205 284.031 0.135247 0.990812<0 428.665> 310.338 971.398 200.58 1107.03 0.204871 -0.978789<-295.26 139.192> 310.338 148.398 200.58 284.031 0.204871 -0.978789<-295.26 139.192> 645.799 1338.57 552.494 1338.57 0.207465 -0.978243<0 372.236> 645.799 515.57 552.494 515.57 0.207465 -0.978243<0 372.236> 746.908 833.164 872.205 1107.03 0.135247 0.990812<0 428.665> 746.908 10.1641 872.205 284.031 0.135247 0.990812<0 428.665> 1030.34 971.398 920.58 1107.03 0.204871 -0.978789<-295.26 139.192> 1030.34 148.398 920.58 284.031 0.204871 -0.978789<-295.26 139.192> 1365.8 1338.57 1272.49 1338.57 0.207465 -0.978243<0 372.236> 1365.8 515.57 1272.49 515.57 0.207465 -0.978243<0 372.236> 1466.91 833.164 1592.21 1107.03 0.135247 0.990812<0 428.665> 1466.91 10.1641 1592.21 284.031 0.135247 0.990812<0 428.665> 1750.34 971.398 1640.58 1107.03 0.204871 -0.978789<-295.26 139.192> 1750.34 148.398 1640.58 284.031 0.204871 -0.978789<-295.26 139.192> +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +-39.607421875 735.5 m 1 + -167.505859375 1338.5703125 l 1 + -74.201171875 1338.5703125 l 1 + -3.326171875 973.0859375 l 1 + 15.658203125 833.1640625 l 1 + 26.908203125 833.1640625 l 1 + 45.892578125 971.3984375 l 1 + 121.970703125 1338.5703125 l 1 + 233.416015625 1338.5703125 l 1 + 310.337890625 971.3984375 l 1 + 329.322265625 833.1640625 l 1 + 340.572265625 833.1640625 l 1 + 359.556640625 973.0859375 l 1 + 430.431640625 1338.5703125 l 1 + 520.291015625 1338.5703125 l 1 + 388.103515625 735.5 l 1 + 278.345703125 735.5 l 1 + 200.580078125 1107.03125 l 1 + 181.595703125 1246.109375 l 1 + 171.189453125 1246.109375 l 1 + 152.205078125 1107.03125 l 1 + 70.994140625 735.5 l 1 + -39.607421875 735.5 l 1 +680.392578125 735.5 m 1 + 552.494140625 1338.5703125 l 1 + 645.798828125 1338.5703125 l 1 + 716.673828125 973.0859375 l 1 + 735.658203125 833.1640625 l 1 + 746.908203125 833.1640625 l 1 + 765.892578125 971.3984375 l 1 + 841.970703125 1338.5703125 l 1 + 953.416015625 1338.5703125 l 1 + 1030.33789062 971.3984375 l 1 + 1049.32226562 833.1640625 l 1 + 1060.57226562 833.1640625 l 1 + 1079.55664062 973.0859375 l 1 + 1150.43164062 1338.5703125 l 1 + 1240.29101562 1338.5703125 l 1 + 1108.10351562 735.5 l 1 + 998.345703125 735.5 l 1 + 920.580078125 1107.03125 l 1 + 901.595703125 1246.109375 l 1 + 891.189453125 1246.109375 l 1 + 872.205078125 1107.03125 l 1 + 790.994140625 735.5 l 1 + 680.392578125 735.5 l 1 +1400.39257812 735.5 m 1 + 1272.49414062 1338.5703125 l 1 + 1365.79882812 1338.5703125 l 1 + 1436.67382812 973.0859375 l 1 + 1455.65820312 833.1640625 l 1 + 1466.90820312 833.1640625 l 1 + 1485.89257812 971.3984375 l 1 + 1561.97070312 1338.5703125 l 1 + 1673.41601562 1338.5703125 l 1 + 1750.33789062 971.3984375 l 1 + 1769.32226562 833.1640625 l 1 + 1780.57226562 833.1640625 l 1 + 1799.55664062 973.0859375 l 1 + 1870.43164062 1338.5703125 l 1 + 1960.29101562 1338.5703125 l 1 + 1828.10351562 735.5 l 1 + 1718.34570312 735.5 l 1 + 1640.58007812 1107.03125 l 1 + 1621.59570312 1246.109375 l 1 + 1611.18945312 1246.109375 l 1 + 1592.20507812 1107.03125 l 1 + 1510.99414062 735.5 l 1 + 1400.39257812 735.5 l 1 +-39.607421875 -87.5 m 5 + -167.505859375 515.5703125 l 5 + -74.201171875 515.5703125 l 5 + -3.326171875 150.0859375 l 5 + 15.658203125 10.1640625 l 5 + 26.908203125 10.1640625 l 5 + 45.892578125 148.3984375 l 5 + 121.970703125 515.5703125 l 5 + 233.416015625 515.5703125 l 5 + 310.337890625 148.3984375 l 5 + 329.322265625 10.1640625 l 5 + 340.572265625 10.1640625 l 5 + 359.556640625 150.0859375 l 5 + 430.431640625 515.5703125 l 5 + 520.291015625 515.5703125 l 5 + 388.103515625 -87.5 l 5 + 278.345703125 -87.5 l 5 + 200.580078125 284.03125 l 5 + 181.595703125 423.109375 l 5 + 171.189453125 423.109375 l 5 + 152.205078125 284.03125 l 5 + 70.994140625 -87.5 l 5 + -39.607421875 -87.5 l 5 +680.392578125 -87.5 m 5 + 552.494140625 515.5703125 l 5 + 645.798828125 515.5703125 l 5 + 716.673828125 150.0859375 l 5 + 735.658203125 10.1640625 l 5 + 746.908203125 10.1640625 l 5 + 765.892578125 148.3984375 l 5 + 841.970703125 515.5703125 l 5 + 953.416015625 515.5703125 l 5 + 1030.33789062 148.3984375 l 5 + 1049.32226562 10.1640625 l 5 + 1060.57226562 10.1640625 l 5 + 1079.55664062 150.0859375 l 5 + 1150.43164062 515.5703125 l 5 + 1240.29101562 515.5703125 l 5 + 1108.10351562 -87.5 l 5 + 998.345703125 -87.5 l 5 + 920.580078125 284.03125 l 5 + 901.595703125 423.109375 l 5 + 891.189453125 423.109375 l 5 + 872.205078125 284.03125 l 5 + 790.994140625 -87.5 l 5 + 680.392578125 -87.5 l 5 +1400.39257812 -87.5 m 5 + 1272.49414062 515.5703125 l 5 + 1365.79882812 515.5703125 l 5 + 1436.67382812 150.0859375 l 5 + 1455.65820312 10.1640625 l 5 + 1466.90820312 10.1640625 l 5 + 1485.89257812 148.3984375 l 5 + 1561.97070312 515.5703125 l 5 + 1673.41601562 515.5703125 l 5 + 1750.33789062 148.3984375 l 5 + 1769.32226562 10.1640625 l 5 + 1780.57226562 10.1640625 l 5 + 1799.55664062 150.0859375 l 5 + 1870.43164062 515.5703125 l 5 + 1960.29101562 515.5703125 l 5 + 1828.10351562 -87.5 l 5 + 1718.34570312 -87.5 l 5 + 1640.58007812 284.03125 l 5 + 1621.59570312 423.109375 l 5 + 1611.18945312 423.109375 l 5 + 1592.20507812 284.03125 l 5 + 1510.99414062 -87.5 l 5 + 1400.39257812 -87.5 l 5 +EndSplineSet +EndChar + +StartChar: uniEFF3 +Encoding: 61427 61427 1 +Width: 1792 +Flags: HW +HStem: 323.95 97.6641<15.6582 26.9082 329.322 340.572 735.658 746.908 1049.32 1060.57 1455.66 1466.91 1769.32 1780.57> 834.56 92.4609<171.189 181.596 891.189 901.596 1611.19 1621.6> +VStem: -167.506 93.3047<833.716 927.021> 121.971 111.445<867.144 927.021> 278.346 109.758<323.95 381.179> 430.432 89.8594<837.161 927.021> 552.494 93.3047<833.716 927.021> 841.971 111.445<867.144 927.021> 998.346 109.758<323.95 381.179> 1150.43 89.8594<837.161 927.021> 1272.49 93.3047<833.716 927.021> 1561.97 111.445<867.144 927.021> 1718.35 109.758<323.95 381.179> 1870.43 89.8594<837.161 927.021> +DStem2: -74.2012 927.021 -167.506 927.021 0.207465 -0.978243<0 372.236> 26.9082 421.614 152.205 695.481 0.135247 0.990812<0 428.665> 310.338 559.849 200.58 695.481 0.204871 -0.978789<-295.26 139.192> 645.799 927.021 552.494 927.021 0.207465 -0.978243<0 372.236> 746.908 421.614 872.205 695.481 0.135247 0.990812<0 428.665> 1030.34 559.849 920.58 695.481 0.204871 -0.978789<-295.26 139.192> 1365.8 927.021 1272.49 927.021 0.207465 -0.978243<0 372.236> 1466.91 421.614 1592.21 695.481 0.135247 0.990812<0 428.665> 1750.34 559.849 1640.58 695.481 0.204871 -0.978789<-295.26 139.192> +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +-39.607421875 323.950195312 m 1 + -167.505859375 927.020507812 l 1 + -74.201171875 927.020507812 l 1 + -3.326171875 561.536132812 l 1 + 15.658203125 421.614257812 l 1 + 26.908203125 421.614257812 l 1 + 45.892578125 559.848632812 l 1 + 121.970703125 927.020507812 l 1 + 233.416015625 927.020507812 l 1 + 310.337890625 559.848632812 l 1 + 329.322265625 421.614257812 l 1 + 340.572265625 421.614257812 l 1 + 359.556640625 561.536132812 l 1 + 430.431640625 927.020507812 l 1 + 520.291015625 927.020507812 l 1 + 388.103515625 323.950195312 l 1 + 278.345703125 323.950195312 l 1 + 200.580078125 695.481445312 l 1 + 181.595703125 834.559570312 l 1 + 171.189453125 834.559570312 l 1 + 152.205078125 695.481445312 l 1 + 70.994140625 323.950195312 l 1 + -39.607421875 323.950195312 l 1 +680.392578125 323.950195312 m 1 + 552.494140625 927.020507812 l 1 + 645.798828125 927.020507812 l 1 + 716.673828125 561.536132812 l 1 + 735.658203125 421.614257812 l 1 + 746.908203125 421.614257812 l 1 + 765.892578125 559.848632812 l 1 + 841.970703125 927.020507812 l 1 + 953.416015625 927.020507812 l 1 + 1030.33789062 559.848632812 l 1 + 1049.32226562 421.614257812 l 1 + 1060.57226562 421.614257812 l 1 + 1079.55664062 561.536132812 l 1 + 1150.43164062 927.020507812 l 1 + 1240.29101562 927.020507812 l 1 + 1108.10351562 323.950195312 l 1 + 998.345703125 323.950195312 l 1 + 920.580078125 695.481445312 l 1 + 901.595703125 834.559570312 l 1 + 891.189453125 834.559570312 l 1 + 872.205078125 695.481445312 l 1 + 790.994140625 323.950195312 l 1 + 680.392578125 323.950195312 l 1 +1400.39257812 323.950195312 m 1 + 1272.49414062 927.020507812 l 1 + 1365.79882812 927.020507812 l 1 + 1436.67382812 561.536132812 l 1 + 1455.65820312 421.614257812 l 1 + 1466.90820312 421.614257812 l 1 + 1485.89257812 559.848632812 l 1 + 1561.97070312 927.020507812 l 1 + 1673.41601562 927.020507812 l 1 + 1750.33789062 559.848632812 l 1 + 1769.32226562 421.614257812 l 1 + 1780.57226562 421.614257812 l 1 + 1799.55664062 561.536132812 l 1 + 1870.43164062 927.020507812 l 1 + 1960.29101562 927.020507812 l 1 + 1828.10351562 323.950195312 l 1 + 1718.34570312 323.950195312 l 1 + 1640.58007812 695.481445312 l 1 + 1621.59570312 834.559570312 l 1 + 1611.18945312 834.559570312 l 1 + 1592.20507812 695.481445312 l 1 + 1510.99414062 323.950195312 l 1 + 1400.39257812 323.950195312 l 1 +EndSplineSet +EndChar + +StartChar: uniEFF2 +Encoding: 61426 61426 2 +Width: 1792 +Flags: HW +HStem: 290.5 108.516<317.609 330.109 666.125 678.625 1117.61 1130.11 1466.12 1478.62> +VStem: 114.094 103.672<856.906 960.578> 435.734 123.828<900.702 960.578> 609.484 121.953<290.5 347.729> 778.469 99.8438<860.734 960.578> 914.094 103.672<856.906 960.578> 1235.73 123.828<900.702 960.578> 1409.48 121.953<290.5 347.729> 1578.47 99.8438<860.734 960.578> +DStem2: 217.766 960.578 114.094 960.578 0.207465 -0.978243<0 413.596> 330.109 399.016 469.328 703.312 0.135247 0.990812<28.0512 476.294> 645.031 552.609 523.078 703.312 0.204871 -0.978789<-328.066 154.657> 1017.77 960.578 914.094 960.578 0.207465 -0.978243<0 413.596> 1130.11 399.016 1269.33 703.312 0.135247 0.990812<28.0512 476.294> 1445.03 552.609 1323.08 703.312 0.204871 -0.978789<-328.066 154.657> +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +256.203125 290.5 m 1 + 114.09375 960.578125 l 1 + 217.765625 960.578125 l 1 + 296.515625 554.484375 l 1 + 317.609375 399.015625 l 1 + 330.109375 399.015625 l 1 + 351.203125 552.609375 l 1 + 435.734375 960.578125 l 1 + 559.5625 960.578125 l 1 + 645.03125 552.609375 l 1 + 666.125 399.015625 l 1 + 678.625 399.015625 l 1 + 699.71875 554.484375 l 1 + 778.46875 960.578125 l 1 + 878.3125 960.578125 l 1 + 731.4375 290.5 l 1 + 609.484375 290.5 l 1 + 523.078125 703.3125 l 1 + 501.984375 857.84375 l 1 + 490.421875 857.84375 l 1 + 469.328125 703.3125 l 1 + 379.09375 290.5 l 1 + 256.203125 290.5 l 1 +1056.203125 290.5 m 1 + 914.09375 960.578125 l 1 + 1017.765625 960.578125 l 1 + 1096.515625 554.484375 l 1 + 1117.609375 399.015625 l 1 + 1130.109375 399.015625 l 1 + 1151.203125 552.609375 l 1 + 1235.734375 960.578125 l 1 + 1359.5625 960.578125 l 1 + 1445.03125 552.609375 l 1 + 1466.125 399.015625 l 1 + 1478.625 399.015625 l 1 + 1499.71875 554.484375 l 1 + 1578.46875 960.578125 l 1 + 1678.3125 960.578125 l 1 + 1531.4375 290.5 l 1 + 1409.484375 290.5 l 1 + 1323.078125 703.3125 l 1 + 1301.984375 857.84375 l 1 + 1290.421875 857.84375 l 1 + 1269.328125 703.3125 l 1 + 1179.09375 290.5 l 1 + 1056.203125 290.5 l 1 +EndSplineSet +EndChar + +StartChar: uniEFF5 +Encoding: 61429 61429 3 +Width: 1792 +Flags: HW +HStem: -142.5 108.516<317.609 330.109 666.125 678.625 1117.61 1130.11 1466.12 1478.62> 723.5 108.516<317.609 330.109 666.125 678.625 1117.61 1130.11 1466.12 1478.62> +VStem: 114.094 103.672<423.906 527.578 1289.91 1393.58> 435.734 123.828<467.702 527.578 1333.7 1393.58> 609.484 121.953<-142.5 -85.2713 723.5 780.729> 778.469 99.8438<427.734 527.578 1293.73 1393.58> 914.094 103.672<423.906 527.578 1289.91 1393.58> 1235.73 123.828<467.702 527.578 1333.7 1393.58> 1409.48 121.953<-142.5 -85.2713 723.5 780.729> 1578.47 99.8438<427.734 527.578 1293.73 1393.58> +DStem2: 217.766 1393.58 114.094 1393.58 0.207465 -0.978243<0 413.596> 217.766 527.578 114.094 527.578 0.207465 -0.978243<0 413.596> 330.109 832.016 469.328 1136.31 0.135247 0.990812<28.0512 476.294> 330.109 -33.9844 469.328 270.312 0.135247 0.990812<28.0512 476.294> 645.031 985.609 523.078 1136.31 0.204871 -0.978789<-328.066 154.657> 645.031 119.609 523.078 270.312 0.204871 -0.978789<-328.066 154.657> 1017.77 1393.58 914.094 1393.58 0.207465 -0.978243<0 413.596> 1017.77 527.578 914.094 527.578 0.207465 -0.978243<0 413.596> 1130.11 832.016 1269.33 1136.31 0.135247 0.990812<28.0512 476.294> 1130.11 -33.9844 1269.33 270.312 0.135247 0.990812<28.0512 476.294> 1445.03 985.609 1323.08 1136.31 0.204871 -0.978789<-328.066 154.657> 1445.03 119.609 1323.08 270.312 0.204871 -0.978789<-328.066 154.657> +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +256.203125 -142.5 m 1 + 114.09375 527.578125 l 1 + 217.765625 527.578125 l 1 + 296.515625 121.484375 l 1 + 317.609375 -33.984375 l 1 + 330.109375 -33.984375 l 1 + 351.203125 119.609375 l 1 + 435.734375 527.578125 l 1 + 559.5625 527.578125 l 1 + 645.03125 119.609375 l 1 + 666.125 -33.984375 l 1 + 678.625 -33.984375 l 1 + 699.71875 121.484375 l 1 + 778.46875 527.578125 l 1 + 878.3125 527.578125 l 1 + 731.4375 -142.5 l 1 + 609.484375 -142.5 l 1 + 523.078125 270.3125 l 1 + 501.984375 424.84375 l 1 + 490.421875 424.84375 l 1 + 469.328125 270.3125 l 1 + 379.09375 -142.5 l 1 + 256.203125 -142.5 l 1 +1056.203125 -142.5 m 1 + 914.09375 527.578125 l 1 + 1017.765625 527.578125 l 1 + 1096.515625 121.484375 l 1 + 1117.609375 -33.984375 l 1 + 1130.109375 -33.984375 l 1 + 1151.203125 119.609375 l 1 + 1235.734375 527.578125 l 1 + 1359.5625 527.578125 l 1 + 1445.03125 119.609375 l 1 + 1466.125 -33.984375 l 1 + 1478.625 -33.984375 l 1 + 1499.71875 121.484375 l 1 + 1578.46875 527.578125 l 1 + 1678.3125 527.578125 l 1 + 1531.4375 -142.5 l 1 + 1409.484375 -142.5 l 1 + 1323.078125 270.3125 l 1 + 1301.984375 424.84375 l 1 + 1290.421875 424.84375 l 1 + 1269.328125 270.3125 l 1 + 1179.09375 -142.5 l 1 + 1056.203125 -142.5 l 1 +256.203125 723.5 m 1 + 114.09375 1393.578125 l 1 + 217.765625 1393.578125 l 1 + 296.515625 987.484375 l 1 + 317.609375 832.015625 l 1 + 330.109375 832.015625 l 1 + 351.203125 985.609375 l 1 + 435.734375 1393.578125 l 1 + 559.5625 1393.578125 l 1 + 645.03125 985.609375 l 1 + 666.125 832.015625 l 1 + 678.625 832.015625 l 1 + 699.71875 987.484375 l 1 + 778.46875 1393.578125 l 1 + 878.3125 1393.578125 l 1 + 731.4375 723.5 l 1 + 609.484375 723.5 l 1 + 523.078125 1136.3125 l 1 + 501.984375 1290.84375 l 1 + 490.421875 1290.84375 l 1 + 469.328125 1136.3125 l 1 + 379.09375 723.5 l 1 + 256.203125 723.5 l 1 +1056.203125 723.5 m 1 + 914.09375 1393.578125 l 1 + 1017.765625 1393.578125 l 1 + 1096.515625 987.484375 l 1 + 1117.609375 832.015625 l 1 + 1130.109375 832.015625 l 1 + 1151.203125 985.609375 l 1 + 1235.734375 1393.578125 l 1 + 1359.5625 1393.578125 l 1 + 1445.03125 985.609375 l 1 + 1466.125 832.015625 l 1 + 1478.625 832.015625 l 1 + 1499.71875 987.484375 l 1 + 1578.46875 1393.578125 l 1 + 1678.3125 1393.578125 l 1 + 1531.4375 723.5 l 1 + 1409.484375 723.5 l 1 + 1323.078125 1136.3125 l 1 + 1301.984375 1290.84375 l 1 + 1290.421875 1290.84375 l 1 + 1269.328125 1136.3125 l 1 + 1179.09375 723.5 l 1 + 1056.203125 723.5 l 1 +EndSplineSet +EndChar + +StartChar: uniEFF1 +Encoding: 61425 61425 4 +Width: 1792 +Flags: H +VStem: 232.799 173.865<979.508 1153.37> 1392.52 167.836<985.537 1153.37> +DStem2: 406.664 1153.37 232.799 1153.37 0.229994 -0.973192<0 877.585> 592.588 294.506 674.291 98 0.24367 0.969858<0 722.624> 993.352 1153.37 895.037 964.373 0.238997 -0.97102<160.026 881.918> 1204.99 296.105 1300.23 98 0.239311 0.970943<0 877.234> +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +482.21484375 98 m 1 + 232.798828125 1153.37304688 l 1 + 406.6640625 1153.37304688 l 1 + 513.9609375 645.3125 l 1 + 588.158203125 294.505859375 l 1 + 592.587890625 294.505859375 l 1 + 677.3671875 645.3125 l 1 + 802.751953125 1153.37304688 l 1 + 993.3515625 1153.37304688 l 1 + 1117.25976562 645.3125 l 1 + 1200.43945312 296.10546875 l 1 + 1204.9921875 296.10546875 l 1 + 1282.14257812 645.3125 l 1 + 1392.515625 1153.37304688 l 1 + 1560.3515625 1153.37304688 l 1 + 1300.23046875 98 l 1 + 1108.27734375 98 l 1 + 973.6640625 643.8359375 l 1 + 895.037109375 964.373046875 l 1 + 891.9609375 964.373046875 l 1 + 811.857421875 643.8359375 l 1 + 674.291015625 98 l 1 + 482.21484375 98 l 1 +EndSplineSet +EndChar + +StartChar: uniF004 +Encoding: 61444 61444 5 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +UndoOperation +Index: 1 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +267.583007812 313.6640625 m 0 + 216.887695312 313.6640625 173.856445312 322.875 138.418945312 341.296875 c 128 + 102.981445312 359.71875 72.6064453125 384.46875 47.2939453125 415.6171875 c 1 + 114.653320312 478.6875 l 1 + 135.958007812 452.7421875 159.442382812 433.125 185.036132812 419.90625 c 128 + 210.700195312 406.6875 239.950195312 400.0078125 272.786132812 400.0078125 c 0 + 311.387695312 400.0078125 340.426757812 408.65625 360.043945312 425.953125 c 128 + 379.590820312 443.25 389.434570312 466.59375 389.434570312 495.9140625 c 0 + 389.434570312 519.5390625 382.473632812 538.2421875 368.692382812 552.09375 c 128 + 354.840820312 565.9453125 330.372070312 576.28125 295.215820312 583.171875 c 2 + 241.637695312 592.734375 l 2 + 182.926757812 603.6328125 138.840820312 622.96875 109.450195312 650.6015625 c 128 + 80.0595703125 678.234375 65.4345703125 716.2734375 65.4345703125 764.6484375 c 0 + 65.4345703125 791.15625 70.4267578125 815.203125 80.5517578125 836.7890625 c 128 + 90.6064453125 858.375 104.739257812 876.515625 122.879882812 891.2109375 c 128 + 141.020507812 905.90625 163.028320312 917.296875 188.973632812 925.3125 c 128 + 214.848632812 933.3984375 243.958007812 937.40625 276.231445312 937.40625 c 0 + 321.723632812 937.40625 361.168945312 929.53125 394.567382812 913.7109375 c 128 + 427.965820312 897.8203125 456.512695312 874.96875 480.137695312 845.015625 c 1 + 411.864257812 784.546875 l 1 + 396.325195312 804.65625 377.340820312 820.828125 354.840820312 832.921875 c 128 + 332.411132812 845.015625 304.426757812 851.0625 271.028320312 851.0625 c 0 + 236.504882812 851.0625 209.715820312 844.1015625 190.661132812 830.3203125 c 128 + 171.676757812 816.46875 162.184570312 796.2890625 162.184570312 769.8515625 c 0 + 162.184570312 744.46875 169.918945312 725.625 185.528320312 713.25 c 128 + 201.067382812 700.875 225.254882812 691.453125 258.090820312 685.125 c 2 + 311.668945312 673.9453125 l 2 + 372.137695312 662.4140625 416.364257812 642.796875 444.278320312 615.1640625 c 128 + 472.192382812 587.53125 486.184570312 549.4921875 486.184570312 501.1171875 c 0 + 486.184570312 472.921875 481.262695312 447.2578125 471.489257812 424.1953125 c 128 + 461.715820312 401.203125 447.442382812 381.4453125 428.739257812 365.0625 c 128 + 409.965820312 348.609375 387.114257812 335.953125 360.043945312 327.0234375 c 128 + 332.973632812 318.09375 302.106445312 313.6640625 267.583007812 313.6640625 c 0 +1052.41113281 785.390625 m 1 + 1048.05175781 785.390625 l 1 + 1000.52050781 687.7265625 l 1 + 868.333007812 446.6953125 l 1 + 736.145507812 687.7265625 l 1 + 688.614257812 785.390625 l 1 + 684.325195312 785.390625 l 1 + 684.325195312 324 l 1 + 591.020507812 324 l 1 + 591.020507812 927.0703125 l 1 + 706.754882812 927.0703125 l 1 + 868.333007812 620.3671875 l 1 + 873.536132812 620.3671875 l 1 + 1035.11425781 927.0703125 l 1 + 1145.71582031 927.0703125 l 1 + 1145.71582031 324 l 1 + 1052.41113281 324 l 1 + 1052.41113281 785.390625 l 1 +1311.02050781 324 m 1 + 1311.02050781 927.0703125 l 1 + 1570.19238281 927.0703125 l 2 + 1626.09082031 927.0703125 1669.26269531 910.6875 1699.77832031 877.8515625 c 128 + 1730.36425781 845.015625 1745.62207031 800.9296875 1745.62207031 745.6640625 c 256 + 1745.62207031 690.328125 1730.36425781 646.2421875 1699.77832031 613.40625 c 128 + 1669.26269531 580.640625 1626.09082031 564.1875 1570.19238281 564.1875 c 2 + 1408.61425781 564.1875 l 1 + 1408.61425781 324 l 1 + 1311.02050781 324 l 1 +1408.61425781 649.7578125 m 1 + 1564.14550781 649.7578125 l 2 + 1588.89550781 649.7578125 1608.23144531 656.2265625 1622.01269531 669.1640625 c 128 + 1635.86425781 682.1015625 1642.75488281 700.734375 1642.75488281 724.921875 c 2 + 1642.75488281 766.3359375 l 2 + 1642.75488281 790.59375 1635.86425781 809.15625 1622.01269531 822.09375 c 128 + 1608.23144531 835.03125 1588.89550781 841.5703125 1564.14550781 841.5703125 c 2 + 1408.61425781 841.5703125 l 1 + 1408.61425781 649.7578125 l 1 +EndSplineSet +EndChar + +StartChar: uniF01A +Encoding: 61466 61466 6 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +UndoOperation +Index: 1 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +536.239257812 -97.8359375 m 0 + 502.840820312 -97.8359375 473.028320312 -92.421875 446.801757812 -81.453125 c 128 + 420.575195312 -70.484375 398.426757812 -54.8046875 380.286132812 -34.34375 c 128 + 362.145507812 -13.8828125 348.153320312 10.7265625 338.379882812 39.484375 c 128 + 328.606445312 68.3125 323.684570312 100.8671875 323.684570312 137.1484375 c 0 + 323.684570312 180.8828125 330.293945312 222.0859375 343.583007812 260.6875 c 128 + 356.801757812 299.2890625 373.817382812 334.7265625 394.559570312 366.9296875 c 128 + 415.301757812 399.203125 438.434570312 427.890625 464.098632812 452.921875 c 128 + 489.692382812 477.953125 514.934570312 498.8359375 539.684570312 515.5703125 c 1 + 669.270507812 515.5703125 l 1 + 635.309570312 490.8203125 604.793945312 466.9140625 577.723632812 443.8515625 c 128 + 550.653320312 420.7890625 527.028320312 397.234375 506.848632812 373.046875 c 128 + 486.668945312 348.7890625 469.864257812 323.1953125 456.293945312 296.125 c 128 + 442.793945312 269.0546875 432.247070312 239.3828125 424.793945312 207.109375 c 1 + 430.840820312 205.421875 l 1 + 437.168945312 217.515625 444.481445312 228.8359375 452.848632812 239.5234375 c 128 + 461.215820312 250.2109375 470.989257812 259.5625 482.239257812 267.578125 c 128 + 493.489257812 275.6640625 506.286132812 281.9921875 520.700195312 286.6328125 c 128 + 535.114257812 291.203125 551.778320312 293.5234375 570.762695312 293.5234375 c 0 + 596.145507812 293.5234375 619.629882812 289.234375 641.215820312 280.5859375 c 128 + 662.801757812 271.9375 681.504882812 259.421875 697.395507812 242.96875 c 128 + 713.215820312 226.5859375 725.590820312 206.828125 734.520507812 183.765625 c 128 + 743.450195312 160.7734375 747.950195312 135.109375 747.950195312 106.9140625 c 0 + 747.950195312 76.3984375 742.887695312 48.5546875 732.762695312 23.5234375 c 128 + 722.708007812 -1.5078125 708.434570312 -22.953125 690.012695312 -40.8125 c 128 + 671.590820312 -58.671875 649.301757812 -72.6640625 623.075195312 -82.71875 c 128 + 596.848632812 -92.84375 567.950195312 -97.8359375 536.239257812 -97.8359375 c 0 +536.239257812 -18.3828125 m 256 + 571.395507812 -18.3828125 598.747070312 -9.03125 618.293945312 9.671875 c 128 + 637.911132812 28.4453125 647.684570312 55.9375 647.684570312 92.21875 c 2 + 647.684570312 104.3125 l 2 + 647.684570312 140.59375 637.911132812 168.0859375 618.293945312 186.7890625 c 128 + 598.747070312 205.5625 571.395507812 214.9140625 536.239257812 214.9140625 c 256 + 501.083007812 214.9140625 473.731445312 205.5625 454.184570312 186.7890625 c 128 + 434.567382812 168.0859375 424.793945312 140.59375 424.793945312 104.3125 c 2 + 424.793945312 92.21875 l 2 + 424.793945312 55.9375 434.567382812 28.4453125 454.184570312 9.671875 c 128 + 473.731445312 -9.03125 501.083007812 -18.3828125 536.239257812 -18.3828125 c 256 +1250.19238281 -97.8359375 m 0 + 1199.49707031 -97.8359375 1156.46582031 -88.625 1121.02832031 -70.203125 c 128 + 1085.59082031 -51.78125 1055.21582031 -27.03125 1029.90332031 4.1171875 c 1 + 1097.26269531 67.1875 l 1 + 1118.56738281 41.2421875 1142.05175781 21.625 1167.64550781 8.40625 c 128 + 1193.30957031 -4.8125 1222.55957031 -11.4921875 1255.39550781 -11.4921875 c 0 + 1293.99707031 -11.4921875 1323.03613281 -2.84375 1342.65332031 14.453125 c 128 + 1362.20019531 31.75 1372.04394531 55.09375 1372.04394531 84.4140625 c 0 + 1372.04394531 108.0390625 1365.08300781 126.7421875 1351.30175781 140.59375 c 128 + 1337.45019531 154.4453125 1312.98144531 164.78125 1277.82519531 171.671875 c 2 + 1224.24707031 181.234375 l 2 + 1165.53613281 192.1328125 1121.45019531 211.46875 1092.05957031 239.1015625 c 128 + 1062.66894531 266.734375 1048.04394531 304.7734375 1048.04394531 353.1484375 c 0 + 1048.04394531 379.65625 1053.03613281 403.703125 1063.16113281 425.2890625 c 128 + 1073.21582031 446.875 1087.34863281 465.015625 1105.48925781 479.7109375 c 128 + 1123.62988281 494.40625 1145.63769531 505.796875 1171.58300781 513.8125 c 128 + 1197.45800781 521.8984375 1226.56738281 525.90625 1258.84082031 525.90625 c 0 + 1304.33300781 525.90625 1343.77832031 518.03125 1377.17675781 502.2109375 c 128 + 1410.57519531 486.3203125 1439.12207031 463.46875 1462.74707031 433.515625 c 1 + 1394.47363281 373.046875 l 1 + 1378.93457031 393.15625 1359.95019531 409.328125 1337.45019531 421.421875 c 128 + 1315.02050781 433.515625 1287.03613281 439.5625 1253.63769531 439.5625 c 0 + 1219.11425781 439.5625 1192.32519531 432.6015625 1173.27050781 418.8203125 c 128 + 1154.28613281 404.96875 1144.79394531 384.7890625 1144.79394531 358.3515625 c 0 + 1144.79394531 332.96875 1152.52832031 314.125 1168.13769531 301.75 c 128 + 1183.67675781 289.375 1207.86425781 279.953125 1240.70019531 273.625 c 2 + 1294.27832031 262.4453125 l 2 + 1354.74707031 250.9140625 1398.97363281 231.296875 1426.88769531 203.6640625 c 128 + 1454.80175781 176.03125 1468.79394531 137.9921875 1468.79394531 89.6171875 c 0 + 1468.79394531 61.421875 1463.87207031 35.7578125 1454.09863281 12.6953125 c 128 + 1444.32519531 -10.296875 1430.05175781 -30.0546875 1411.34863281 -46.4375 c 128 + 1392.57519531 -62.890625 1369.72363281 -75.546875 1342.65332031 -84.4765625 c 128 + 1315.58300781 -93.40625 1284.71582031 -97.8359375 1250.19238281 -97.8359375 c 0 +268.12890625 735.5 m 1 + 70.26953125 1338.5703125 l 1 + 171.37890625 1338.5703125 l 1 + 267.28515625 1040.515625 l 1 + 325.15234375 830.5625 l 1 + 328.59765625 830.5625 l 1 + 387.37890625 1040.515625 l 1 + 483.28515625 1338.5703125 l 1 + 581.79296875 1338.5703125 l 1 + 381.33203125 735.5 l 1 + 268.12890625 735.5 l 1 +802.64453125 735.5 m 1 + 705.05078125 735.5 l 1 + 705.05078125 1338.5703125 l 1 + 965.06640625 1338.5703125 l 2 + 1019.27734375 1338.5703125 1061.88671875 1322.328125 1092.96484375 1289.7734375 c 128 + 1124.04296875 1257.21875 1139.65234375 1212.9921875 1139.65234375 1157.1640625 c 0 + 1139.65234375 1113.921875 1129.66796875 1078.0625 1109.83984375 1049.5859375 c 128 + 1089.94140625 1021.0390625 1060.69140625 1001.0703125 1022.08984375 989.5390625 c 1 + 1152.58984375 735.5 l 1 + 1043.74609375 735.5 l 1 + 922.73828125 979.1328125 l 1 + 802.64453125 979.1328125 l 1 + 802.64453125 735.5 l 1 +958.17578125 1061.2578125 m 2 + 982.92578125 1061.2578125 1002.26171875 1067.7265625 1016.04296875 1080.6640625 c 128 + 1029.89453125 1093.6015625 1036.78515625 1112.234375 1036.78515625 1136.421875 c 2 + 1036.78515625 1177.8359375 l 2 + 1036.78515625 1202.09375 1029.89453125 1220.65625 1016.04296875 1233.59375 c 128 + 1002.26171875 1246.53125 982.92578125 1253.0703125 958.17578125 1253.0703125 c 2 + 802.64453125 1253.0703125 l 1 + 802.64453125 1061.2578125 l 1 + 958.17578125 1061.2578125 l 2 +1507.38671875 725.1640625 m 0 + 1428.49609375 725.1640625 1366.55078125 751.7421875 1321.62109375 805.0390625 c 128 + 1276.69140625 858.3359375 1254.26171875 935.6796875 1254.26171875 1037.0703125 c 0 + 1254.26171875 1087.6953125 1260.02734375 1132.34375 1271.55859375 1170.9453125 c 128 + 1283.01953125 1209.546875 1299.75390625 1242.1015625 1321.62109375 1268.609375 c 128 + 1343.55859375 1295.1171875 1370.13671875 1315.0859375 1401.56640625 1328.65625 c 128 + 1432.92578125 1342.15625 1468.22265625 1348.90625 1507.38671875 1348.90625 c 0 + 1559.83984375 1348.90625 1603.71484375 1337.4453125 1639.15234375 1314.3828125 c 128 + 1674.58984375 1291.3203125 1702.36328125 1257.359375 1722.54296875 1212.4296875 c 1 + 1640.48828125 1167.5 l 1 + 1630.08203125 1196.328125 1614.12109375 1219.1796875 1592.53515625 1236.1953125 c 128 + 1570.87890625 1253.2109375 1542.54296875 1261.6484375 1507.38671875 1261.6484375 c 0 + 1460.76953125 1261.6484375 1424.13671875 1245.828125 1397.69921875 1214.1875 c 128 + 1371.19140625 1182.4765625 1357.90234375 1138.671875 1357.90234375 1082.84375 c 2 + 1357.90234375 991.2265625 l 2 + 1357.90234375 935.3984375 1371.19140625 891.59375 1397.69921875 859.8828125 c 128 + 1424.13671875 828.2421875 1460.76953125 812.421875 1507.38671875 812.421875 c 0 + 1543.66796875 812.421875 1573.19921875 821.7734375 1595.98046875 840.4765625 c 128 + 1618.69140625 859.1796875 1635.56640625 883.2265625 1646.53515625 912.6171875 c 1 + 1725.14453125 865.0859375 l 1 + 1704.96484375 821.3515625 1676.76953125 787.0390625 1640.48828125 762.2890625 c 128 + 1604.13671875 737.5390625 1559.83984375 725.1640625 1507.38671875 725.1640625 c 0 +EndSplineSet +EndChar + +StartChar: uniF016 +Encoding: 61462 61462 7 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +UndoOperation +Index: 1 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +765.548828125 -142.5 m 1 + 705.080078125 38.90625 l 1 + 454.533203125 38.90625 l 1 + 395.939453125 -142.5 l 1 + 285.548828125 -142.5 l 1 + 514.064453125 527.578125 l 1 + 650.392578125 527.578125 l 1 + 878.830078125 -142.5 l 1 + 765.548828125 -142.5 l 1 +582.189453125 427.734375 m 1 + 577.423828125 427.734375 l 1 + 480.470703125 132.03125 l 1 + 678.205078125 132.03125 l 1 + 582.189453125 427.734375 l 1 +1167.50195312 228.046875 m 1 + 1093.59570312 370.15625 l 1 + 1090.70507812 370.15625 l 1 + 1090.70507812 -142.5 l 1 + 987.033203125 -142.5 l 1 + 987.033203125 527.578125 l 1 + 1107.97070312 527.578125 l 1 + 1326.87695312 157.03125 l 1 + 1400.78320312 14.921875 l 1 + 1403.67382812 14.921875 l 1 + 1403.67382812 527.578125 l 1 + 1507.34570312 527.578125 l 1 + 1507.34570312 -142.5 l 1 + 1386.40820312 -142.5 l 1 + 1167.50195312 228.046875 l 1 +447.483398438 712.015625 m 0 + 391.155273438 712.015625 343.342773438 722.25 303.967773438 742.71875 c 128 + 264.592773438 763.1875 230.842773438 790.6875 202.717773438 825.296875 c 1 + 277.561523438 895.375 l 1 + 301.233398438 866.546875 327.327148438 844.75 355.764648438 830.0625 c 128 + 384.280273438 815.375 416.780273438 807.953125 453.264648438 807.953125 c 0 + 496.155273438 807.953125 528.420898438 817.5625 550.217773438 836.78125 c 128 + 571.936523438 856 582.874023438 881.9375 582.874023438 914.515625 c 0 + 582.874023438 940.765625 575.139648438 961.546875 559.827148438 976.9375 c 128 + 544.436523438 992.328125 517.249023438 1003.8125 478.186523438 1011.46875 c 2 + 418.655273438 1022.09375 l 2 + 353.420898438 1034.203125 304.436523438 1055.6875 271.780273438 1086.390625 c 128 + 239.124023438 1117.09375 222.874023438 1159.359375 222.874023438 1213.109375 c 0 + 222.874023438 1242.5625 228.420898438 1269.28125 239.670898438 1293.265625 c 128 + 250.842773438 1317.25 266.545898438 1337.40625 286.702148438 1353.734375 c 128 + 306.858398438 1370.0625 331.311523438 1382.71875 360.139648438 1391.625 c 128 + 388.889648438 1400.609375 421.233398438 1405.0625 457.092773438 1405.0625 c 0 + 507.639648438 1405.0625 551.467773438 1396.3125 588.577148438 1378.734375 c 128 + 625.686523438 1361.078125 657.405273438 1335.6875 683.655273438 1302.40625 c 1 + 607.795898438 1235.21875 l 1 + 590.530273438 1257.5625 569.436523438 1275.53125 544.436523438 1288.96875 c 128 + 519.514648438 1302.40625 488.420898438 1309.125 451.311523438 1309.125 c 0 + 412.952148438 1309.125 383.186523438 1301.390625 362.014648438 1286.078125 c 128 + 340.920898438 1270.6875 330.374023438 1248.265625 330.374023438 1218.890625 c 0 + 330.374023438 1190.6875 338.967773438 1169.75 356.311523438 1156 c 128 + 373.577148438 1142.25 400.452148438 1131.78125 436.936523438 1124.75 c 2 + 496.467773438 1112.328125 l 2 + 563.655273438 1099.515625 612.795898438 1077.71875 643.811523438 1047.015625 c 128 + 674.827148438 1016.3125 690.374023438 974.046875 690.374023438 920.296875 c 0 + 690.374023438 888.96875 684.905273438 860.453125 674.045898438 834.828125 c 128 + 663.186523438 809.28125 647.327148438 787.328125 626.545898438 769.125 c 128 + 605.686523438 750.84375 580.295898438 736.78125 550.217773438 726.859375 c 128 + 520.139648438 716.9375 485.842773438 712.015625 447.483398438 712.015625 c 0 +905.686523438 723.5 m 1 + 747.327148438 1393.578125 l 1 + 857.717773438 1393.578125 l 1 + 925.842773438 1071 l 1 + 972.952148438 848.265625 l 1 + 975.764648438 848.265625 l 1 + 1029.59277344 1071 l 1 + 1109.20214844 1393.578125 l 1 + 1230.21777344 1393.578125 l 1 + 1308.88964844 1071 l 1 + 1361.70214844 849.28125 l 1 + 1364.59277344 849.28125 l 1 + 1413.57714844 1071 l 1 + 1483.65527344 1393.578125 l 1 + 1590.21777344 1393.578125 l 1 + 1425.06152344 723.5 l 1 + 1303.18652344 723.5 l 1 + 1217.71777344 1070.0625 l 1 + 1167.79589844 1273.578125 l 1 + 1165.84277344 1273.578125 l 1 + 1114.98339844 1070.0625 l 1 + 1027.63964844 723.5 l 1 + 905.686523438 723.5 l 1 +EndSplineSet +EndChar + +StartChar: uniF01E +Encoding: 61470 61470 8 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +UndoOperation +Index: 1 +Type: 3 +WasModified: 0 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +590.483398438 279.015625 m 0 + 534.155273438 279.015625 486.342773438 289.25 446.967773438 309.71875 c 128 + 407.592773438 330.1875 373.842773438 357.6875 345.717773438 392.296875 c 1 + 420.561523438 462.375 l 1 + 444.233398438 433.546875 470.327148438 411.75 498.764648438 397.0625 c 128 + 527.280273438 382.375 559.780273438 374.953125 596.264648438 374.953125 c 0 + 639.155273438 374.953125 671.420898438 384.5625 693.217773438 403.78125 c 128 + 714.936523438 423 725.874023438 448.9375 725.874023438 481.515625 c 0 + 725.874023438 507.765625 718.139648438 528.546875 702.827148438 543.9375 c 128 + 687.436523438 559.328125 660.249023438 570.8125 621.186523438 578.46875 c 2 + 561.655273438 589.09375 l 2 + 496.420898438 601.203125 447.436523438 622.6875 414.780273438 653.390625 c 128 + 382.124023438 684.09375 365.874023438 726.359375 365.874023438 780.109375 c 0 + 365.874023438 809.5625 371.420898438 836.28125 382.670898438 860.265625 c 128 + 393.842773438 884.25 409.545898438 904.40625 429.702148438 920.734375 c 128 + 449.858398438 937.0625 474.311523438 949.71875 503.139648438 958.625 c 128 + 531.889648438 967.609375 564.233398438 972.0625 600.092773438 972.0625 c 0 + 650.639648438 972.0625 694.467773438 963.3125 731.577148438 945.734375 c 128 + 768.686523438 928.078125 800.405273438 902.6875 826.655273438 869.40625 c 1 + 750.795898438 802.21875 l 1 + 733.530273438 824.5625 712.436523438 842.53125 687.436523438 855.96875 c 128 + 662.514648438 869.40625 631.420898438 876.125 594.311523438 876.125 c 0 + 555.952148438 876.125 526.186523438 868.390625 505.014648438 853.078125 c 128 + 483.920898438 837.6875 473.374023438 815.265625 473.374023438 785.890625 c 0 + 473.374023438 757.6875 481.967773438 736.75 499.311523438 723 c 128 + 516.577148438 709.25 543.452148438 698.78125 579.936523438 691.75 c 2 + 639.467773438 679.328125 l 2 + 706.655273438 666.515625 755.795898438 644.71875 786.811523438 614.015625 c 128 + 817.827148438 583.3125 833.374023438 541.046875 833.374023438 487.296875 c 0 + 833.374023438 455.96875 827.905273438 427.453125 817.045898438 401.828125 c 128 + 806.186523438 376.28125 790.327148438 354.328125 769.545898438 336.125 c 128 + 748.686523438 317.84375 723.295898438 303.78125 693.217773438 293.859375 c 128 + 663.139648438 283.9375 628.842773438 279.015625 590.483398438 279.015625 c 0 +1052.59277344 960.578125 m 1 + 1052.59277344 547.765625 l 2 + 1052.59277344 490.8125 1063.45214844 447.921875 1085.17089844 419.171875 c 128 + 1106.96777344 390.34375 1144.07714844 375.96875 1196.57714844 375.96875 c 256 + 1249.07714844 375.96875 1286.18652344 390.34375 1307.90527344 419.171875 c 128 + 1329.70214844 447.921875 1340.56152344 490.8125 1340.56152344 547.765625 c 2 + 1340.56152344 960.578125 l 1 + 1447.12402344 960.578125 l 1 + 1447.12402344 565.03125 l 2 + 1447.12402344 515.8125 1442.67089844 473.234375 1433.68652344 437.375 c 128 + 1424.70214844 401.515625 1410.32714844 371.90625 1390.48339844 348.546875 c 128 + 1370.63964844 325.1875 1344.85839844 307.765625 1313.21777344 296.28125 c 128 + 1281.49902344 284.71875 1242.67089844 279.015625 1196.57714844 279.015625 c 256 + 1150.48339844 279.015625 1111.57714844 284.71875 1079.93652344 296.28125 c 128 + 1048.21777344 307.765625 1022.51464844 325.1875 1002.67089844 348.546875 c 128 + 982.827148438 371.90625 968.374023438 401.515625 959.467773438 437.375 c 128 + 950.483398438 473.234375 946.030273438 515.8125 946.030273438 565.03125 c 2 + 946.030273438 960.578125 l 1 + 1052.59277344 960.578125 l 1 +EndSplineSet +EndChar + +StartChar: uniF028 +Encoding: 61480 61480 9 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +EndUndoOperation +UndoOperation +Index: 1 +Type: 3 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +1149 -164.5 m 5 + 999 -164.5 l 6 + 940 -164.5 896 -148.5 868 -118.5 c 132 + 840 -88.5 827 -47.5 827 3.5 c 6 + 827 83.5 l 5 + 768 90.5 713 108.5 666 135.5 c 132 + 619 162.5 579 200.5 546 245.5 c 132 + 513 290.5 486 345.5 468 409.5 c 132 + 450 473.5 441 544.5 441 625.5 c 4 + 441 714.5 452 793.5 474 861.5 c 132 + 496 929.5 526 985.5 566 1031.5 c 132 + 606 1077.5 654 1111.5 710 1135.5 c 132 + 766 1159.5 827 1171.5 896 1171.5 c 260 + 965 1171.5 1027 1159.5 1083 1135.5 c 132 + 1139 1111.5 1187 1077.5 1227 1031.5 c 132 + 1267 985.5 1297 929.5 1319 861.5 c 132 + 1341 793.5 1351 714.5 1351 625.5 c 4 + 1351 468.5 1319 344.5 1253 253.5 c 132 + 1188 162.5 1099 106.5 987 87.5 c 5 + 987 -20.5 l 5 + 1149 -20.5 l 5 + 1149 -164.5 l 5 +896 233.5 m 4 + 936 233.5 973 239.5 1007 253.5 c 132 + 1041 267.5 1070 288.5 1094 315.5 c 132 + 1118 342.5 1136 375.5 1149 413.5 c 132 + 1162 451.5 1168 495.5 1168 543.5 c 6 + 1168 709.5 l 6 + 1168 757.5 1162 801.5 1149 839.5 c 132 + 1136 877.5 1118 908.5 1094 935.5 c 132 + 1070 962.5 1041 983.5 1007 997.5 c 132 + 973 1011.5 936 1019.5 896 1019.5 c 4 + 855 1019.5 817 1011.5 784 997.5 c 132 + 751 983.5 723 962.5 699 935.5 c 132 + 675 908.5 657 877.5 644 839.5 c 132 + 631 801.5 624 757.5 624 709.5 c 6 + 624 543.5 l 6 + 624 495.5 631 451.5 644 413.5 c 132 + 657 375.5 675 342.5 699 315.5 c 132 + 723 288.5 751 267.5 784 253.5 c 132 + 817 239.5 855 233.5 896 233.5 c 4 +EndSplineSet +EndChar + +StartChar: uniF015 +Encoding: 61461 61461 10 +Width: 1792 +Flags: H +LayerCount: 2 +UndoRedoHistory +Layer: 1 +Undoes +UndoOperation +Index: 0 +Type: 1 +WasModified: 1 +WasOrder2: 0 +Layer: 2 +Width: 1792 +VWidth: 1792 +LBearingChange: 0 +UnicodeEnc: 0 +InstructionsLength: 0 +SplineSet +14.755859375 1069.5703125 m 5 + 259.302734375 1069.5703125 l 6 + 305.919921875 1069.5703125 341.357421875 1056.3515625 365.544921875 1029.84375 c 132 + 389.732421875 1003.3359375 401.826171875 964.734375 401.826171875 914.0390625 c 4 + 401.826171875 875.4375 393.458984375 845.203125 376.794921875 823.3359375 c 132 + 360.060546875 801.46875 336.154296875 789.09375 305.076171875 786.2109375 c 5 + 305.076171875 775.8046875 l 5 + 322.373046875 775.8046875 338.052734375 772.5 352.185546875 765.890625 c 132 + 366.248046875 759.28125 378.482421875 749.859375 388.888671875 737.765625 c 132 + 399.224609375 725.671875 407.310546875 711.328125 413.076171875 694.59375 c 132 + 418.841796875 677.859375 421.724609375 659.71875 421.724609375 640.171875 c 4 + 421.724609375 614.2265625 418.419921875 590.6015625 411.810546875 569.296875 c 132 + 405.130859375 547.9921875 395.779296875 529.7109375 383.685546875 514.453125 c 132 + 371.591796875 499.1953125 357.037109375 487.3828125 340.091796875 479.015625 c 132 + 323.076171875 470.6484375 304.232421875 466.5 283.490234375 466.5 c 6 + 14.755859375 466.5 l 5 + 14.755859375 1069.5703125 l 5 +106.373046875 546.8671875 m 5 + 256.701171875 546.8671875 l 6 + 278.568359375 546.8671875 295.302734375 552.7734375 306.833984375 564.5859375 c 132 + 318.294921875 576.3984375 324.060546875 595.2421875 324.060546875 621.1875 c 6 + 324.060546875 660.9140625 l 6 + 324.060546875 686.7890625 318.294921875 705.703125 306.833984375 717.515625 c 132 + 295.302734375 729.328125 278.568359375 735.234375 256.701171875 735.234375 c 6 + 106.373046875 735.234375 l 5 + 106.373046875 546.8671875 l 5 +106.373046875 813.84375 m 5 + 242.005859375 813.84375 l 6 + 262.185546875 813.84375 277.724609375 819.1875 288.623046875 829.8046875 c 132 + 299.591796875 840.4921875 305.076171875 857.859375 305.076171875 882.1171875 c 6 + 305.076171875 920.9296875 l 6 + 305.076171875 945.1875 299.591796875 962.5546875 288.623046875 973.2421875 c 132 + 277.724609375 983.859375 262.185546875 989.203125 242.005859375 989.203125 c 6 + 106.373046875 989.203125 l 5 + 106.373046875 813.84375 l 5 +518.755859375 466.5 m 5 + 518.755859375 1069.5703125 l 5 + 868.701171875 1069.5703125 l 5 + 868.701171875 989.203125 l 5 + 610.373046875 989.203125 l 5 + 610.373046875 812.9296875 l 5 + 843.599609375 812.9296875 l 5 + 843.599609375 733.4765625 l 5 + 610.373046875 733.4765625 l 5 + 610.373046875 546.8671875 l 5 + 868.701171875 546.8671875 l 5 + 868.701171875 466.5 l 5 + 518.755859375 466.5 l 5 +950.755859375 466.5 m 5 + 950.755859375 1069.5703125 l 5 + 1300.70117188 1069.5703125 l 5 + 1300.70117188 989.203125 l 5 + 1042.37304688 989.203125 l 5 + 1042.37304688 812.9296875 l 5 + 1275.59960938 812.9296875 l 5 + 1275.59960938 733.4765625 l 5 + 1042.37304688 733.4765625 l 5 + 1042.37304688 546.8671875 l 5 + 1300.70117188 546.8671875 l 5 + 1300.70117188 466.5 l 5 + 950.755859375 466.5 l 5 +1382.75585938 466.5 m 5 + 1382.75585938 1069.5703125 l 5 + 1621.25585938 1069.5703125 l 6 + 1673.07617188 1069.5703125 1712.09960938 1054.171875 1738.32617188 1023.375 c 132 + 1764.48242188 992.5078125 1777.63085938 948.0703125 1777.63085938 889.8515625 c 260 + 1777.63085938 831.703125 1764.48242188 787.1953125 1738.32617188 756.3984375 c 132 + 1712.09960938 725.53125 1673.07617188 710.1328125 1621.25585938 710.1328125 c 6 + 1474.37304688 710.1328125 l 5 + 1474.37304688 466.5 l 5 + 1382.75585938 466.5 l 5 +1474.37304688 791.34375 m 5 + 1613.45117188 791.34375 l 6 + 1635.31835938 791.34375 1651.91210938 796.96875 1663.16210938 808.21875 c 132 + 1674.34179688 819.46875 1679.96679688 837.46875 1679.96679688 862.21875 c 6 + 1679.96679688 917.484375 l 6 + 1679.96679688 942.3046875 1674.34179688 960.3046875 1663.16210938 971.484375 c 132 + 1651.91210938 982.734375 1635.31835938 988.359375 1613.45117188 988.359375 c 6 + 1474.37304688 988.359375 l 5 + 1474.37304688 791.34375 l 5 +EndSplineSet +EndUndoOperation +EndUndoes +Redoes +EndRedoes +EndUndoRedoHistory +Fore +SplineSet +14.755859375 927.0703125 m 1 + 259.302734375 927.0703125 l 2 + 305.919921875 927.0703125 341.357421875 913.8515625 365.544921875 887.34375 c 128 + 389.732421875 860.8359375 401.826171875 822.234375 401.826171875 771.5390625 c 0 + 401.826171875 732.9375 393.458984375 702.703125 376.794921875 680.8359375 c 128 + 360.060546875 658.96875 336.154296875 646.59375 305.076171875 643.7109375 c 1 + 305.076171875 633.3046875 l 1 + 322.373046875 633.3046875 338.052734375 630 352.185546875 623.390625 c 128 + 366.248046875 616.78125 378.482421875 607.359375 388.888671875 595.265625 c 128 + 399.224609375 583.171875 407.310546875 568.828125 413.076171875 552.09375 c 128 + 418.841796875 535.359375 421.724609375 517.21875 421.724609375 497.671875 c 0 + 421.724609375 471.7265625 418.419921875 448.1015625 411.810546875 426.796875 c 128 + 405.130859375 405.4921875 395.779296875 387.2109375 383.685546875 371.953125 c 128 + 371.591796875 356.6953125 357.037109375 344.8828125 340.091796875 336.515625 c 128 + 323.076171875 328.1484375 304.232421875 324 283.490234375 324 c 2 + 14.755859375 324 l 1 + 14.755859375 927.0703125 l 1 +106.373046875 404.3671875 m 1 + 256.701171875 404.3671875 l 2 + 278.568359375 404.3671875 295.302734375 410.2734375 306.833984375 422.0859375 c 128 + 318.294921875 433.8984375 324.060546875 452.7421875 324.060546875 478.6875 c 2 + 324.060546875 518.4140625 l 2 + 324.060546875 544.2890625 318.294921875 563.203125 306.833984375 575.015625 c 128 + 295.302734375 586.828125 278.568359375 592.734375 256.701171875 592.734375 c 2 + 106.373046875 592.734375 l 1 + 106.373046875 404.3671875 l 1 +106.373046875 671.34375 m 1 + 242.005859375 671.34375 l 2 + 262.185546875 671.34375 277.724609375 676.6875 288.623046875 687.3046875 c 128 + 299.591796875 697.9921875 305.076171875 715.359375 305.076171875 739.6171875 c 2 + 305.076171875 778.4296875 l 2 + 305.076171875 802.6875 299.591796875 820.0546875 288.623046875 830.7421875 c 128 + 277.724609375 841.359375 262.185546875 846.703125 242.005859375 846.703125 c 2 + 106.373046875 846.703125 l 1 + 106.373046875 671.34375 l 1 +518.755859375 324 m 1 + 518.755859375 927.0703125 l 1 + 868.701171875 927.0703125 l 1 + 868.701171875 846.703125 l 1 + 610.373046875 846.703125 l 1 + 610.373046875 670.4296875 l 1 + 843.599609375 670.4296875 l 1 + 843.599609375 590.9765625 l 1 + 610.373046875 590.9765625 l 1 + 610.373046875 404.3671875 l 1 + 868.701171875 404.3671875 l 1 + 868.701171875 324 l 1 + 518.755859375 324 l 1 +950.755859375 324 m 1 + 950.755859375 927.0703125 l 1 + 1300.70117188 927.0703125 l 1 + 1300.70117188 846.703125 l 1 + 1042.37304688 846.703125 l 1 + 1042.37304688 670.4296875 l 1 + 1275.59960938 670.4296875 l 1 + 1275.59960938 590.9765625 l 1 + 1042.37304688 590.9765625 l 1 + 1042.37304688 404.3671875 l 1 + 1300.70117188 404.3671875 l 1 + 1300.70117188 324 l 1 + 950.755859375 324 l 1 +1382.75585938 324 m 1 + 1382.75585938 927.0703125 l 1 + 1621.25585938 927.0703125 l 2 + 1673.07617188 927.0703125 1712.09960938 911.671875 1738.32617188 880.875 c 128 + 1764.48242188 850.0078125 1777.63085938 805.5703125 1777.63085938 747.3515625 c 256 + 1777.63085938 689.203125 1764.48242188 644.6953125 1738.32617188 613.8984375 c 128 + 1712.09960938 583.03125 1673.07617188 567.6328125 1621.25585938 567.6328125 c 2 + 1474.37304688 567.6328125 l 1 + 1474.37304688 324 l 1 + 1382.75585938 324 l 1 +1474.37304688 648.84375 m 1 + 1613.45117188 648.84375 l 2 + 1635.31835938 648.84375 1651.91210938 654.46875 1663.16210938 665.71875 c 128 + 1674.34179688 676.96875 1679.96679688 694.96875 1679.96679688 719.71875 c 2 + 1679.96679688 774.984375 l 2 + 1679.96679688 799.8046875 1674.34179688 817.8046875 1663.16210938 828.984375 c 128 + 1651.91210938 840.234375 1635.31835938 845.859375 1613.45117188 845.859375 c 2 + 1474.37304688 845.859375 l 1 + 1474.37304688 648.84375 l 1 +EndSplineSet +EndChar +EndChars +EndSplineFont diff --git a/res/icons.ttf b/res/icons.ttf new file mode 100644 index 00000000..ee215107 Binary files /dev/null and b/res/icons.ttf differ diff --git a/src/engine/config.cpp b/src/engine/config.cpp index 6d90a49f..ef8bf3a0 100644 --- a/src/engine/config.cpp +++ b/src/engine/config.cpp @@ -237,56 +237,60 @@ bool DivConfig::loadFromBase64(const char* buf) { } bool DivConfig::getBool(String key, bool fallback) const { - try { - String val=conf.at(key); - if (val=="true") { + auto val=conf.find(key); + if (val!=conf.cend()) { + if (val->second=="true") { return true; - } else if (val=="false") { + } else if (val->second=="false") { return false; } - } catch (std::out_of_range& e) { } return fallback; } int DivConfig::getInt(String key, int fallback) const { - try { - String val=conf.at(key); - int ret=std::stoi(val); - return ret; - } catch (std::out_of_range& e) { - } catch (std::invalid_argument& e) { + auto val=conf.find(key); + if (val!=conf.cend()) { + try { + int ret=std::stoi(val->second); + return ret; + } catch (std::out_of_range& e) { + } catch (std::invalid_argument& e) { + } } return fallback; } float DivConfig::getFloat(String key, float fallback) const { - try { - String val=conf.at(key); - float ret=std::stof(val); - return ret; - } catch (std::out_of_range& e) { - } catch (std::invalid_argument& e) { + auto val=conf.find(key); + if (val!=conf.cend()) { + try { + float ret=std::stof(val->second); + return ret; + } catch (std::out_of_range& e) { + } catch (std::invalid_argument& e) { + } } return fallback; } double DivConfig::getDouble(String key, double fallback) const { - try { - String val=conf.at(key); - double ret=std::stod(val); - return ret; - } catch (std::out_of_range& e) { - } catch (std::invalid_argument& e) { + auto val=conf.find(key); + if (val!=conf.cend()) { + try { + double ret=std::stod(val->second); + return ret; + } catch (std::out_of_range& e) { + } catch (std::invalid_argument& e) { + } } return fallback; } String DivConfig::getString(String key, String fallback) const { - try { - String val=conf.at(key); - return val; - } catch (std::out_of_range& e) { + auto val=conf.find(key); + if (val!=conf.cend()) { + return val->second; } return fallback; } @@ -294,37 +298,34 @@ String DivConfig::getString(String key, String fallback) const { std::vector DivConfig::getIntList(String key, std::initializer_list fallback) const { String next; std::vector ret; - try { - String val=conf.at(key); - - for (char i: val) { - if (i==',') { + auto val=conf.find(key); + if (val!=conf.cend()) { + try { + for (char i: val->second) { + if (i==',') { + int num=std::stoi(next); + ret.push_back(num); + next=""; + } else { + next+=i; + } + } + if (!next.empty()) { int num=std::stoi(next); ret.push_back(num); - next=""; - } else { - next+=i; } - } - if (!next.empty()) { - int num=std::stoi(next); - ret.push_back(num); - } - return ret; - } catch (std::out_of_range& e) { - } catch (std::invalid_argument& e) { + return ret; + } catch (std::out_of_range& e) { + } catch (std::invalid_argument& e) { + } } return fallback; } bool DivConfig::has(String key) const { - try { - String test=conf.at(key); - } catch (std::out_of_range& e) { - return false; - } - return true; + auto val=conf.find(key); + return (val!=conf.cend()); } void DivConfig::set(String key, bool value) { diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index acd0cc77..875ff487 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -485,6 +485,12 @@ class DivDispatch { */ virtual bool keyOffAffectsPorta(int ch); + /** + * test whether volume is global. + * @return whether it is. + */ + virtual bool isVolGlobal(); + /** * get the lowest note in a portamento. * @param ch the channel in question. diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 56c3625d..61d8344a 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -79,6 +79,7 @@ #include "platform/sm8521.h" #include "platform/pv1000.h" #include "platform/k053260.h" +#include "platform/ted.h" #include "platform/pcmdac.h" #include "platform/dummy.h" #include "../ta-log.h" @@ -215,13 +216,6 @@ void DivDispatchContainer::clear() { if (dispatch->getDCOffRequired()) { dcOffCompensation=true; } - // run for one cycle to determine DC offset - // TODO: SAA1099 doesn't like that - /*dispatch->acquire(bbIn[0],bbIn[1],0,1); - temp[0]=bbIn[0][0]; - temp[1]=bbIn[1][0]; - prevSample[0]=temp[0]; - prevSample[1]=temp[1];*/ } void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, double gotRate, const DivConfig& flags) { @@ -478,7 +472,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_NAMCO: dispatch=new DivPlatformNamcoWSG; - // Pac-Man (TODO: support Pole Position?) + // Pac-Man ((DivPlatformNamcoWSG*)dispatch)->setDeviceType(1); break; case DIV_SYSTEM_NAMCO_15XX: @@ -507,6 +501,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_K053260: dispatch=new DivPlatformK053260; break; + case DIV_SYSTEM_TED: + dispatch=new DivPlatformTED; + break; case DIV_SYSTEM_PCM_DAC: dispatch=new DivPlatformPCMDAC; break; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c32b5311..6421de73 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -107,7 +107,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0xea: return "EAxx: Legato"; case 0xeb: - return "EBxx: Set sample bank"; + return "EBxx: Set LEGACY sample mode bank"; case 0xec: return "ECxx: Note cut"; case 0xed: @@ -927,12 +927,13 @@ void DivEngine::runExportThread() { } } - float* outBuf[2]; + float* outBuf[DIV_MAX_OUTPUTS]; + memset(outBuf,0,sizeof(void*)*DIV_MAX_OUTPUTS); outBuf[0]=new float[EXPORT_BUFSIZE]; outBuf[1]=new float[EXPORT_BUFSIZE]; short* sysBuf[DIV_MAX_CHIPS]; for (int i=0; igetOutputCount()]; } // take control of audio output @@ -3112,6 +3113,8 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) { song.ins.push_back(which); song.insLen=song.ins.size(); checkAssetDir(song.insDir,song.ins.size()); + checkAssetDir(song.waveDir,song.wave.size()); + checkAssetDir(song.sampleDir,song.sample.size()); saveLock.unlock(); BUSY_END; return song.insLen; @@ -3850,6 +3853,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(); @@ -4041,6 +4061,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; @@ -4063,6 +4104,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; @@ -4079,6 +4121,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; @@ -4106,6 +4149,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; @@ -4122,6 +4166,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 5c81f3e1..effa0273 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -56,8 +56,8 @@ #define DIV_UNSTABLE -#define DIV_VERSION "dev164" -#define DIV_ENGINE_VERSION 164 +#define DIV_VERSION "dev165" +#define DIV_ENGINE_VERSION 165 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -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); @@ -602,7 +605,7 @@ class DivEngine { // - -2 to add a whole loop of trailing SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1); // dump to ZSM. - SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true); + SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true); // dump command stream. SafeWriter* saveCommand(bool binary=false); // export to an audio file diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 77f3f3dc..9cf94547 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; } @@ -2344,7 +2346,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } - if (ds.version>=136) song.patchbayAuto=reader.readC(); + if (ds.version>=136) ds.patchbayAuto=reader.readC(); if (ds.version>=138) { ds.brokenPortaLegato=reader.readC(); @@ -2941,6 +2943,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // Namco 163 pitch compensation compat + if (ds.version<165) { + for (int i=0; irate=rate; diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 6ca85ffe..15ff00d9 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -566,6 +566,10 @@ bool DivPlatformC64::getWantPreNote() { return true; } +bool DivPlatformC64::isVolGlobal() { + return true; +} + float DivPlatformC64::getPostAmp() { return (sidCore==1)?3.0f:1.0f; } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 0da59fc8..def32eaa 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -105,6 +105,7 @@ class DivPlatformC64: public DivDispatch { void notifyInsChange(int ins); bool getDCOffRequired(); bool getWantPreNote(); + bool isVolGlobal(); float getPostAmp(); DivMacroInt* getChanMacroInt(int ch); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index adb342f3..4f8745bd 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) { @@ -1253,6 +1219,7 @@ int DivPlatformES5506::init(DivEngine* p, int channels, int sugRate, const DivCo dumpWrites=false; skipRegisterWrites=false; volScale=0; + curPage=0; for (int i=0; i<32; i++) { isMuted[i]=false; diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index c0fb7dd2..9be217b9 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 8d61b334..7e2086db 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]; @@ -255,7 +254,7 @@ void DivPlatformGB::tick(bool sysTick) { chan[i].sweepChanged=true; break; case DivInstrumentGB::DIV_GB_HWCMD_WAIT: - chan[i].hwSeqDelay=data+1; + chan[i].hwSeqDelay=(data+1)*parent->tickMult; leave=true; break; case DivInstrumentGB::DIV_GB_HWCMD_WAIT_REL: diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 20e87d61..3ce42299 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)); @@ -953,6 +972,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } case DIV_CMD_FM_EXTCH: { if (extSys) { + if (extMode==(bool)c.value) break; extMode=c.value; immWrite(0x27,extMode?0x40:0); } @@ -1314,11 +1334,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 +1351,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 c9de0493..d618c689 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 d4ac2760..d8760be9 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 @@ -143,6 +144,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].insChanged=true; } opChan[ch].ins=c.value; + chan[extChanOffs].ins=opChan[ch].ins; break; case DIV_CMD_PANNING: { if (c.value==0 && c.value2==0) { @@ -218,8 +220,15 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_EXTCH: { + if (extMode==(bool)c.value) break; extMode=c.value; immWrite(0x27,extMode?0x40:0); + if (!extMode) { + for (int i=0; i<4; i++) { + opChan[i].insChanged=true; + } + chan[extChanOffs].insChanged=true; + } break; } case DIV_CMD_FM_LFO: { @@ -430,17 +439,21 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) { return; } isOpMuted[ch-2]=mute; - - int ordch=orderedOps[ch-2]; - unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator op=chan[2].state.op[ordch]; - if (isOpMuted[ch-2] || !op.enable) { - rWrite(baseAddr+0x40,127); - } else { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127)); - } - rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); + DivPlatformGenesis::muteChannel(extChanOffs,IS_EXTCH_MUTED); + + if (extMode) { + int ordch=orderedOps[ch-2]; + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=chan[2].state.op[ordch]; + if (isOpMuted[ch-2] || !op.enable) { + rWrite(baseAddr+0x40,127); + } else { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127)); + } + + rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); + } } static int opChanOffsL[4]={ @@ -476,13 +489,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 +524,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 +615,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 +643,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 +676,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 +691,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 7ba1cc2c..5791d242 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/msm5232.cpp b/src/engine/platform/msm5232.cpp index 4a810728..e4564134 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -56,10 +56,10 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) { for (int i=0; i<8; i++) { int o=( - ((regPool[12+(i>>4)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+ - ((regPool[12+(i>>4)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+ - ((regPool[12+(i>>4)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+ - ((regPool[12+(i>>4)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0) + ((regPool[12+(i>>2)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+ + ((regPool[12+(i>>2)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0) )<<2; oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767); } diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 7f4d2e63..e5de1645 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -31,10 +31,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() { } void DivPlatformMSM6258::acquire(short** buf, size_t len) { - short* outs[2]={ - &msmOut, - NULL - }; for (size_t h=0; hsound_stream_update(outs,1); + msm->sound_stream_update(&msmOut,1); msmDividerCount=msmDivider; } msmClockCount=msmClock; @@ -390,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 2c18d90c..6be120c2 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/n163.cpp b/src/engine/platform/n163.cpp index 30061ceb..e24459ba 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -34,7 +34,7 @@ rWriteMask(0x78-(c<<3)+(a&7),v,m) \ } -#define CHIP_FREQBASE (15*32768) +#define CHIP_FREQBASE (15*524288) const char* regCheatSheetN163[]={ "FreqL7", "40", @@ -256,7 +256,12 @@ void DivPlatformN163::tick(bool sysTick) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { // TODO: what is this mess? chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); - chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/16); + if (lenCompensate) { + chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/256); + } else { + chan[i].freq*=(chanMax+1); + chan[i].freq>>=3; + } if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { @@ -359,13 +364,13 @@ int DivPlatformN163::dispatch(DivCommand c) { int destFreq=NOTE_FREQUENCY(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { - chan[c.chan].baseFreq+=c.value; + chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:16); if (chan[c.chan].baseFreq>=destFreq) { chan[c.chan].baseFreq=destFreq; return2=true; } } else { - chan[c.chan].baseFreq-=c.value; + chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:16); if (chan[c.chan].baseFreq<=destFreq) { chan[c.chan].baseFreq=destFreq; return2=true; @@ -570,6 +575,8 @@ void DivPlatformN163::setFlags(const DivConfig& flags) { oscBuf[i]->rate=rate/(initChanMax+1); } + lenCompensate=flags.getBool("lenCompensate",false); + // needed to make sure changing channel count won't trigger glitches reset(); } diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 3ebcbc44..c5ec64b7 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -61,7 +61,7 @@ class DivPlatformN163: public DivDispatch { unsigned char initChanMax; unsigned char chanMax; short loadWave, loadPos; - bool multiplex; + bool multiplex, lenCompensate; n163_core n163; unsigned char regPool[128]; diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 5b804b23..5ac493f1 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 8fac5235..657a084e 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 d081fabc..2d283d9a 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -28,6 +28,8 @@ #define CHIP_FREQBASE 1180068 +#define DRUM_VOL(_x) (drumActivated[_x]?drumVol[_x]:15) + const unsigned char cycleMapOPLL[18]={ 8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9 }; @@ -52,7 +54,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) { QueuedWrite& w=writes.front(); if (w.addrOrVal) { OPLL_Write(&fm,1,w.val); - //printf("write: %x = %.2x\n",w.addr,w.val); + //logV("write: %x = %.2x",w.addr,w.val); regPool[w.addr&0xff]=w.val; writes.pop(); delay=21; @@ -104,10 +106,10 @@ void DivPlatformOPLL::tick(bool sysTick) { if (i>=6 && properDrums) { drumVol[i-6]=15-chan[i].outVol; - rWrite(0x36,drumVol[0]); - rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); - rWrite(0x38,drumVol[3]|(drumVol[2]<<4)); - } else if (i<6 || !drums) { + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); + } else if (i<6 || !crapDrums) { if (i<9) { rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4)); } @@ -230,13 +232,16 @@ void DivPlatformOPLL::tick(bool sysTick) { if (i>=6 && properDrums) { drumState&=~(0x10>>(i-6)); immWrite(0x0e,0x20|drumState); - } else if (i>=6 && drums) { + //logV("properDrums %d",i); + } else if (i>=6 && crapDrums) { drumState&=~(0x10>>(chan[i].note%12)); immWrite(0x0e,0x20|drumState); + //logV("drums %d",i); } else { if (i<9) { immWrite(0x20+i,(chan[i].freqH)|(chan[i].state.alg?0x20:0)); } + //logV("normal %d",i); } //chan[i].keyOn=false; chan[i].keyOff=false; @@ -253,7 +258,7 @@ void DivPlatformOPLL::tick(bool sysTick) { for (int i=0; i<11; i++) { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE); - if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq; + if (chan[i].fixedFreq>0 && properDrums) chan[i].freq=chan[i].fixedFreq; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; int freqt=toFreq(chan[i].freq); @@ -262,7 +267,21 @@ void DivPlatformOPLL::tick(bool sysTick) { if (i>=6 && properDrums && (i<9 || !noTopHatFreq)) { immWrite(0x10+drumSlot[i],freqt&0xff); immWrite(0x20+drumSlot[i],freqt>>8); - } else if (i<6 || !drums) { + switch (i) { + case 7: + lastFreqSH=0; + break; + case 8: + lastFreqTT=0; + break; + case 9: + lastFreqTT=1; + break; + case 19: + lastFreqSH=1; + break; + } + } else if (i<6 || !crapDrums) { if (i<9) { immWrite(0x10+i,freqt&0xff); } @@ -275,7 +294,7 @@ void DivPlatformOPLL::tick(bool sysTick) { immWrite(0x0e,0x20|drumState); } chan[i].keyOn=false; - } else if (chan[i].keyOn && i>=6 && drums) { + } else if (chan[i].keyOn && i>=6 && crapDrums) { //printf("%d\n",chan[i].note%12); drumState|=(0x10>>(chan[i].note%12)); immWrite(0x0e,0x20|drumState); @@ -362,22 +381,24 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) { } if (chan[ch].state.opllPreset==16) { // compatible drums mode if (ch>=6) { - drums=true; - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x18,0xC0); - immWrite(0x28,0x01); + if (!properDrumsSys) { + crapDrums=true; + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x18,0xC0); + immWrite(0x28,0x01); + } } } else { if (ch>=6) { - if (drums) { - drums=false; + if (crapDrums) { + crapDrums=false; immWrite(0x0e,0); drumState=0; } @@ -389,6 +410,45 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) { } } +void DivPlatformOPLL::switchMode(bool mode) { + if (mode==properDrums) return; + if (mode) { + //logV("mode switch to DRUMS"); + for (int i=0; i<5; i++) { + drumActivated[i]=chan[6+i].keyOn; + } + + immWrite(0x26,0); + immWrite(0x27,0); + immWrite(0x28,0); + immWrite(0x16,0); + immWrite(0x17,0); + immWrite(0x18,0); + immWrite(0x0e,0x20); + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); + oldWrites[0x36]=-1; + oldWrites[0x37]=-1; + oldWrites[0x38]=-1; + } else { + //logV("mode switch to NORMAL"); + immWrite(0x0e,0x20); + immWrite(0x0e,0x00); + for (int i=6; i<9; i++) { + if (chan[i].active) { + chan[i].freqChanged=true; + chan[i].keyOff=false; + chan[i].keyOn=true; + oldWrites[0x30+i]=-1; + } + chan[i].insChanged=true; + } + } + properDrums=mode; + drumState=0; +} + int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -405,18 +465,31 @@ int DivPlatformOPLL::dispatch(DivCommand c) { if (c.chan>=6 && properDrums) { // drums mode chan[c.chan].insChanged=false; + drumActivated[c.chan-6]=true; if (c.value!=DIV_NOTE_NULL) { if (chan[c.chan].state.opllPreset==16 && chan[c.chan].state.fixedDrums) { - switch (c.chan) { - case 6: - chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9); - break; - case 7: case 10: - chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9); - break; - case 8: case 9: - chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9); - break; + if (fixedAll) { + chan[6].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9); + chan[7].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9); + chan[8].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9); + chan[9].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9); + chan[10].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9); + + chan[7].freqChanged=true; + chan[8].freqChanged=true; + chan[9].freqChanged=true; + } else { + switch (c.chan) { + case 6: + chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9); + break; + case 7: case 10: + chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9); + break; + case 8: case 9: + chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9); + break; + } } } else { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); @@ -426,6 +499,10 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } chan[c.chan].keyOn=true; chan[c.chan].active=true; + + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); break; } @@ -436,7 +513,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; - if (c.chan>=6 && drums) { + if (c.chan>=6 && crapDrums) { switch (chan[c.chan].note%12) { case 0: // kick drumVol[0]=(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15); @@ -454,9 +531,9 @@ int DivPlatformOPLL::dispatch(DivCommand c) { drumVol[4]=(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15); break; } - rWrite(0x36,drumVol[0]); - rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); - rWrite(0x38,drumVol[3]|(drumVol[2]<<4)); + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); } chan[c.chan].freqChanged=true; } @@ -491,11 +568,11 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } if (c.chan>=6 && properDrums) { drumVol[c.chan-6]=15-chan[c.chan].outVol; - rWrite(0x36,drumVol[0]); - rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); - rWrite(0x38,drumVol[3]|(drumVol[2]<<4)); + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); break; - } else if (c.chan<6 || !drums) { + } else if (c.chan<6 || !crapDrums) { if (c.chan<9) { rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); } @@ -553,7 +630,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { if (c.chan>=9 && !properDrums) return 0; - if (c.chan<6 || (!drums && !properDrums)) { + if (c.chan<6 || (!crapDrums && !properDrums)) { if (chan[c.chan].insChanged) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL); commitState(c.chan,ins); @@ -768,14 +845,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { case DIV_CMD_FM_EXTCH: if (!properDrumsSys) break; if ((int)properDrums==c.value) break; - if (c.value) { - properDrums=true; - immWrite(0x0e,0x20); - } else { - properDrums=false; - immWrite(0x0e,0x00); - drumState=0; - } + switchMode(c.value); break; case DIV_CMD_MACRO_OFF: chan[c.chan].std.mask(c.value,true); @@ -830,7 +900,7 @@ void DivPlatformOPLL::forceIns() { } } } - if (drums) { // WHAT?! FIX THIS! + if (crapDrums) { // WHAT?! FIX THIS! immWrite(0x16,0x20); immWrite(0x26,0x05); immWrite(0x16,0x20); @@ -842,11 +912,25 @@ void DivPlatformOPLL::forceIns() { immWrite(0x18,0xC0); immWrite(0x28,0x01); } - // restore drum volumes + // restore drum volumes and state if (properDrums) { - rWrite(0x36,drumVol[0]); - rWrite(0x37,drumVol[1]|(drumVol[4]<<4)); - rWrite(0x38,drumVol[3]|(drumVol[2]<<4)); + rWrite(0x36,DRUM_VOL(0)); + rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4)); + rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4)); + + if (lastFreqSH==0) { + chan[7].freqChanged=true; + } else if (lastFreqSH==1) { + chan[10].freqChanged=true; + } + + if (lastFreqTT==0) { + chan[8].freqChanged=true; + } else if (lastFreqTT==1) { + chan[9].freqChanged=true; + } + + chan[6].freqChanged=true; } drumState=0; } @@ -927,16 +1011,18 @@ void DivPlatformOPLL::reset() { drumState=0; lastCustomMemory=-1; - drumVol[0]=0; - drumVol[1]=0; - drumVol[2]=0; - drumVol[3]=0; - drumVol[4]=0; + for (int i=0; i<5; i++) { + drumVol[i]=0; + drumActivated[i]=true; + } delay=0; - drums=false; + crapDrums=false; properDrums=properDrumsSys; + lastFreqSH=-1; + lastFreqTT=-1; + if (properDrums) { immWrite(0x0e,0x20); } @@ -1002,6 +1088,7 @@ void DivPlatformOPLL::setFlags(const DivConfig& flags) { oscBuf[i]->rate=rate/2; } noTopHatFreq=flags.getBool("noTopHatFreq",false); + fixedAll=flags.getBool("fixedAll",false); } int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 7333f729..8bb6baa2 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -59,12 +59,18 @@ class DivPlatformOPLL: public DivDispatch { unsigned char lastBusy; unsigned char drumState; unsigned char drumVol[5]; + bool drumActivated[5]; + + // -1: undefined + // 0: snare/tom + // 1: hi-hat/top + signed char lastFreqSH, lastFreqTT; unsigned char regPool[256]; bool useYMFM; - bool drums; - bool properDrums, properDrumsSys, noTopHatFreq; + bool crapDrums; + bool properDrums, properDrumsSys, noTopHatFreq, fixedAll; bool vrc7; unsigned char patchSet; @@ -75,6 +81,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 a275c635..593c41b8 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 0437a90c..d9315b5e 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/pet.cpp b/src/engine/platform/pet.cpp index 4d085581..e7f13575 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -283,6 +283,7 @@ void DivPlatformPET::reset() { memset(regPool,0,16); chan[0]=Channel(); chan[0].std.setEngine(parent); + rWrite(10,chan[0].wave); } int DivPlatformPET::getOutputCount() { diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 640f364e..76aa62fc 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 43401ebc..a0312161 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/sound/oki/okim6258.cpp b/src/engine/platform/sound/oki/okim6258.cpp index 5bf6714d..a6c3e26f 100644 --- a/src/engine/platform/sound/oki/okim6258.cpp +++ b/src/engine/platform/sound/oki/okim6258.cpp @@ -135,9 +135,9 @@ void okim6258_device::device_reset() // sound_stream_update - handle a stream update //------------------------------------------------- -void okim6258_device::sound_stream_update(short** outputs, int len) +void okim6258_device::sound_stream_update(short* output, int len) { - short* buffer = outputs[0]; + short* buffer = output; if (m_status & STATUS_PLAYING) { diff --git a/src/engine/platform/sound/oki/okim6258.h b/src/engine/platform/sound/oki/okim6258.h index 88a429d3..b3dc65ef 100644 --- a/src/engine/platform/sound/oki/okim6258.h +++ b/src/engine/platform/sound/oki/okim6258.h @@ -43,7 +43,7 @@ public: void device_clock_changed(); // sound stream updates - void sound_stream_update(short** outputs, int len); + void sound_stream_update(short* output, int len); private: int16_t clock_adpcm(uint8_t nibble); diff --git a/src/engine/platform/sound/ted-sound.c b/src/engine/platform/sound/ted-sound.c new file mode 100644 index 00000000..c0366299 --- /dev/null +++ b/src/engine/platform/sound/ted-sound.c @@ -0,0 +1,231 @@ +/* + * ted-sound.c + * + * Written by + * Andreas Boose + * Tibor Biczo + * Marco van den Heuvel + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include +#include +#include + +#include "ted-sound.h" + +/* ------------------------------------------------------------------------- */ + +/* FIXME: Find proper volume multiplier. */ +const int16_t volume_tab[16] = { + 0x0000, 0x0800, 0x1000, 0x1800, 0x2000, 0x2800, 0x3000, 0x3800, + 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff +}; + +int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int scc) +{ + int i; + int j; + int16_t volume; + + if (snd->digital) { + for (i = 0; i < nr; i++) { + pbuf[i] = (snd->volume * (snd->voice0_output_enabled + snd->voice1_output_enabled)); + } + } else { + for (i = 0; i < nr; i++) { + snd->sample_position_remainder += snd->sample_length_remainder; + if (snd->sample_position_remainder >= snd->speed) { + snd->sample_position_remainder -= snd->speed; + snd->sample_position_integer++; + } + snd->sample_position_integer += snd->sample_length_integer; + if (snd->sample_position_integer >= 8) { + /* Advance state engine */ + uint32_t ticks = snd->sample_position_integer >> 3; + if (snd->voice0_accu <= ticks) { + uint32_t delay = ticks - snd->voice0_accu; + snd->voice0_sign ^= 1; + snd->voice0_accu = 1023 - snd->voice0_reload; + if (snd->voice0_accu == 0) { + snd->voice0_accu = 1024; + } + if (delay >= snd->voice0_accu) { + snd->voice0_sign = ((delay / snd->voice0_accu) + & 1) ? snd->voice0_sign ^ 1 + : snd->voice0_sign; + snd->voice0_accu = snd->voice0_accu - (delay % snd->voice0_accu); + } else { + snd->voice0_accu -= delay; + } + } else { + snd->voice0_accu -= ticks; + } + + if (snd->voice1_accu <= ticks) { + uint32_t delay = ticks - snd->voice1_accu; + snd->voice1_sign ^= 1; + snd->noise_shift_register + = (snd->noise_shift_register << 1) + + ( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^ + ((snd->noise_shift_register >> 5) & 1) ^ + ((snd->noise_shift_register >> 4) & 1) ^ + ((snd->noise_shift_register >> 1) & 1)); + snd->voice1_accu = 1023 - snd->voice1_reload; + if (snd->voice1_accu == 0) { + snd->voice1_accu = 1024; + } + if (delay >= snd->voice1_accu) { + snd->voice1_sign = ((delay / snd->voice1_accu) + & 1) ? snd->voice1_sign ^ 1 + : snd->voice1_sign; + for (j = 0; j < (int)(delay / snd->voice1_accu); + j++) { + snd->noise_shift_register + = (snd->noise_shift_register << 1) + + ( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^ + ((snd->noise_shift_register >> 5) & 1) ^ + ((snd->noise_shift_register >> 4) & 1) ^ + ((snd->noise_shift_register >> 1) & 1)); + } + snd->voice1_accu = snd->voice1_accu - (delay % snd->voice1_accu); + } else { + snd->voice1_accu -= delay; + } + } else { + snd->voice1_accu -= ticks; + } + } + snd->sample_position_integer = snd->sample_position_integer & 7; + + volume = 0; + + if (snd->voice0_output_enabled && snd->voice0_sign) { + volume += snd->volume; + } + if (snd->voice1_output_enabled && !snd->noise && snd->voice1_sign) { + volume += snd->volume; + } + if (snd->voice1_output_enabled && snd->noise && (!(snd->noise_shift_register & 1))) { + volume += snd->volume; + } + + pbuf[i] = volume; + } + } + return nr; +} + +int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec) +{ + uint8_t val; + memset(snd,0,sizeof(struct plus4_sound_s)); + + snd->speed = speed; + snd->sample_length_integer = cycles_per_sec / speed; + snd->sample_length_remainder = cycles_per_sec % speed; + snd->sample_position_integer = 0; + snd->sample_position_remainder = 0; + snd->noise_shift_register = 0; + + snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8)); + snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8)); + val = snd->plus4_sound_data[3]; + snd->volume = volume_tab[val & 0x0f]; + snd->voice0_output_enabled = (val & 0x10) ? 1 : 0; + snd->voice1_output_enabled = (val & 0x60) ? 1 : 0; + snd->noise = ((val & 0x60) == 0x40) ? 1 : 0; + snd->digital = val & 0x80; + if (snd->digital) { + snd->voice0_sign = 1; + snd->voice0_accu = 0; + snd->voice1_sign = 1; + snd->voice1_accu = 0; + snd->noise_shift_register = 0; + } + + return 1; +} + +void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val) +{ + switch (addr) { + case 0x0e: + snd->plus4_sound_data[0] = val; + snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8)); + break; + case 0x0f: + snd->plus4_sound_data[1] = val; + snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8)); + break; + case 0x10: + snd->plus4_sound_data[2] = val & 3; + snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8)); + break; + case 0x11: + snd->volume = volume_tab[val & 0x0f]; + snd->voice0_output_enabled = (val & 0x10) ? 1 : 0; + snd->voice1_output_enabled = (val & 0x60) ? 1 : 0; + snd->noise = ((val & 0x60) == 0x40) ? 1 : 0; + snd->digital = val & 0x80; + if (snd->digital) { + snd->voice0_sign = 1; + snd->voice0_accu = 0; + snd->voice1_sign = 1; + snd->voice1_accu = 0; + snd->noise_shift_register = 0; + } + snd->plus4_sound_data[3] = val; + break; + case 0x12: + snd->plus4_sound_data[4] = val & 3; + snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8)); + break; + } +} + +uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr) +{ + switch (addr) { + case 0x0e: + return snd->plus4_sound_data[0]; + case 0x0f: + return snd->plus4_sound_data[1]; + case 0x10: + return snd->plus4_sound_data[2] | 0xc0; + case 0x11: + return snd->plus4_sound_data[3]; + case 0x12: + return snd->plus4_sound_data[4]; + } + + return 0; +} + +void ted_sound_reset(struct plus4_sound_s* snd) +{ + uint16_t i; + + snd->noise_shift_register = 0; + for (i = 0x0e; i <= 0x12; i++) { + ted_sound_machine_store(snd,i,0); + } +} diff --git a/src/engine/platform/sound/ted-sound.h b/src/engine/platform/sound/ted-sound.h new file mode 100644 index 00000000..d2c6de1c --- /dev/null +++ b/src/engine/platform/sound/ted-sound.h @@ -0,0 +1,81 @@ +/* + * ted-sound.h + * + * Written by + * Andreas Boose + * Marco van den Heuvel + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#ifndef VICE_TEDSOUND_H +#define VICE_TEDSOUND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct plus4_sound_s { + /* Voice 0 collect number of cycles elapsed */ + uint32_t voice0_accu; + /* Voice 0 toggle sign and reload accu if accu reached 0 */ + uint32_t voice0_reload; + /* Voice 0 sign of the square wave */ + int16_t voice0_sign; + uint8_t voice0_output_enabled; + + /* Voice 1 collect number of cycles elapsed */ + uint32_t voice1_accu; + /* Voice 1 toggle sign and reload accu if accu reached 0 */ + uint32_t voice1_reload; + /* Voice 1 sign of the square wave */ + int16_t voice1_sign; + uint8_t voice1_output_enabled; + + /* Volume multiplier */ + int16_t volume; + /* 8 cycles units per sample */ + uint32_t speed; + uint32_t sample_position_integer; + uint32_t sample_position_remainder; + uint32_t sample_length_integer; + uint32_t sample_length_remainder; + /* Digital output? */ + uint8_t digital; + /* Noise generator active? */ + uint8_t noise; + uint8_t noise_shift_register; + + /* Registers */ + uint8_t plus4_sound_data[5]; +}; + +int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec); +int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int sound_chip_channels); +void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val); +uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr); +void ted_sound_reset(struct plus4_sound_s* snd); + +#ifdef __cplusplus +}; +#endif +#endif diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index b38b9f86..3c591cda 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -626,7 +626,7 @@ void DivPlatformSoundUnit::quit() { delete oscBuf[i]; } delete su; - delete sampleMem; + delete[] sampleMem; } DivPlatformSoundUnit::~DivPlatformSoundUnit() { diff --git a/src/engine/platform/ted.cpp b/src/engine/platform/ted.cpp new file mode 100644 index 00000000..a1e431bb --- /dev/null +++ b/src/engine/platform/ted.cpp @@ -0,0 +1,358 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ted.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } + +#define CHIP_DIVIDER 8 + +const char* regCheatSheetTED[]={ + "Freq0L", "0e", + "Freq1L", "0f", + "Freq1H", "10", + "Control", "11", + "Freq0H", "12", + NULL +}; + +const char** DivPlatformTED::getRegisterSheet() { + return regCheatSheetTED; +} + +void DivPlatformTED::acquire(short** buf, size_t len) { + for (size_t h=0; hdata[oscBuf[0]->needle++]=(ted.voice0_output_enabled && ted.voice0_sign)?(ted.volume<<1):0; + oscBuf[1]->data[oscBuf[1]->needle++]=(ted.voice1_output_enabled && ((ted.noise && (!(ted.noise_shift_register&1))) || (!ted.noise && ted.voice1_sign)))?(ted.volume<<1):0; + } +} + +void DivPlatformTED::tick(bool sysTick) { + bool resetPhase=false; + + for (int _i=0; _i<2; _i++) { + int i=chanOrder[_i]; + + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,MIN(8,chan[i].std.vol.val),8); + updateCtrl=true; + vol=chan[i].outVol; + } + if (chan[i].std.duty.had) { + chan[i].noise=chan[i].std.duty.val&2; + chan[i].square=chan[i].std.duty.val&1; + chan[i].freqChanged=true; + updateCtrl=true; + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + chan[i].freq=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)-1; + if (i==1 && chan[i].noise && !chan[i].square) chan[i].freq>>=4; + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>1023) chan[i].freq=1023; + + if (i==1) { + rWrite(0x0f,(1022-chan[i].freq)&0xff); + rWrite(0x10,((1022-chan[i].freq)>>8)&0xff); + } else { + rWrite(0x0e,(1022-chan[i].freq)&0xff); + rWrite(0x12,((1022-chan[i].freq)>>8)&0xff); + } + + if (chan[i].keyOn) { + updateCtrl=true; + } + if (chan[i].keyOff) { + updateCtrl=true; + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + resetPhase=true; + updateCtrl=true; + } + } + + if (resetPhase) { + rWrite(0x11,0x80); + } + + if (updateCtrl) { + updateCtrl=false; + rWrite(0x11,(vol&15)|((chan[0].active && chan[0].square && !isMuted[0])?0x10:0)|((chan[1].active && chan[1].square && !isMuted[1])?0x20:0)|((chan[1].active && chan[1].noise && !isMuted[1])?0x40:0)); + } +} + +int DivPlatformTED::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TED); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].insChanged=false; + if (keyPriority) { + if (chanOrder[0]==c.chan) { + chanOrder[0]=chanOrder[1]; + chanOrder[1]=c.chan; + } + } + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + } + vol=chan[c.chan].outVol; + updateCtrl=true; + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].noise=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TED)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 8; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformTED::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + updateCtrl=true; +} + +void DivPlatformTED::forceIns() { + for (int i=0; i<2; i++) { + chan[i].freqChanged=true; + } + updateCtrl=true; +} + +bool DivPlatformTED::isVolGlobal() { + return true; +} + +void* DivPlatformTED::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformTED::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformTED::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformTED::getRegisterPool() { + return regPool; +} + +int DivPlatformTED::getRegisterPoolSize() { + return 5; +} + +void DivPlatformTED::reset() { + writes.clear(); + memset(regPool,0,8); + for (int i=0; i<2; i++) { + chan[i]=DivPlatformTED::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + ted_sound_machine_init(&ted,1,8); + updateCtrl=true; + vol=15; + + chanOrder[0]=0; + chanOrder[1]=1; +} + +int DivPlatformTED::getOutputCount() { + return 1; +} + +bool DivPlatformTED::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformTED::notifyInsDeletion(void* ins) { + for (int i=0; i<2; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformTED::setFlags(const DivConfig& flags) { + if (flags.getInt("clockSel",0)) { + chipClock=COLOR_PAL*2.0/5.0; + } else { + chipClock=COLOR_NTSC/2.0; + } + CHECK_CUSTOM_CLOCK; + rate=chipClock/8; + for (int i=0; i<2; i++) { + oscBuf[i]->rate=rate; + } + keyPriority=flags.getBool("keyPriority",true); +} + +void DivPlatformTED::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformTED::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformTED::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<2; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 2; +} + +void DivPlatformTED::quit() { + for (int i=0; i<2; i++) { + delete oscBuf[i]; + } +} + +DivPlatformTED::~DivPlatformTED() { +} diff --git a/src/engine/platform/ted.h b/src/engine/platform/ted.h new file mode 100644 index 00000000..58b4d0b7 --- /dev/null +++ b/src/engine/platform/ted.h @@ -0,0 +1,79 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _TED_H +#define _TED_H + +#include "../dispatch.h" +#include "../fixedQueue.h" +#include "sound/ted-sound.h" + +class DivPlatformTED: public DivDispatch { + struct Channel: public SharedChannel { + bool noise, square; + Channel(): + SharedChannel(8), + noise(false), + square(true) {} + }; + Channel chan[2]; + DivDispatchOscBuffer* oscBuf[2]; + bool isMuted[2]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(): addr(0), val(0) {} + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + FixedQueue writes; + + struct plus4_sound_s ted; + unsigned char vol; + bool updateCtrl, keyPriority; + + unsigned char chanOrder[2]; + unsigned char regPool[8]; + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + bool isVolGlobal(); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformTED(); +}; + +#endif diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 1836bee1..86038216 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/vic20.cpp b/src/engine/platform/vic20.cpp index bd25b528..05705f7c 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -278,6 +278,10 @@ void DivPlatformVIC20::forceIns() { } } +bool DivPlatformVIC20::isVolGlobal() { + return true; +} + void* DivPlatformVIC20::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index e233d984..b1c1533a 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -46,6 +46,7 @@ class DivPlatformVIC20: public DivDispatch { public: void acquire(short** buf, size_t len); int dispatch(DivCommand c); + bool isVolGlobal(); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 68cccca4..02e92e19 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -670,6 +670,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } case DIV_CMD_FM_EXTCH: { if (extSys) { + if (extMode==(bool)c.value) break; extMode=c.value; immWrite(0x27,extMode?0x40:0); } diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 6398ba7f..31294bd4 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -138,6 +138,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { opChan[ch].insChanged=true; } opChan[ch].ins=c.value; + chan[extChanOffs].ins=opChan[ch].ins; break; case DIV_CMD_PITCH: { opChan[ch].pitch=c.value; @@ -182,8 +183,15 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { break; } case DIV_CMD_FM_EXTCH: { + if (extMode==(bool)c.value) break; extMode=c.value; immWrite(0x27,extMode?0x40:0); + if (!extMode) { + for (int i=0; i<4; i++) { + opChan[i].insChanged=true; + } + chan[extChanOffs].insChanged=true; + } break; } case DIV_CMD_FM_FB: { @@ -355,6 +363,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 +396,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 +409,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 +452,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 +532,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 +559,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 +567,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 +594,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 +562,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 +589,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 +562,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 +589,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; igetChanMacroInt(dispatchChanOfChan[note.channel]); if (macroInt!=NULL) { - if (macroInt->hasRelease) { + if (macroInt->hasRelease && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,note.channel)); } else { dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel)); @@ -1967,8 +1967,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi if (cycles<=0) { // we have to tick if (nextTick()) { - totalTicks=0; - totalSeconds=0; + /*totalTicks=0; + totalSeconds=0;*/ lastLoopPos=size-(runLeftG>>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 d88bf4bc..17cd235c 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/song.h b/src/engine/song.h index 4be25c43..7fc8dc2a 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -128,7 +128,8 @@ enum DivSystem { DIV_SYSTEM_YM2608_CSM, DIV_SYSTEM_SM8521, DIV_SYSTEM_PV1000, - DIV_SYSTEM_K053260 + DIV_SYSTEM_K053260, + DIV_SYSTEM_TED }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index a911e2df..08ed8f4f 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -451,7 +451,7 @@ void DivEngine::registerSystems() { EffectHandlerMap fmOPN2EffectHandlerMap(fmEffectHandlerMap); fmOPN2EffectHandlerMap.insert({ - {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}}, + {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}, }); @@ -681,7 +681,7 @@ void DivEngine::registerSystems() { {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Toggle noise mode"}}, {0x12, {DIV_CMD_PCE_LFO_MODE, "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)"}}, {0x13, {DIV_CMD_PCE_LFO_SPEED, "13xx: Set LFO speed"}}, - {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}} + {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}} } ); @@ -939,7 +939,7 @@ void DivEngine::registerSystems() { {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_NULL}, { {0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle (pulse: 0 to 7)"}}, - {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (pulse channel)"}}, + {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, } ); @@ -1182,7 +1182,7 @@ void DivEngine::registerSystems() { {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Setup noise mode (0: disabled; 1-8: enabled/tap)"}}, {0x12, {DIV_CMD_WS_SWEEP_TIME, "12xx: Setup sweep period (0: disabled; 1-20: enabled/period)"}}, {0x13, {DIV_CMD_WS_SWEEP_AMOUNT, "13xx: Set sweep amount"}}, - {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}}, + {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, } ); @@ -1497,7 +1497,7 @@ void DivEngine::registerSystems() { {0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}}, {0x11, {DIV_CMD_X1_010_ENVELOPE_SHAPE, "11xx: Set envelope shape"}}, {0x12, {DIV_CMD_X1_010_SAMPLE_BANK_SLOT, "12xx: Set sample bank slot (0 to 7)"}}, - {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}}, + {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, }, { {0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set PCM frequency (1 to FF)"}}, @@ -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>}}, @@ -1863,6 +1862,16 @@ void DivEngine::registerSystems() { } ); + sysDefs[DIV_SYSTEM_TED]=new DivSysDef( + "MOS Technology TED", NULL, 0xcd, 0, 2, false, true, 0, false, 0, + "two square waves (one may be turned into noise). used in the Commodore Plus/4, 16 and 116.", + {"Channel 1", "Channel 2"}, + {"CH1", "CH2"}, + {DIV_CH_PULSE, DIV_CH_PULSE}, + {DIV_INS_TED, DIV_INS_TED}, + {} + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, "this is a system designed for testing purposes.", diff --git a/src/engine/zsm.cpp b/src/engine/zsm.cpp index 04bf68b9..2776379a 100644 --- a/src/engine/zsm.cpp +++ b/src/engine/zsm.cpp @@ -66,6 +66,8 @@ void DivZSM::init(unsigned int rate) { // Channel masks ymMask=0; psgMask=0; + // Optimize writes + optimize=true; } int DivZSM::getoffset() { @@ -116,17 +118,18 @@ void DivZSM::writeYM(unsigned char a, unsigned char v) { } } +void DivZSM::writeSync(unsigned char a, unsigned char v) { + return syncCache.push_back(DivRegWrite(a,v)); +} + void DivZSM::writePSG(unsigned char a, unsigned char v) { - // TODO: suppress writes to PSG voice that is not audible (volume=0) - // ^ Let's leave these alone, ZSMKit has a feature that can benefit - // from silent channels. if (a>=69) { logD("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v); return; } else if (a==68) { // Sync event numWrites++; - return syncCache.push_back(v); + return writeSync(0x00,v); } else if (a>=64) { return writePCM(a-64,v); } @@ -209,6 +212,10 @@ void DivZSM::setLoopPoint() { } } +void DivZSM::setOptimize(bool o) { + optimize=o; +} + SafeWriter* DivZSM::finish() { tick(0); // flush any pending writes / ticks flushTicks(); // flush ticks in case there were no writes pending @@ -274,6 +281,11 @@ void DivZSM::flushWrites() { flushTicks(); // only flush ticks if there are writes pending. for (unsigned char i=0; i<64; i++) { if (psgState[psg_NEW][i]==psgState[psg_PREV][i]) continue; + // if optimize=true, suppress writes to PSG voices that are not audible (volume=0 or R+L=0) + // ZSMKit has a feature that can benefit from having silent channels + // updated, so this is something that can be toggled off or on for export + if (optimize && (i&3)!=2 && (psgState[psg_NEW][(i&0x3c)+2]&0x3f)==0) continue; // vol + if (optimize && (i&3)!=2 && (psgState[psg_NEW][(i&0x3c)+2]&0xc0)==0) continue; // R+L psgState[psg_PREV][i]=psgState[psg_NEW][i]; w->writeC(i); w->writeC(psgState[psg_NEW][i]); @@ -390,15 +402,18 @@ void DivZSM::flushWrites() { } } n=0; - while (n<(long)syncCache.size()) { // we have one or more sync events to write - int writes=syncCache.size()-n; - w->writeC(ZSM_EXT); - if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES; - w->writeC(ZSM_EXT_SYNC|(writes<<1)); - for (; writes>0; writes--) { - w->writeC(0x00); // 0x00 = Arbitrary sync message - w->writeC(syncCache[n++]); + for (DivRegWrite& write: syncCache) { + if (n%ZSM_SYNC_MAX_WRITES==0) { + w->writeC(ZSM_EXT); + if (syncCache.size()-n>ZSM_SYNC_MAX_WRITES) { + w->writeC((unsigned char)(ZSM_EXT_SYNC|(ZSM_SYNC_MAX_WRITES<<1))); + } else { + w->writeC((unsigned char)(ZSM_EXT_SYNC|((syncCache.size()-n)<<1))); + } } + n++; + w->writeC(write.addr); + w->writeC(write.val); } syncCache.clear(); numWrites=0; diff --git a/src/engine/zsm.h b/src/engine/zsm.h index ff497693..79b8b116 100644 --- a/src/engine/zsm.h +++ b/src/engine/zsm.h @@ -63,13 +63,14 @@ class DivZSM { std::vector pcmData; std::vector pcmCache; std::vector pcmInsts; - std::vector syncCache; + std::vector syncCache; int loopOffset; int numWrites; int ticks; int tickRate; int ymMask; int psgMask; + bool optimize; public: DivZSM(); ~DivZSM(); @@ -78,6 +79,8 @@ class DivZSM { void writeYM(unsigned char a, unsigned char v); void writePSG(unsigned char a, unsigned char v); void writePCM(unsigned char a, unsigned char v); + void writeSync(unsigned char a, unsigned char v); + void setOptimize(bool o); void tick(int numticks = 1); void setLoopPoint(); SafeWriter* finish(); diff --git a/src/engine/zsmOps.cpp b/src/engine/zsmOps.cpp index 85ddb5d4..3c72da66 100644 --- a/src/engine/zsmOps.cpp +++ b/src/engine/zsmOps.cpp @@ -26,7 +26,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0; -SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { +SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop, bool optimize) { int VERA=-1; int YM=-1; int IGNORED=0; @@ -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); @@ -106,12 +107,34 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { // TODO: incorporate the Furnace meta-command for init data and filter // out writes to otherwise-unused channels. } + // Indicate the song's tuning as a sync meta-event + // specified in terms of how many 1/256th semitones + // the song is offset from standard A-440 tuning. + // This is mainly to benefit visualizations in players + // for non-standard tunings so that they can avoid + // displaying the entire song held in pitch bend. + // Tunings offsets that exceed a half semitone + // will simply be represented in a different key + // by nature of overflowing the signed char value + signed char tuningoffset=(signed char)(round(3072*(log(song.tuning/440.0)/log(2))))&0xff; + zsm.writeSync(0x01,tuningoffset); + // Set optimize flag, which mainly buffers PSG writes + // whenever the channel is silent + zsm.setOptimize(optimize); 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 5dadfcf6..b849c6bb 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -38,6 +38,7 @@ const char* aboutLine[]={ "akumanatt", "cam900", "djtuBIG-MaliceX", + "Eknous-P", "laoo", "MooingLemur", "OPNA2608", @@ -83,7 +84,7 @@ const char* aboutLine[]={ "djtuBIG-MaliceX", "dumbut", "Eknous-P", - "ElectricKeet", + "Electric Keet", "EpicTyphlosion", "FΛDE", "Forte", @@ -114,11 +115,13 @@ const char* aboutLine[]={ "potatoTeto", "psxdominator", "Raijin", + "railzen7", "SnugglyBun", "SuperJet Spade", "SwapXFO", "TakuikaNinja", "TCORPStudios", + "Teuthida", "The Blender Fiddler", "TheDuccinator", "theloredev", @@ -135,6 +138,7 @@ const char* aboutLine[]={ "ZoomTen (Zumi)", "", "-- additional feedback/fixes --", + "Electric Keet", "fd", "GENATARi", "host12prog", @@ -182,6 +186,8 @@ const char* aboutLine[]={ "Stella by Stella Team", "QSound emulator by superctr and Valley Bell", "VICE VIC-20 sound core by Rami Rasanen and viznut", + "VICE TED sound core by Andreas Boose, Tibor Biczo", + "and Marco van den Heuvel", "VERA sound core by Frank van den Hoef", "mzpokeysnd POKEY emulator by Michael Borisov", "ASAP POKEY emulator by Piotr Fusik", diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 803a5665..aa7c91b5 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: @@ -124,6 +131,7 @@ void FurnaceGUI::drawChanOsc() { if (ImGui::BeginTable("ChanOscSettings",3)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Columns"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -146,9 +154,24 @@ void FurnaceGUI::drawChanOsc() { centerSettingReset=true; } + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + 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(); } + ImGui::AlignTextToFramePadding(); ImGui::Text("Amplitude"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -241,6 +264,7 @@ void FurnaceGUI::drawChanOsc() { if (ImGui::ColorPicker4("Color",(float*)&i.color)) { updateChanOscGradTex=true; } + ImGui::AlignTextToFramePadding(); ImGui::Text("Distance"); ImGui::SameLine(); float pDist=i.distance*100.0f; @@ -249,6 +273,7 @@ void FurnaceGUI::drawChanOsc() { updateChanOscGradTex=true; } + ImGui::AlignTextToFramePadding(); ImGui::Text("Spread"); ImGui::SameLine(); float pSpread=i.spread*100.0f; @@ -293,6 +318,7 @@ void FurnaceGUI::drawChanOsc() { ImGui::ColorPicker4("Color",(float*)&chanOscColor); } + ImGui::AlignTextToFramePadding(); ImGui::Text("Text format:"); ImGui::SameLine(); ImGui::InputText("##TextFormat",&chanOscTextFormat); @@ -304,6 +330,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 +372,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 48d5ed8c..dd19d652 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/compatFlags.cpp b/src/gui/compatFlags.cpp index f1b16c63..55f3cb58 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -199,6 +199,7 @@ void FurnaceGUI::drawCompatFlags() { } if (ImGui::BeginTabItem("Pitch/Playback")) { ImGui::Text("Pitch linearity:"); + ImGui::Indent(); if (ImGui::RadioButton("None",e->song.linearPitch==0)) { e->song.linearPitch=0; } @@ -217,6 +218,7 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("like Impulse Tracker"); } + ImGui::Unindent(); if (e->song.linearPitch==2) { ImGui::SameLine(); @@ -228,6 +230,7 @@ void FurnaceGUI::drawCompatFlags() { } ImGui::Text("Loop modality:"); + ImGui::Indent(); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { e->song.loopModality=0; } @@ -246,8 +249,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("select to not reset channels on loop."); } + ImGui::Unindent(); ImGui::Text("Cut/delay effect policy:"); + ImGui::Indent(); if (ImGui::RadioButton("Strict",e->song.delayBehavior==0)) { e->song.delayBehavior=0; } @@ -266,8 +271,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("no checks"); } + ImGui::Unindent(); ImGui::Text("Simultaneous jump (0B+0D) treatment:"); + ImGui::Indent(); if (ImGui::RadioButton("Normal",e->song.jumpTreatment==0)) { e->song.jumpTreatment=0; } @@ -286,6 +293,7 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("only accept 0Dxx"); } + ImGui::Unindent(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Other")) { diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 971030ee..8e9bc33e 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -282,6 +282,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]); name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i); break; + case DIV_INS_TED: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TED]); + name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i); @@ -326,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); } @@ -406,13 +407,10 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { lastAssetType=2; } if (ImGui::IsItemHovered() && !mobileUI) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); - ImGui::SetTooltip("(legacy bank %d: %s)",i/12,sampleNote[i%12]); if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { sampleEditOpen=true; nextWindow=GUI_WINDOW_SAMPLE_EDIT; } - ImGui::PopStyleColor(); } if (sampleListDir || (settings.unifiedDataView && insListDir)) { DRAG_SOURCE(dir,asset,"FUR_SDIR"); diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index bd6bdb81..d2b2f44a 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/debugWindow.cpp b/src/gui/debugWindow.cpp index bfeb9067..9d59e44d 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -486,6 +486,7 @@ void FurnaceGUI::drawDebug() { pgProgram.clear(); } + ImGui::AlignTextToFramePadding(); ImGui::Text("Address"); ImGui::SameLine(); ImGui::SetNextItemWidth(100.0f*dpiScale); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 60d713d3..d26a1b31 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -589,14 +589,34 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_PAT_EXPAND_SONG: doExpandSong(collapseAmount); break; - case GUI_ACTION_PAT_LATCH: // TODO + case GUI_ACTION_PAT_LATCH: { + DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true); + latchIns=pat->data[cursor.y][2]; + latchVol=pat->data[cursor.y][3]; + latchEffect=pat->data[cursor.y][4]; + latchEffectVal=pat->data[cursor.y][5]; + latchTarget=0; + latchNibble=false; break; - case GUI_ACTION_PAT_SCROLL_MODE: // TODO - break; - case GUI_ACTION_PAT_CLEAR_LATCH: // TODO + } + case GUI_ACTION_PAT_CLEAR_LATCH: + latchIns=-2; + latchVol=-1; + latchEffect=-1; + latchEffectVal=-1; + latchTarget=0; + latchNibble=false; break; case GUI_ACTION_INS_LIST_ADD: + if (settings.insTypeMenu) { + makeInsTypeList=e->getPossibleInsTypes(); + if (makeInsTypeList.size()>1) { + displayInsTypeList=true; + displayInsTypeListMakeInsSample=-1; + break; + } + } curIns=e->addInstrument(cursor.xCoarse); if (curIns==-1) { showError("too many instruments!"); @@ -642,9 +662,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; @@ -1268,7 +1285,7 @@ void FurnaceGUI::doAction(int what) { } else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) { for (unsigned int i=start; idata8[i]=-sample->data8[i]; - if (sample->data16[i]==-128) sample->data16[i]=127; + if (sample->data8[i]==-128) sample->data8[i]=127; } } diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 099ff7e2..24aeb87c 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -619,6 +619,7 @@ void FurnaceGUI::drawMobileControls() { "Furnace Amiga emulator is working properly by\n" "comparing it with real Amiga output." ); + ImGui::AlignTextToFramePadding(); ImGui::Text("Directory"); ImGui::SameLine(); ImGui::InputText("##AVDPath",&workingDirROMExport); @@ -717,6 +718,7 @@ void FurnaceGUI::drawEditControls() { e->setMetronome(metro); } + ImGui::AlignTextToFramePadding(); ImGui::Text("Follow"); ImGui::SameLine(); unimportant(ImGui::Checkbox("Orders",&followOrders)); @@ -1063,6 +1065,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::Begin("Edit Controls",&editControlsOpen,globalWinFlags)) { ImGui::Columns(2); + ImGui::AlignTextToFramePadding(); ImGui::Text("Octave"); ImGui::SameLine(); float cursor=ImGui::GetCursorPosX(); @@ -1078,6 +1081,7 @@ void FurnaceGUI::drawEditControls() { } } + ImGui::AlignTextToFramePadding(); ImGui::Text("Step"); ImGui::SameLine(); ImGui::SetCursorPosX(cursor); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index b9e03511..1e55d86e 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -68,7 +68,7 @@ void FurnaceGUI::prepareUndo(ActionType action) { } break; case GUI_UNDO_PATTERN_COLLAPSE_SONG: - case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO + case GUI_UNDO_PATTERN_EXPAND_SONG: // this is handled by doCollapseSong/doExpandSong break; case GUI_UNDO_REPLACE: // this is handled by doReplace() break; @@ -134,7 +134,7 @@ void FurnaceGUI::makeUndo(ActionType action) { } break; case GUI_UNDO_PATTERN_COLLAPSE_SONG: - case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO + case GUI_UNDO_PATTERN_EXPAND_SONG: // this is handled by doCollapseSong/doExpandSong break; case GUI_UNDO_REPLACE: // this is handled by doReplace() break; diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp index 604204b8..0be9b159 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,58 @@ 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 (fxColors[i]==GUI_COLOR_PATTERN_EFFECT_PANNING) { + DivDispatch* dispatch=e->getDispatch(e->dispatchOfChan[cursor.xCoarse]); + if (dispatch!=NULL) { + int outputs=dispatch->getOutputCount(); + if (outputs<2) { + effectShow=false; + } + if (outputs<3) { + if (i>=0x88 && i<=0x8f) { + effectShow=false; + } + } + } + } + if (name!=NULL && effectShow) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushFont(patFont); diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index a0a45e1e..b9ec5f69 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -28,7 +28,6 @@ struct NFDState { } }; -// TODO: filter void _nfdThread(const NFDState state, std::atomic* ok, std::vector* result, bool* errorOutput) { nfdchar_t* out=NULL; nfdresult_t ret=NFD_CANCEL; diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index b6fcd14c..60976d31 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -595,6 +595,7 @@ void FurnaceGUI::drawFindReplace() { ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.25); ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Note"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -662,6 +663,7 @@ void FurnaceGUI::drawFindReplace() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Ins"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -697,6 +699,7 @@ void FurnaceGUI::drawFindReplace() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Volume"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -734,6 +737,7 @@ void FurnaceGUI::drawFindReplace() { ImGui::PushID(0x1000+j); ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Effect"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -769,6 +773,7 @@ void FurnaceGUI::drawFindReplace() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Value"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4e24219e..7cdba4c6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -77,6 +77,14 @@ bool Particle::update(float frameTime) { return (life>0); } +void FurnaceGUI::centerNextWindow(const char* name, float w, float h) { + if (ImGui::IsPopupOpen(name)) { + if (settings.centerPopup) { + ImGui::SetNextWindowPos(ImVec2(w*0.5,h*0.5),ImGuiCond_Always,ImVec2(0.5,0.5)); + } + } +} + void FurnaceGUI::bindEngine(DivEngine* eng) { e=eng; wavePreview.setEngine(e); @@ -98,6 +106,10 @@ const char* FurnaceGUI::noteName(short note, short octave) { if (seek<0 || seek>=180) { return "???"; } + if (settings.flatNotes) { + if (settings.germanNotation) return noteNamesGF[seek]; + return noteNamesF[seek]; + } if (settings.germanNotation) return noteNamesG[seek]; return noteNames[seek]; } @@ -606,10 +618,11 @@ void FurnaceGUI::autoDetectSystem() { std::map sysCountMap; std::map sysConfMap; for (int i=0; isong.systemLen; i++) { - try { - sysCountMap.at(e->song.system[i])++; - } catch (std::exception& ex) { + auto it=sysCountMap.find(e->song.system[i]); + if (it==sysCountMap.cend()) { sysCountMap[e->song.system[i]]=1; + } else { + it->second++; } sysConfMap[e->song.system[i]]=e->song.systemFlags[i]; } @@ -627,10 +640,11 @@ void FurnaceGUI::autoDetectSystem() { defCountMap.clear(); defConfMap.clear(); for (FurnaceGUISysDefChip& k: j.orig) { - try { - defCountMap.at(k.sys)++; - } catch (std::exception& ex) { + auto it=defCountMap.find(k.sys); + if (it==defCountMap.cend()) { defCountMap[k.sys]=1; + } else { + it->second++; } DivConfig dc; dc.loadFromMemory(k.flags); @@ -643,27 +657,37 @@ void FurnaceGUI::autoDetectSystem() { logV("- %s: %d",e->getSystemName(k.first),k.second); }*/ for (std::pair k: defCountMap) { - try { - if (sysCountMap.at(k.first)!=k.second) { - isMatch=false; - break; - } - DivConfig& sysDC=sysConfMap.at(k.first); - for (std::pair l: defConfMap.at(k.first).configMap()) { - if (!sysDC.has(l.first)) { - isMatch=false; - break; - } - if (sysDC.getString(l.first,"")!=l.second) { - isMatch=false; - break; - } - } - if (!isMatch) break; - } catch (std::exception& ex) { + auto countI=sysCountMap.find(k.first); + if (countI==sysCountMap.cend()) { + isMatch=false; + break; + } else if (countI->second!=k.second) { isMatch=false; break; } + + auto confI=sysConfMap.find(k.first); + if (confI==sysConfMap.cend()) { + isMatch=false; + break; + } + DivConfig& sysDC=confI->second; + auto defConfI=defConfMap.find(k.first); + if (defConfI==defConfMap.cend()) { + isMatch=false; + break; + } + for (std::pair l: defConfI->second.configMap()) { + if (!sysDC.has(l.first)) { + isMatch=false; + break; + } + if (sysDC.getString(l.first,"")!=l.second) { + isMatch=false; + break; + } + } + if (!isMatch) break; } if (isMatch) { logV("match found!"); @@ -1093,8 +1117,9 @@ void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { } void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { - try { - int key=noteKeys.at(scancode); + auto it=noteKeys.find(scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (num<-60) num=-60; // C-(-5) if (num>119) num=119; // B-9 @@ -1106,24 +1131,27 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { e->synchronized([this,num]() { e->autoNoteOff(-1,num); }); - } catch (std::out_of_range& e) { } } void FurnaceGUI::noteInput(int num, int key, int vol) { DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true); + bool removeIns=false; prepareUndo(GUI_UNDO_PATTERN_EDIT); if (key==GUI_NOTE_OFF) { // note off pat->data[cursor.y][0]=100; pat->data[cursor.y][1]=0; + removeIns=true; } else if (key==GUI_NOTE_OFF_RELEASE) { // note off + env release pat->data[cursor.y][0]=101; pat->data[cursor.y][1]=0; + removeIns=true; } else if (key==GUI_NOTE_RELEASE) { // env release only pat->data[cursor.y][0]=102; pat->data[cursor.y][1]=0; + removeIns=true; } else { pat->data[cursor.y][0]=num%12; pat->data[cursor.y][1]=num/12; @@ -1149,6 +1177,14 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect; if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal; } + if (removeIns) { + if (settings.removeInsOff) { + pat->data[cursor.y][2]=-1; + } + if (settings.removeVolOff) { + pat->data[cursor.y][3]=-1; + } + } makeUndo(GUI_UNDO_PATTERN_EDIT); editAdvance(); curNibble=false; @@ -1306,8 +1342,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { break; } } else { - try { - int num=valueKeys.at(ev.key.keysym.sym); + auto it=valueKeys.find(ev.key.keysym.sym); + if (it!=valueKeys.cend()) { + int num=it->second; switch (latchTarget) { case 1: // instrument changeLatch(latchIns); @@ -1322,7 +1359,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { changeLatch(latchEffectVal); break; } - } catch (std::out_of_range& e) { } } return; @@ -1335,8 +1371,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { alterSampleMap(true,-1); return; } - try { - int key=noteKeys.at(ev.key.keysym.scancode); + auto it=noteKeys.find(ev.key.keysym.scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (num<-60) num=-60; // C-(-5) @@ -1344,7 +1381,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { alterSampleMap(true,num); return; - } catch (std::out_of_range& e) { } } else { // TODO: map? @@ -1352,34 +1388,35 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { alterSampleMap(false,-1); return; } - try { - int num=valueKeys.at(ev.key.keysym.sym); + auto it=valueKeys.find(ev.key.keysym.sym); + if (it!=valueKeys.cend()) { + int num=it->second; if (num<10) { alterSampleMap(false,num); return; } - } catch (std::out_of_range& e) { } } } // PER-WINDOW KEYS switch (curWindow) { - case GUI_WINDOW_PATTERN: - try { - int action=actionMapPat.at(mapped); + case GUI_WINDOW_PATTERN: { + auto actionI=actionMapPat.find(mapped); + if (actionI!=actionMapPat.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } // pattern input otherwise if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break; if (!ev.key.repeat) { if (cursor.xFine==0) { // note - try { - int key=noteKeys.at(ev.key.keysym.scancode); + auto it=noteKeys.find(ev.key.keysym.scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (num<-60) num=-60; // C-(-5) @@ -1388,39 +1425,41 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { if (edit) { noteInput(num,key); } - } catch (std::out_of_range& e) { } } else if (edit) { // value - try { - int num=valueKeys.at(ev.key.keysym.sym); + auto it=valueKeys.find(ev.key.keysym.sym); + if (it!=valueKeys.cend()) { + int num=it->second; valueInput(num); - } catch (std::out_of_range& e) { } } } break; - case GUI_WINDOW_ORDERS: - try { - int action=actionMapOrders.at(mapped); + } + case GUI_WINDOW_ORDERS: { + auto actionI=actionMapOrders.find(mapped); + if (actionI!=actionMapOrders.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } // order input otherwise if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break; if (orderEditMode!=0) { - try { - int num=valueKeys.at(ev.key.keysym.sym); + auto it=valueKeys.find(ev.key.keysym.sym); + if (it!=valueKeys.cend()) { + int num=it->second; if (orderCursor>=0 && orderCursorgetTotalChannelCount()) { prepareUndo(GUI_UNDO_CHANGE_ORDER); e->lockSave([this,num]() { + if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0; e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num); }); MARK_MODIFIED; + curNibble=!curNibble; if (orderEditMode==2 || orderEditMode==3) { - curNibble=!curNibble; if (!curNibble) { if (orderEditMode==2) { orderCursor++; @@ -1435,62 +1474,66 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { e->walkSong(loopOrder,loopRow,loopEnd); makeUndo(GUI_UNDO_CHANGE_ORDER); } - } catch (std::out_of_range& e) { } } break; - case GUI_WINDOW_SAMPLE_EDIT: - try { - int action=actionMapSample.at(mapped); + } + case GUI_WINDOW_SAMPLE_EDIT: { + auto actionI=actionMapSample.find(mapped); + if (actionI!=actionMapSample.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } break; - case GUI_WINDOW_INS_LIST: - try { - int action=actionMapInsList.at(mapped); + } + case GUI_WINDOW_INS_LIST: { + auto actionI=actionMapInsList.find(mapped); + if (actionI!=actionMapInsList.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } break; - case GUI_WINDOW_WAVE_LIST: - try { - int action=actionMapWaveList.at(mapped); + } + case GUI_WINDOW_WAVE_LIST: { + auto actionI=actionMapWaveList.find(mapped); + if (actionI!=actionMapWaveList.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } break; - case GUI_WINDOW_SAMPLE_LIST: - try { - int action=actionMapSampleList.at(mapped); + } + case GUI_WINDOW_SAMPLE_LIST: { + auto actionI=actionMapSampleList.find(mapped); + if (actionI!=actionMapSampleList.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } break; + } default: break; } // GLOBAL KEYS - try { - int action=actionMapGlobal.at(mapped); + auto actionI=actionMapGlobal.find(mapped); + if (actionI!=actionMapGlobal.cend()) { + int action=actionI->second; if (action>0) { doAction(action); return; } - } catch (std::out_of_range& e) { } } @@ -1630,16 +1673,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( @@ -1822,6 +1855,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( @@ -2077,6 +2120,7 @@ int FurnaceGUI::save(String path, int dmfVersion) { } int FurnaceGUI::load(String path) { + bool wasPlaying=e->isPlaying(); if (!path.empty()) { logI("loading module..."); FILE* f=ps_fopen(path.c_str(),"rb"); @@ -2153,6 +2197,12 @@ int FurnaceGUI::load(String path) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } pushRecentFile(path); + // do not auto-play a backup + if (path.find(backupPath)!=0) { + if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) { + play(); + } + } return 0; } @@ -2741,6 +2791,7 @@ void FurnaceGUI::editOptions(bool topMenu) { if (ImGui::MenuItem("values up (+16)",BIND_FOR(GUI_ACTION_PAT_VALUE_UP_COARSE))) doTranspose(16,opMaskTransposeValue); if (ImGui::MenuItem("values down (-16)",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN_COARSE))) doTranspose(-16,opMaskTransposeValue); ImGui::Separator(); + ImGui::AlignTextToFramePadding(); ImGui::Text("transpose"); ImGui::SameLine(); ImGui::SetNextItemWidth(120.0f*dpiScale); @@ -2976,9 +3027,10 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { if (settings.notePreviewBehavior==0) return 1; switch (curWindow) { case GUI_WINDOW_SAMPLE_EDIT: - case GUI_WINDOW_SAMPLE_LIST: - try { - int key=noteKeys.at(ev->key.keysym.scancode); + case GUI_WINDOW_SAMPLE_LIST: { + auto it=noteKeys.find(ev->key.keysym.scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (key!=100 && key!=101 && key!=102) { int pStart=-1; @@ -2999,13 +3051,14 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { samplePreviewKey=ev->key.keysym.scancode; samplePreviewNote=num; } - } catch (std::out_of_range& e) { } break; + } case GUI_WINDOW_WAVE_LIST: - case GUI_WINDOW_WAVE_EDIT: - try { - int key=noteKeys.at(ev->key.keysym.scancode); + case GUI_WINDOW_WAVE_EDIT: { + auto it=noteKeys.find(ev->key.keysym.scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (key!=100 && key!=101 && key!=102) { e->previewWave(curWave,num); @@ -3013,9 +3066,9 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { wavePreviewKey=ev->key.keysym.scancode; wavePreviewNote=num; } - } catch (std::out_of_range& e) { } break; + } case GUI_WINDOW_ORDERS: // ignore here break; case GUI_WINDOW_PATTERN: @@ -3025,9 +3078,10 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { if (edit && cursor.xFine!=0) break; } // fall-through - default: - try { - int key=noteKeys.at(ev->key.keysym.scancode); + default: { + auto it=noteKeys.find(ev->key.keysym.scancode); + if (it!=noteKeys.cend()) { + int key=it->second; int num=12*curOctave+key; if (num<-60) num=-60; // C-(-5) @@ -3036,9 +3090,9 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { if (key!=100 && key!=101 && key!=102) { previewNote(cursor.xCoarse,num); } - } catch (std::out_of_range& e) { } break; + } } } } else if (ev->type==SDL_KEYUP) { @@ -3843,6 +3897,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!"); @@ -3872,7 +3927,7 @@ bool FurnaceGUI::loop() { if (!mobileUI) { ImGui::BeginMainMenuBar(); - if (ImGui::BeginMenu("file")) { + if (ImGui::BeginMenu(settings.capitalMenuBar?"File":"file")) { if (ImGui::MenuItem("new...",BIND_FOR(GUI_ACTION_NEW))) { if (modified) { showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW); @@ -3967,6 +4022,7 @@ bool FurnaceGUI::loop() { ImGui::Checkbox("loop",&vgmExportLoop); if (vgmExportLoop && e->song.loopModality==2) { ImGui::Text("loop trail:"); + ImGui::Indent(); if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) { vgmExportTrailingTicks=-1; } @@ -3982,6 +4038,7 @@ bool FurnaceGUI::loop() { if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0; } } + ImGui::Unindent(); } ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints); if (ImGui::IsItemHovered()) { @@ -4051,6 +4108,8 @@ bool FurnaceGUI::loop() { } ImGui::Checkbox("loop",&zsmExportLoop); ImGui::SameLine(); + ImGui::Checkbox("optimize size",&zsmExportOptimize); + ImGui::SameLine(); if (ImGui::Button("Begin Export")) { openFileDialog(GUI_FILE_EXPORT_ZSM); ImGui::CloseCurrentPopup(); @@ -4070,6 +4129,7 @@ bool FurnaceGUI::loop() { "Furnace Amiga emulator is working properly by\n" "comparing it with real Amiga output." ); + ImGui::AlignTextToFramePadding(); ImGui::Text("Directory"); ImGui::SameLine(); ImGui::InputText("##AVDPath",&workingDirROMExport); @@ -4133,7 +4193,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(); } } @@ -4195,7 +4255,7 @@ bool FurnaceGUI::loop() { } else { exitDisabledTimer=0; } - if (ImGui::BeginMenu("edit")) { + if (ImGui::BeginMenu(settings.capitalMenuBar?"Edit":"edit")) { ImGui::Text("..."); ImGui::Separator(); if (ImGui::MenuItem("undo",BIND_FOR(GUI_ACTION_UNDO))) doUndo(); @@ -4208,7 +4268,7 @@ bool FurnaceGUI::loop() { } ImGui::EndMenu(); } - if (ImGui::BeginMenu("settings")) { + if (ImGui::BeginMenu(settings.capitalMenuBar?"Settings":"settings")) { #ifndef IS_MOBILE if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) { doAction(GUI_ACTION_FULLSCREEN); @@ -4244,7 +4304,7 @@ bool FurnaceGUI::loop() { } ImGui::EndMenu(); } - if (ImGui::BeginMenu("window")) { + if (ImGui::BeginMenu(settings.capitalMenuBar?"Window":"window")) { if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen; if (ImGui::MenuItem("speed",BIND_FOR(GUI_ACTION_WINDOW_SPEED),speedOpen)) speedOpen=!speedOpen; @@ -4286,7 +4346,7 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } - if (ImGui::BeginMenu("help")) { + if (ImGui::BeginMenu(settings.capitalMenuBar?"Help":"help")) { if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen; if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen; if (ImGui::MenuItem("inspector",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) inspectorOpen=!inspectorOpen; @@ -4382,6 +4442,10 @@ bool FurnaceGUI::loop() { MEASURE(calcChanOsc,calcChanOsc()); + if (followPattern) { + curOrder=e->getOrder(); + } + if (mobileUI) { globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus; //globalWinFlags=ImGuiWindowFlags_NoTitleBar; @@ -4460,17 +4524,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()); @@ -4505,6 +4558,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); @@ -4566,7 +4624,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; @@ -4602,6 +4659,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; @@ -4669,9 +4727,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"); } @@ -4765,11 +4820,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())) { @@ -5023,7 +5073,7 @@ bool FurnaceGUI::loop() { break; } case GUI_FILE_EXPORT_ZSM: { - SafeWriter* w=e->saveZSM(zsmExportTickRate,zsmExportLoop); + SafeWriter* w=e->saveZSM(zsmExportTickRate,zsmExportLoop,zsmExportOptimize); if (w!=NULL) { FILE* f=ps_fopen(copyOfName.c_str(),"wb"); if (f!=NULL) { @@ -5071,6 +5121,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; @@ -5198,6 +5251,7 @@ bool FurnaceGUI::loop() { MEASURE_BEGIN(popup); + centerNextWindow("Rendering...",canvasW,canvasH); if (ImGui::BeginPopupModal("Rendering...",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Please wait..."); if (ImGui::Button("Abort")) { @@ -5225,6 +5279,7 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + centerNextWindow("Error",canvasW,canvasH); if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s",errorString.c_str()); if (ImGui::Button("OK")) { @@ -5233,6 +5288,7 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + centerNextWindow("Warning",canvasW,canvasH); if (ImGui::BeginPopupModal("Warning",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s",warnString.c_str()); switch (warnAction) { @@ -5591,6 +5647,19 @@ bool FurnaceGUI::loop() { wavePreviewInit=true; updateFMPreview=true; } + + if (settings.blankIns) { + e->song.ins[curIns]->fm.fb=0; + for (int i=0; i<4; i++) { + e->song.ins[curIns]->fm.op[i]=DivInstrumentFM::Operator(); + e->song.ins[curIns]->fm.op[i].ar=31; + e->song.ins[curIns]->fm.op[i].dr=31; + e->song.ins[curIns]->fm.op[i].rr=15; + e->song.ins[curIns]->fm.op[i].tl=127; + e->song.ins[curIns]->fm.op[i].dt=3; + } + } + MARK_MODIFIED; } } @@ -5601,11 +5670,13 @@ bool FurnaceGUI::loop() { // TODO: // - multiple selection // - replace instrument + centerNextWindow("Select Instrument",canvasW,canvasH); if (ImGui::BeginPopupModal("Select Instrument",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { bool quitPlease=false; if (pendingInsSingle) { ImGui::Text("this is an instrument bank! select which one to use:"); } else { + ImGui::AlignTextToFramePadding(); ImGui::Text("this is an instrument bank! select which ones to load:"); ImGui::SameLine(); if (ImGui::Button("All")) { @@ -5678,6 +5749,7 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + centerNextWindow("Import Raw Sample",canvasW,canvasH); if (ImGui::BeginPopupModal("Import Raw Sample",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Data type:"); 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!"); @@ -6067,7 +6141,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); @@ -6126,6 +6199,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); @@ -6454,6 +6528,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!"); @@ -6585,7 +6660,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); @@ -6644,6 +6718,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); @@ -6688,6 +6763,13 @@ bool FurnaceGUI::finish() { SDL_HapticClose(vibrator); } + for (int i=0; i availRenderDrivers; std::vector availAudioDrivers; - bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; + bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, zsmExportOptimize, vgmExportPatternHints; bool vgmExportDirectStream, displayInsTypeList; bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; @@ -1369,6 +1386,7 @@ class FurnaceGUI { ImFont* iconFont; ImFont* patFont; ImFont* bigFont; + ImFont* headFont; ImWchar* fontRange; ImVec4 uiColors[GUI_COLOR_MAX]; ImVec4 volColors[128]; @@ -1387,7 +1405,7 @@ class FurnaceGUI { char emptyLabel2[32]; struct Settings { - int mainFontSize, patFontSize, iconSize; + int mainFontSize, patFontSize, headFontSize, iconSize; int audioEngine; int audioQuality; int audioChans; @@ -1404,6 +1422,7 @@ class FurnaceGUI { String tg100Path; String mu5Path; int mainFont; + int headFont; int patFont; int audioRate; int audioBufSize; @@ -1419,6 +1438,7 @@ class FurnaceGUI { int chipNames; int overflowHighlight; int partyTime; + int flatNotes; int germanNotation; int stepOnDelete; int scrollStep; @@ -1457,6 +1477,8 @@ class FurnaceGUI { int oscTakesEntireWindow; int oscBorder; int oscEscapesBoundary; + int oscMono; + int oscAntiAlias; int separateFMColors; int insEditColorize; int metroVol; @@ -1522,8 +1544,16 @@ class FurnaceGUI { int newSongBehavior; int memUsageUnit; int cursorFollowsWheel; + int noDMFCompat; + int removeInsOff; + int removeVolOff; + int playOnLoad; + int insTypeMenu; + int capitalMenuBar; + int centerPopup; unsigned int maxUndoSteps; String mainFontPath; + String headFontPath; String patFontPath; String audioDevice; String midiInDevice; @@ -1542,6 +1572,7 @@ class FurnaceGUI { Settings(): mainFontSize(18), patFontSize(18), + headFontSize(27), iconSize(16), audioEngine(DIV_AUDIO_SDL), audioQuality(0), @@ -1612,6 +1643,8 @@ class FurnaceGUI { oscTakesEntireWindow(0), oscBorder(1), oscEscapesBoundary(0), + oscMono(1), + oscAntiAlias(1), separateFMColors(0), insEditColorize(0), metroVol(100), @@ -1676,8 +1709,16 @@ class FurnaceGUI { newSongBehavior(0), memUsageUnit(1), cursorFollowsWheel(0), + noDMFCompat(0), + removeInsOff(0), + removeVolOff(0), + playOnLoad(0), + insTypeMenu(1), + capitalMenuBar(0), + centerPopup(1), maxUndoSteps(100), mainFontPath(""), + headFontPath(""), patFontPath(""), audioDevice(""), midiInDevice(""), @@ -1724,7 +1765,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; @@ -1960,14 +2001,15 @@ class FurnaceGUI { ImVec2 subPortPos; // oscilloscope - int oscTotal; - float oscValues[512]; + int oscTotal, oscWidth; + float* oscValues[DIV_MAX_OUTPUTS]; float oscZoom; float oscWindowSize; + float oscInput, oscInput1; 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; @@ -2028,6 +2070,9 @@ class FurnaceGUI { bool pianoReadonly; int pianoOffset, pianoOffsetEdit; int pianoView, pianoInputPadMode; + + //effect sorting + bool effectsShow[10]; // TX81Z bool hasACED; @@ -2071,7 +2116,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); @@ -2096,6 +2141,8 @@ class FurnaceGUI { ImVec4 channelColor(int ch); ImVec4 channelTextColor(int ch); + void centerNextWindow(const char* name, float w, float h); + void readOsc(); void calcChanOsc(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index f7dcd40b..a4c5c956 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -62,6 +62,42 @@ const char* noteNamesG[180]={ "C-9", "C#9", "D-9", "D#9", "E-9", "F-9", "F#9", "G-9", "G#9", "A-9", "A#9", "H-9" }; +const char* noteNamesF[180]={ + "c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "bd5", "b_5", + "c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "bd4", "b_4", + "c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "bd3", "b_3", + "c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "bd2", "b_2", + "c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "bd1", "b_1", + "C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "Bb0", "B-0", + "C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "Bb1", "B-1", + "C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "Bb2", "B-2", + "C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "Bb3", "B-3", + "C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "Bb4", "B-4", + "C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "Bb5", "B-5", + "C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "Bb6", "B-6", + "C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "Bb7", "B-7", + "C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "Bb8", "B-8", + "C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "Bb9", "B-9" +}; + +const char* noteNamesGF[180]={ + "c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "b_5", "h_5", + "c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "b_4", "h_4", + "c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "b_3", "h_3", + "c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "b_2", "h_2", + "c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "b_1", "h_1", + "C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "B-0", "H-0", + "C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "B-1", "H-1", + "C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "B-2", "H-2", + "C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "B-3", "H-3", + "C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "B-4", "H-4", + "C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "B-5", "H-5", + "C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "B-6", "H-6", + "C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "B-7", "H-7", + "C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "B-8", "H-8", + "C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "B-9", "H-9" +}; + const char* pitchLabel[11]={ "1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x" }; @@ -132,6 +168,8 @@ const char* insTypes[DIV_INS_MAX+1]={ "SM8521", "PV-1000", "K053260", + "SCSP", + "TED", NULL }; @@ -170,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 @@ -603,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), @@ -765,6 +828,22 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_OSC_BORDER,"",ImVec4(0.4f,0.6f,0.95f,1.0f)), D(GUI_COLOR_OSC_WAVE,"",ImVec4(0.95f,0.95f,1.0f,1.0f)), D(GUI_COLOR_OSC_WAVE_PEAK,"",ImVec4(0.95f,0.95f,1.0f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH0,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH1,"",ImVec4(1.0f,0.2f,0.2f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH2,"",ImVec4(0.1f,0.5f,1.0f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH3,"",ImVec4(0.5f,0.5f,0.5f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH4,"",ImVec4(0.7f,0.2f,0.7f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH5,"",ImVec4(0.2f,1.0f,0.2f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH6,"",ImVec4(1.0f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH7,"",ImVec4(1.0f,0.5f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH8,"",ImVec4(0.9f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH9,"",ImVec4(0.8f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH10,"",ImVec4(0.7f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH11,"",ImVec4(0.6f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH12,"",ImVec4(0.5f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH13,"",ImVec4(0.4f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH14,"",ImVec4(0.3f,1.0f,0.1f,1.0f)), + D(GUI_COLOR_OSC_WAVE_CH15,"",ImVec4(0.2f,1.0f,0.1f,1.0f)), D(GUI_COLOR_OSC_REF,"",ImVec4(0.3,0.65f,1.0f,0.15f)), D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,0.65f,1.0f,0.13f)), @@ -853,6 +932,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)), D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)), D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), + D(GUI_COLOR_INSTR_SCSP,"",ImVec4(0.5f,0.5f,0.5f,1.0f)), + D(GUI_COLOR_INSTR_TED,"",ImVec4(0.7f,0.6f,1.0f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1037,6 +1118,7 @@ const int availableSystems[]={ DIV_SYSTEM_SM8521, DIV_SYSTEM_PV1000, DIV_SYSTEM_K053260, + DIV_SYSTEM_TED, DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PONG, 0 // don't remove this last one! @@ -1087,6 +1169,7 @@ const int chipsSquare[]={ DIV_SYSTEM_MSM5232, DIV_SYSTEM_T6W28, DIV_SYSTEM_PV1000, + DIV_SYSTEM_TED, 0 // don't remove this last one! }; diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index 00b35049..616692de 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -38,6 +38,8 @@ struct FurnaceGUIColorDef { extern const int opOrder[4]; extern const char* noteNames[180]; extern const char* noteNamesG[180]; +extern const char* noteNamesF[180]; +extern const char* noteNamesGF[180]; extern const char* pitchLabel[11]; extern const char* insTypes[]; extern const char* sampleLoopModes[]; @@ -55,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 8d0bf0d0..1bfcf284 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -267,6 +267,10 @@ const char* msm5232ControlBits[7]={ "16'", "8'", "4'", "2'", "sustain", NULL }; +const char* tedControlBits[3]={ + "square", "noise", NULL +}; + const char* x1_010EnvBits[8]={ "enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL }; @@ -2284,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); } @@ -4263,6 +4264,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } if (ins->type==DIV_INS_C64) if (ImGui::BeginTabItem("C64")) { + ImGui::AlignTextToFramePadding(); ImGui::Text("Waveform"); ImGui::SameLine(); pushToggleColors(ins->c64.triOn); @@ -4347,6 +4349,7 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar("Cutoff",ImGuiDataType_U16,&ins->c64.cut,&_ZERO,&_TWO_THOUSAND_FORTY_SEVEN)); rightClickable P(CWSliderScalar("Resonance",ImGuiDataType_U8,&ins->c64.res,&_ZERO,&_FIFTEEN)); rightClickable + ImGui::AlignTextToFramePadding(); ImGui::Text("Filter Mode"); ImGui::SameLine(); pushToggleColors(ins->c64.lp); @@ -4684,7 +4687,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)) { @@ -4696,7 +4699,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Text("Ch"); ImGui::TableNextColumn(); - ImGui::Text("Offset"); + ImGui::Text("Position"); ImGui::TableNextColumn(); ImGui::Text("Length"); @@ -4728,7 +4731,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; } @@ -5249,6 +5252,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Wave 1"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -5259,6 +5263,7 @@ void FurnaceGUI::drawInsEdit() { } if (isSingleWaveFX) { ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Wave 2"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -5389,7 +5394,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MSM6258) { volMax=0; } - if (ins->type==DIV_INS_MSM6295) { + if (ins->type==DIV_INS_MSM6295 || ins->type==DIV_INS_TED) { volMax=8; } if (ins->type==DIV_INS_ADPCMA) { @@ -5475,6 +5480,10 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="On/Off"; dutyMax=1; } + if (ins->type==DIV_INS_TED) { + dutyLabel="Square/Noise"; + dutyMax=2; + } if (ins->type==DIV_INS_SWAN) { dutyLabel="Noise"; dutyMax=ins->amiga.useSample?0:8; @@ -5555,9 +5564,11 @@ 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; + if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -5703,6 +5714,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits)); } else if (ins->type==DIV_INS_POKEY) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,pokeyCtlBits)); + } else if (ins->type==DIV_INS_TED) { + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,80,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,tedControlBits)); } else if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits)); } else if (ins->type==DIV_INS_ES5506) { @@ -5770,7 +5783,8 @@ void FurnaceGUI::drawInsEdit() { (ins->type==DIV_INS_X1_010 && ins->amiga.useSample) || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 || - ins->type==DIV_INS_K053260) { + ins->type==DIV_INS_K053260 || + ins->type==DIV_INS_TED) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { diff --git a/src/gui/intro.cpp b/src/gui/intro.cpp index bace1439..0fc1c960 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/mixer.cpp b/src/gui/mixer.cpp index 95dba125..dfa270ec 100644 --- a/src/gui/mixer.cpp +++ b/src/gui/mixer.cpp @@ -244,6 +244,7 @@ void FurnaceGUI::drawMixer() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); ImGui::TableNextColumn(); if (ImGui::Checkbox("Invert",&doInvert)) { @@ -337,9 +338,11 @@ void FurnaceGUI::drawMixer() { if (selectedSubPort>=0) { portDragActive=true; ImGui::InhibitInertialScroll(); - try { - subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort); - } catch (std::out_of_range& e) { + + auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort); + if (subPortI!=portPos.cend()) { + subPortPos=subPortI->second; + } else { portDragActive=false; } } @@ -353,9 +356,10 @@ void FurnaceGUI::drawMixer() { if (selectedSubPort>=0) { portDragActive=true; ImGui::InhibitInertialScroll(); - try { - subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort); - } catch (std::out_of_range& e) { + auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort); + if (subPortI!=portPos.cend()) { + subPortPos=subPortI->second; + } else { portDragActive=false; } } @@ -365,9 +369,10 @@ void FurnaceGUI::drawMixer() { if (selectedSubPort>=0) { portDragActive=true; ImGui::InhibitInertialScroll(); - try { - subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort); - } catch (std::out_of_range& e) { + auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort); + if (subPortI!=portPos.cend()) { + subPortPos=subPortI->second; + } else { portDragActive=false; } } @@ -380,9 +385,10 @@ void FurnaceGUI::drawMixer() { if (selectedSubPort>=0) { portDragActive=true; ImGui::InhibitInertialScroll(); - try { - subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort); - } catch (std::out_of_range& e) { + auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort); + if (subPortI!=portPos.cend()) { + subPortPos=subPortI->second; + } else { portDragActive=false; } } @@ -415,22 +421,24 @@ void FurnaceGUI::drawMixer() { // draw connections for (unsigned int i: e->song.patchbay) { if ((i>>20)==selectedPortSet) continue; - try { - ImVec2 portSrc=portPos.at(i>>16); - ImVec2 portDest=portPos.at(0x10000|(i&0xffff)); + auto portSrcI=portPos.find(i>>16); + auto portDestI=portPos.find(0x10000|(i&0xffff)); + if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) { + ImVec2 portSrc=portSrcI->second; + ImVec2 portDest=portDestI->second; dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION_BG]),2.0f*dpiScale); - } catch (std::out_of_range& e) { } } // foreground for (unsigned int i: e->song.patchbay) { if ((i>>20)!=selectedPortSet) continue; - try { - ImVec2 portSrc=portPos.at(i>>16); - ImVec2 portDest=portPos.at(0x10000|(i&0xffff)); + auto portSrcI=portPos.find(i>>16); + auto portDestI=portPos.find(0x10000|(i&0xffff)); + if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) { + ImVec2 portSrc=portSrcI->second; + ImVec2 portDest=portDestI->second; dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION]),2.0f*dpiScale); - } catch (std::out_of_range& e) { } } } diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 7f783dde..fc3b1724 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -20,6 +20,8 @@ #include "gui.h" #include "imgui_internal.h" #include +#include "../ta-log.h" +#include "../engine/filter.h" void FurnaceGUI::readOsc() { int writePos=e->oscWritePos; @@ -47,19 +49,52 @@ void FurnaceGUI::readOsc() { int winSize=e->getAudioDescGot().rate*(oscWindowSize/1000.0); int oscReadPos=(writePos-winSize)&0x7fff; - for (int i=0; i<512; i++) { - int pos=(oscReadPos+(i*winSize/512))&0x7fff; - oscValues[i]=0; - for (int j=0; jgetAudioDescGot().outChans; j++) { - oscValues[i]+=e->oscBuf[j][pos]; - } - oscValues[i]/=e->getAudioDescGot().outChans; - if (oscValues[i]>0.001f || oscValues[i]<-0.001f) { - WAKE_UP; + for (int ch=0; chgetAudioDescGot().outChans; ch++) { + if (oscValues[ch]==NULL) { + oscValues[ch]=new float[1024]; + } + memset(oscValues[ch],0,1024*sizeof(float)); + float* sincITable=DivFilterTables::getSincIntegralTable(); + + float posFrac=0.0; + int posInt=oscReadPos; + float factor=(float)oscWidth/(float)winSize; + for (int i=0; ioscBuf[ch][posInt&0x7fff]; + + posFrac+=1.0; + while (posFrac>=1.0) { + unsigned int n=((unsigned int)(posFrac*8192.0))&8191; + posFrac-=factor; + posInt++; + + float* t1=&sincITable[(8191-n)<<3]; + float* t2=&sincITable[n<<3]; + float delta=e->oscBuf[ch][posInt&0x7fff]-e->oscBuf[ch][(posInt-1)&0x7fff]; + + for (int j=0; j<8; j++) { + if (i-j>0) { + oscValues[ch][i-j]+=t1[j]*-delta; + } + if (i+j+10.001f || oscValues[ch][i]<-0.001f) { + WAKE_UP; + } } } + /*for (int i=0; igetAudioDescGot().outChans; i++) { peak[i]*=1.0-peakDecay; @@ -124,7 +159,7 @@ void FurnaceGUI::drawOsc() { ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); - ImVec2 waveform[512]; + ImVec2 waveform[1024]; ImVec2 size=ImGui::GetContentRegionAvail(); ImVec2 minArea=window->DC.CursorPos; @@ -211,22 +246,67 @@ void FurnaceGUI::drawOsc() { dpiScale ); - for (size_t i=0; i<512; i++) { - float x=(float)i/512.0f; - float y=oscValues[i]*oscZoom; - if (!settings.oscEscapesBoundary) { - if (y<-0.5f) y=-0.5f; - if (y>0.5f) y=0.5f; + oscWidth=round(inRect.Max.x-inRect.Min.x); + if (oscWidth<1) oscWidth=1; + if (oscWidth>1024) oscWidth=1024; + + ImDrawListFlags prevFlags=dl->Flags; + if (!settings.oscAntiAlias) { + dl->Flags&=~(ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex); + } + + if (settings.oscMono) { + for (int i=0; igetAudioDescGot().outChans; j++) { + avg+=oscValues[j][i]; + } + avg/=e->getAudioDescGot().outChans; + + float y=avg*oscZoom; + if (!settings.oscEscapesBoundary) { + if (y<-0.5f) y=-0.5f; + if (y>0.5f) y=0.5f; + } + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); + } + + if (settings.oscEscapesBoundary) { + dl->PushClipRectFullScreen(); + dl->AddPolyline(waveform,oscWidth,color,ImDrawFlags_None,dpiScale); + dl->PopClipRect(); + } else { + dl->AddPolyline(waveform,oscWidth,color,ImDrawFlags_None,dpiScale); } - waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); - } - if (settings.oscEscapesBoundary) { - dl->PushClipRectFullScreen(); - dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); - dl->PopClipRect(); } else { - dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); + for (int ch=0; chgetAudioDescGot().outChans; ch++) { + for (int i=0; i0.5f) y=0.5f; + } + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); + } + + if (!isClipping) { + color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]); + } + + if (settings.oscEscapesBoundary) { + dl->PushClipRectFullScreen(); + dl->AddPolyline(waveform,oscWidth,color,ImDrawFlags_None,dpiScale); + dl->PopClipRect(); + } else { + dl->AddPolyline(waveform,oscWidth,color,ImDrawFlags_None,dpiScale); + } + } } + + dl->Flags=prevFlags; + if (settings.oscBorder) { dl->AddRect(inRect.Min,inRect.Max,borderColor,settings.oscRoundedCorners?(8.0f*dpiScale):0.0f,0,1.5f*dpiScale); } diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 0b6d9678..2805af02 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -420,9 +420,6 @@ void FurnaceGUI::drawPattern() { //char id[32]; ImGui::PushFont(patFont); int ord=oldOrder; - if (followPattern) { - curOrder=e->getOrder(); - } oldOrder=curOrder; int chans=e->getTotalChannelCount(); int displayChans=0; @@ -440,7 +437,7 @@ void FurnaceGUI::drawPattern() { ImGui::SetCursorPosX(ImGui::GetCursorPosX()+centerOff); } } - if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea)) { + if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea|(settings.cursorFollowsWheel?ImGuiTableFlags_NoScrollWithMouse:0))) { ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed); char chanID[2048]; float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); @@ -829,8 +826,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/piano.cpp b/src/gui/piano.cpp index 4842d05c..3c501c45 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -100,6 +100,7 @@ void FurnaceGUI::drawPiano() { } if (ImGui::BeginPopupContextItem("PianoOptions",ImGuiPopupFlags_MouseButtonLeft)) { ImGui::Text("Key layout:"); + ImGui::Indent(); if (ImGui::RadioButton("Automatic",pianoView==PIANO_LAYOUT_AUTOMATIC)) { pianoView=PIANO_LAYOUT_AUTOMATIC; } @@ -109,7 +110,9 @@ void FurnaceGUI::drawPiano() { if (ImGui::RadioButton("Continuous",pianoView==PIANO_LAYOUT_CONTINUOUS)) { pianoView=PIANO_LAYOUT_CONTINUOUS; } + ImGui::Unindent(); ImGui::Text("Value input pad:"); + ImGui::Indent(); if (ImGui::RadioButton("Disabled",pianoInputPadMode==PIANO_INPUT_PAD_DISABLE)) { pianoInputPadMode=PIANO_INPUT_PAD_DISABLE; } @@ -122,6 +125,7 @@ void FurnaceGUI::drawPiano() { if (ImGui::RadioButton("Split (always visible)",pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE; } + ImGui::Unindent(); ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition); ImGui::Checkbox("Read-only (can't input notes)",&pianoReadonly); ImGui::EndPopup(); @@ -377,7 +381,6 @@ void FurnaceGUI::drawPiano() { pianoOptions=!pianoOptions; } - // TODO: wave and sample preview // first check released keys for (int i=0; i<180; i++) { int note=i-60; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 6d4b5e07..94ad6eee 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -278,6 +278,12 @@ void FurnaceGUI::initSystemPresets() { ENTRY( "Commodore VIC-20", { CH(DIV_SYSTEM_VIC20, 1.0f, 0, "clockSel=1") + }, + "tickRate=50" + ); + ENTRY( + "Commodore Plus/4", { + CH(DIV_SYSTEM_TED, 1.0f, 0, "") } ); ENTRY( @@ -985,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, @@ -1401,28 +1407,40 @@ void FurnaceGUI::initSystemPresets() { "Sega System 32", { CH(DIV_SYSTEM_YM2612, 1.0f, 0, "clockSel=4"), // discrete 8.05MHz YM3438 CH(DIV_SYSTEM_YM2612, 1.0f, 0, "clockSel=4"), // ^^ - CH(DIV_SYSTEM_RF5C68, 1.0f, 0, "clockSel=2") // 12.5MHz + CH(DIV_SYSTEM_RF5C68, 1.0f, 0, + "clockSel=2\n" + "chipType=1\n" + ) // 12.5MHz } ); ENTRY( "Sega System 32 (extended channel 3 on first OPN2C)", { CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, "clockSel=4"), // discrete 8.05MHz YM3438 CH(DIV_SYSTEM_YM2612, 1.0f, 0, "clockSel=4"), // ^^ - CH(DIV_SYSTEM_RF5C68, 1.0f, 0, "clockSel=2") // 12.5MHz + CH(DIV_SYSTEM_RF5C68, 1.0f, 0, + "clockSel=2\n" + "chipType=1\n" + ) // 12.5MHz } ); ENTRY( "Sega System 32 (extended channel 3 on second OPN2C)", { CH(DIV_SYSTEM_YM2612, 1.0f, 0, "clockSel=4"), // discrete 8.05MHz YM3438 CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, "clockSel=4"), // ^^ - CH(DIV_SYSTEM_RF5C68, 1.0f, 0, "clockSel=2") // 12.5MHz + CH(DIV_SYSTEM_RF5C68, 1.0f, 0, + "clockSel=2\n" + "chipType=1\n" + ) // 12.5MHz } ); ENTRY( "Sega System 32 (extended channel 3 on both OPN2Cs)", { CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, "clockSel=4"), // discrete 8.05MHz YM3438 CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, "clockSel=4"), // ^^ - CH(DIV_SYSTEM_RF5C68, 1.0f, 0, "clockSel=2") // 12.5MHz + CH(DIV_SYSTEM_RF5C68, 1.0f, 0, + "clockSel=2\n" + "chipType=1\n" + ) // 12.5MHz } ); ENTRY( @@ -2449,6 +2467,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_PV1000, 1.0f, 0, "") } ); + ENTRY( + "MOS Technology TED", { + CH(DIV_SYSTEM_TED, 1.0f, 0, "clockSel=1") + } + ); CATEGORY_END; CATEGORY_BEGIN("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis."); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 99ccbd22..5cea58f0 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -372,6 +372,7 @@ void FurnaceGUI::drawSampleEdit() { if (sampleInfo) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Type"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -445,6 +446,7 @@ void FurnaceGUI::drawSampleEdit() { bool coarseChanged=false; ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Hz"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -459,6 +461,7 @@ void FurnaceGUI::drawSampleEdit() { } } + ImGui::AlignTextToFramePadding(); ImGui::Text("Note"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -501,6 +504,7 @@ void FurnaceGUI::drawSampleEdit() { } } + ImGui::AlignTextToFramePadding(); ImGui::Text("Fine"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -537,6 +541,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::TableNextColumn(); ImGui::BeginDisabled(!(doLoop || keepLoopAlive)); keepLoopAlive=false; + ImGui::AlignTextToFramePadding(); ImGui::Text("Mode"); ImGui::SameLine(); pushWarningColor(!warnLoopMode.empty()); @@ -560,6 +565,7 @@ void FurnaceGUI::drawSampleEdit() { popWarningColor(); pushWarningColor(!warnLoopPos.empty()); + ImGui::AlignTextToFramePadding(); ImGui::Text("Start"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -585,6 +591,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("%s",warnLoopPos.c_str()); } + ImGui::AlignTextToFramePadding(); ImGui::Text("End"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1018,6 +1025,7 @@ void FurnaceGUI::drawSampleEdit() { if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; } + ImGui::AlignTextToFramePadding(); ImGui::Text("Power"); ImGui::SameLine(); if (ImGui::RadioButton("1x",sampleFilterPower==1)) { @@ -1601,7 +1609,7 @@ void FurnaceGUI::drawSampleEdit() { posX=samplePos+pos.x*sampleZoom; if (posX>(int)sample->samples) posX=-1; } - posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767); + posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:65535); if (posX>=0) { statusBar2=fmt::sprintf("(%d, %d)",posX,posY); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 028a5407..28635aa4 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", @@ -229,6 +239,7 @@ const char* specificControls[18]={ #define UI_KEYBIND_CONFIG(what) \ ImGui::TableNextRow(); \ ImGui::TableNextColumn(); \ + ImGui::AlignTextToFramePadding();\ ImGui::TextUnformatted(guiActions[what].friendlyName); \ ImGui::TableNextColumn(); \ if (ImGui::Button(fmt::sprintf("%s##KC_" #what,(bindSetPending && bindSetTarget==what)?"Press key...":getKeyName(actionKeys[what])).c_str())) { \ @@ -236,6 +247,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 +318,1340 @@ 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("Play after opening song:"); + ImGui::Indent(); + if (ImGui::RadioButton("No##pol0",settings.playOnLoad==0)) { + settings.playOnLoad=0; + } + if (ImGui::RadioButton("Only if already playing##pol1",settings.playOnLoad==1)) { + settings.playOnLoad=1; + } + if (ImGui::RadioButton("Yes##pol0",settings.playOnLoad==2)) { + settings.playOnLoad=2; + } + ImGui::Unindent(); + + ImGui::Text("Audio export loop/fade out time:"); + ImGui::Indent(); + if (ImGui::RadioButton("Set to these values on start-up:##fot0",settings.persistFadeOut==0)) { + settings.persistFadeOut=0; + } + ImGui::BeginDisabled(settings.persistFadeOut); + ImGui::Indent(); + 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::Unindent(); + ImGui::EndDisabled(); + if (ImGui::RadioButton("Remember last values##fot1",settings.persistFadeOut==1)) { + settings.persistFadeOut=1; + } + ImGui::Unindent(); + + // SUBSECTION CHIP + CONFIG_SUBSECTION("Chip"); + ImGui::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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:"); + ImGui::Indent(); + 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; + } + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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; + } + ImGui::Unindent(); + + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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:"); + ImGui::Indent(); + 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; + }*/ + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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; + } + ImGui::Unindent(); + } + + END_SECTION; + } + CONFIG_SECTION("Emulation") { + // SUBSECTION LAYOUT + CONFIG_SUBSECTION("Cores"); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Arcade/YM2151 core"); + ImGui::SameLine(); + ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Genesis/YM2612 core"); + ImGui::SameLine(); + ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("SN76489 core"); + ImGui::SameLine(); + ImGui::Combo("##SNCore",&settings.snCore,snCores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("NES core"); + ImGui::SameLine(); + ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("FDS core"); + ImGui::SameLine(); + ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("SID core"); + ImGui::SameLine(); + ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,3); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("POKEY core"); + ImGui::SameLine(); + ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("OPN/OPNA/OPNB cores"); + ImGui::SameLine(); + ImGui::Combo("##OPNCore",&settings.opnCore,opnCores,2); + + ImGui::Separator(); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("PC Speaker strategy"); + ImGui::SameLine(); + ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); + + ImGui::Separator(); + ImGui::Text("Sample ROMs:"); + + ImGui::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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::AlignTextToFramePadding(); + 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,1920 +1661,1171 @@ 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(); + + bool centerPopupB=settings.centerPopup; + if (ImGui::Checkbox("Center pop-up windows",¢erPopupB)) { + settings.centerPopup=centerPopupB; + } + + ImGui::Text("Play/edit controls layout:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Position of buttons in Orders:"); + ImGui::Indent(); + 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::Unindent(); + + // 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:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Allow dragging selection:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Toggle channel solo on:"); + ImGui::Indent(); + 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; + } + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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::Unindent(); + + 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; + } + + bool removeInsOffB=settings.removeInsOff; + if (ImGui::Checkbox("Remove instrument value when inserting note off/release",&removeInsOffB)) { + settings.removeInsOff=removeInsOffB; + } + + bool removeVolOffB=settings.removeVolOff; + if (ImGui::Checkbox("Remove volume value when inserting note off/release",&removeVolOffB)) { + settings.removeVolOff=removeVolOffB; + } + + // SUBSECTION CURSOR MOVEMENT + CONFIG_SUBSECTION("Cursor movement"); + + ImGui::Text("Wrap horizontally:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Wrap vertically:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Cursor movement keys behavior:"); + ImGui::Indent(); + 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::Unindent(); + + 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:"); + ImGui::Indent(); + 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::Unindent(); + + 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::AlignTextToFramePadding(); + ImGui::Text("Main font"); + ImGui::SameLine(); + ImGui::Combo("##MainFont",&settings.mainFont,mainFonts,7); + ImGui::Indent(); + 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::Unindent(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Header font"); + ImGui::SameLine(); + ImGui::Combo("##HeadFont",&settings.headFont,headFonts,7); + ImGui::Indent(); + 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::Unindent(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Pattern font"); + ImGui::SameLine(); + ImGui::Combo("##PatFont",&settings.patFont,patFonts,7); + ImGui::Indent(); + 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; + } + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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(); + } + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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::Unindent(); + + bool capitalMenuBarB=settings.capitalMenuBar; + if (ImGui::Checkbox("Capitalize menu bar",&capitalMenuBarB)) { + settings.capitalMenuBar=capitalMenuBarB; + } + + // 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:"); + ImGui::Indent(); + if (ImGui::RadioButton("Decimal##orbD",settings.orderRowsBase==0)) { + settings.orderRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##orbH",settings.orderRowsBase==1)) { + settings.orderRowsBase=1; + } + ImGui::Unindent(); + + // 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:"); + ImGui::Indent(); + if (ImGui::RadioButton("Decimal##prbD",settings.patRowsBase==0)) { + settings.patRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##prbH",settings.patRowsBase==1)) { + settings.patRowsBase=1; + } + ImGui::Unindent(); + + 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:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Channel volume bar:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Channel feedback style:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Channel font:"); + ImGui::Indent(); + if (ImGui::RadioButton("Regular##CHFont0",settings.channelFont==0)) { + settings.channelFont=0; + } + if (ImGui::RadioButton("Monospace##CHFont1",settings.channelFont==1)) { + settings.channelFont=1; + } + ImGui::Unindent(); + + bool channelTextCenterB=settings.channelTextCenter; + if (ImGui::Checkbox("Center channel name",&channelTextCenterB)) { + settings.channelTextCenter=channelTextCenterB; + } + + ImGui::Text("Channel colors:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Channel name colors:"); + ImGui::Indent(); + 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::Unindent(); + + // 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; + } + + bool insTypeMenuB=settings.insTypeMenu; + if (ImGui::Checkbox("Display instrument type menu when adding instrument",&insTypeMenuB)) { + settings.insTypeMenu=insTypeMenuB; + } + + // SUBSECTION MACRO EDITOR + CONFIG_SUBSECTION("Macro Editor"); + ImGui::Text("Macro editor layout:"); + ImGui::Indent(); + 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::Unindent(); + + 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:"); + ImGui::Indent(); + 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::Unindent(); + + bool oplStandardWaveNamesB=settings.oplStandardWaveNames; + if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { + settings.oplStandardWaveNames=oplStandardWaveNamesB; + } + + ImGui::Text("FM parameter editor layout:"); + ImGui::Indent(); + 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::Unindent(); + + ImGui::Text("Position of Sustain in FM editor:"); + ImGui::Indent(); + 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::Unindent(); + + 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:"); + ImGui::Indent(); + if (ImGui::RadioButton("Bytes##MUU0",settings.memUsageUnit==0)) { + settings.memUsageUnit=0; + } + if (ImGui::RadioButton("Kilobytes##MUU1",settings.memUsageUnit==1)) { + settings.memUsageUnit=1; + } + ImGui::Unindent(); + + // 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 oscMonoB=settings.oscMono; + if (ImGui::Checkbox("Mono",&oscMonoB)) { + settings.oscMono=oscMonoB; + } + + bool oscAntiAliasB=settings.oscAntiAlias; + if (ImGui::Checkbox("Anti-aliased",&oscAntiAliasB)) { + settings.oscAntiAlias=oscAntiAliasB; + } + + 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 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"); + + if (ImGui::TreeNode("Wave (non-mono)")) { + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH0,"Waveform (1)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH1,"Waveform (2)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH2,"Waveform (3)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH3,"Waveform (4)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH4,"Waveform (5)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH5,"Waveform (6)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH6,"Waveform (7)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH7,"Waveform (8)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH8,"Waveform (9)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH9,"Waveform (10)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH10,"Waveform (11)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH11,"Waveform (12)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH12,"Waveform (13)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH13,"Waveform (14)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH14,"Waveform (15)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_CH15,"Waveform (16)"); + ImGui::TreePop(); + } + 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. @@ -2593,44 +2834,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(); @@ -2645,6 +2883,11 @@ void FurnaceGUI::drawSettings() { settingsOpen=false; syncSettings(); } + ImGui::SameLine(); + if (ImGui::Button("Apply##SettingsApply")) { + settingsOpen=true; + willCommit=true; + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SETTINGS; ImGui::End(); @@ -2660,6 +2903,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; @@ -2685,8 +2929,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); @@ -2700,6 +2946,7 @@ void FurnaceGUI::syncSettings() { settings.chipNames=e->getConfInt("chipNames",0); settings.overflowHighlight=e->getConfInt("overflowHighlight",0); settings.partyTime=e->getConfInt("partyTime",0); + settings.flatNotes=e->getConfInt("flatNotes",0); settings.germanNotation=e->getConfInt("germanNotation",0); settings.stepOnDelete=e->getConfInt("stepOnDelete",0); settings.scrollStep=e->getConfInt("scrollStep",0); @@ -2738,6 +2985,8 @@ void FurnaceGUI::syncSettings() { settings.oscTakesEntireWindow=e->getConfInt("oscTakesEntireWindow",0); settings.oscBorder=e->getConfInt("oscBorder",1); settings.oscEscapesBoundary=e->getConfInt("oscEscapesBoundary",0); + settings.oscMono=e->getConfInt("oscMono",1); + settings.oscAntiAlias=e->getConfInt("oscAntiAlias",1); settings.separateFMColors=e->getConfInt("separateFMColors",0); settings.insEditColorize=e->getConfInt("insEditColorize",0); settings.metroVol=e->getConfInt("metroVol",100); @@ -2810,8 +3059,16 @@ 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); + settings.removeInsOff=e->getConfInt("removeInsOff",0); + settings.removeVolOff=e->getConfInt("removeVolOff",0); + settings.playOnLoad=e->getConfInt("playOnLoad",0); + settings.insTypeMenu=e->getConfInt("insTypeMenu",1); + settings.capitalMenuBar=e->getConfInt("capitalMenuBar",0); + settings.centerPopup=e->getConfInt("centerPopup",1); 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); @@ -2842,6 +3099,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.chipNames,0,1); clampSetting(settings.overflowHighlight,0,1); clampSetting(settings.partyTime,0,1); + clampSetting(settings.flatNotes,0,1); clampSetting(settings.germanNotation,0,1); clampSetting(settings.stepOnDelete,0,1); clampSetting(settings.scrollStep,0,1); @@ -2938,6 +3196,13 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.newSongBehavior,0,1); clampSetting(settings.memUsageUnit,0,1); clampSetting(settings.cursorFollowsWheel,0,1); + clampSetting(settings.noDMFCompat,0,1); + clampSetting(settings.removeInsOff,0,1); + clampSetting(settings.removeVolOff,0,1); + clampSetting(settings.playOnLoad,0,2); + clampSetting(settings.insTypeMenu,0,1); + clampSetting(settings.capitalMenuBar,0,1); + clampSetting(settings.centerPopup,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -3011,6 +3276,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])); @@ -3036,8 +3302,10 @@ void FurnaceGUI::commitSettings() { e->setConf("tg100Path",settings.tg100Path); e->setConf("mu5Path",settings.mu5Path); e->setConf("mainFont",settings.mainFont); + e->setConf("headFont",settings.headFont); e->setConf("patFont",settings.patFont); e->setConf("mainFontPath",settings.mainFontPath); + e->setConf("headFontPath",settings.headFontPath); e->setConf("patFontPath",settings.patFontPath); e->setConf("patRowsBase",settings.patRowsBase); e->setConf("orderRowsBase",settings.orderRowsBase); @@ -3051,6 +3319,7 @@ void FurnaceGUI::commitSettings() { e->setConf("chipNames",settings.chipNames); e->setConf("overflowHighlight",settings.overflowHighlight); e->setConf("partyTime",settings.partyTime); + e->setConf("flatNotes",settings.flatNotes); e->setConf("germanNotation",settings.germanNotation); e->setConf("stepOnDelete",settings.stepOnDelete); e->setConf("scrollStep",settings.scrollStep); @@ -3089,6 +3358,8 @@ void FurnaceGUI::commitSettings() { e->setConf("oscTakesEntireWindow",settings.oscTakesEntireWindow); e->setConf("oscBorder",settings.oscBorder); e->setConf("oscEscapesBoundary",settings.oscEscapesBoundary); + e->setConf("oscMono",settings.oscMono); + e->setConf("oscAntiAlias",settings.oscAntiAlias); e->setConf("separateFMColors",settings.separateFMColors); e->setConf("insEditColorize",settings.insEditColorize); e->setConf("metroVol",settings.metroVol); @@ -3162,6 +3433,13 @@ void FurnaceGUI::commitSettings() { e->setConf("newSongBehavior",settings.newSongBehavior); e->setConf("memUsageUnit",settings.memUsageUnit); e->setConf("cursorFollowsWheel",settings.cursorFollowsWheel); + e->setConf("noDMFCompat",settings.noDMFCompat); + e->setConf("removeInsOff",settings.removeInsOff); + e->setConf("removeVolOff",settings.removeVolOff); + e->setConf("playOnLoad",settings.playOnLoad); + e->setConf("insTypeMenu",settings.insTypeMenu); + e->setConf("capitalMenuBar",settings.capitalMenuBar); + e->setConf("centerPopup",settings.centerPopup); // 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!"); @@ -3589,9 +3868,10 @@ void FurnaceGUI::popWarningColor() { #ifdef _WIN32 #define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" #define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! -#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! +#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\micross.ttf" +#define SYSTEM_HEAD_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" +#define SYSTEM_HEAD_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" +#define SYSTEM_HEAD_FONT_PATH_3 "C:\\Windows\\Fonts\\micross.ttf" #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 @@ -3600,6 +3880,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" @@ -3608,6 +3891,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" @@ -3615,6 +3901,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" @@ -3855,12 +4144,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("header 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; @@ -3946,16 +4240,51 @@ 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; } - // TODO: allow changing these colors. ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",uiColors[GUI_COLOR_FILE_DIR],ICON_FA_FOLDER_O); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_FILE_O); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",uiColors[GUI_COLOR_FILE_SONG_NATIVE],ICON_FA_FILE); diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index 65803e57..b1c4895a 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -36,6 +36,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Name"); ImGui::TableNextColumn(); float avail=ImGui::GetContentRegionAvail().x; @@ -59,6 +60,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { } ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Author"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); @@ -68,6 +70,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Album"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); @@ -77,6 +80,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { if (!basicMode) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("System"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x)); @@ -112,6 +116,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Tuning (A-4)"); ImGui::TableNextColumn(); float tune=e->song.tuning; diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp index 5557c663..59346785 100644 --- a/src/gui/speed.cpp +++ b/src/gui/speed.cpp @@ -37,6 +37,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); if (ImGui::SmallButton(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { tempoView=!tempoView; } @@ -74,6 +75,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); if (keepGrooveAlive || e->curSubSong->speeds.len>2) { if (ImGui::SmallButton("Groove")) { e->lockEngine([this]() { @@ -163,6 +165,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (!basicMode) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Virtual Tempo"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(halfAvail); @@ -185,6 +188,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Divider"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(halfAvail); @@ -200,6 +204,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Highlight"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(halfAvail); @@ -222,6 +227,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Pattern Length"); ImGui::TableNextColumn(); float avail=ImGui::GetContentRegionAvail().x; @@ -236,6 +242,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (!basicMode) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Song Length"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index ed879ff2..5bc2ee10 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -32,6 +32,7 @@ void FurnaceGUI::drawStats() { size_t lastProcTime=e->processTime; double maxGot=1000000000.0*(double)e->getAudioDescGot().bufsize/(double)e->getAudioDescGot().rate; String procStr=fmt::sprintf("%.1f%%",100.0*((double)lastProcTime/(double)maxGot)); + ImGui::AlignTextToFramePadding(); ImGui::Text("Audio load"); ImGui::SameLine(); ImGui::ProgressBar((double)lastProcTime/maxGot,ImVec2(-FLT_MIN,0),procStr.c_str()); @@ -47,6 +48,7 @@ void FurnaceGUI::drawStats() { } else { usageStr=fmt::sprintf("%d/%d",usage,capacity); } + ImGui::AlignTextToFramePadding(); ImGui::Text("%s [%d]", e->getSystemName(e->song.system[i]), j); ImGui::SameLine(); ImGui::ProgressBar(((float)usage)/((float)capacity),ImVec2(-FLT_MIN,0),usageStr.c_str()); diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index 5d34c23f..15b107ed 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -127,6 +127,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) { ImGui::SetTooltip("Remove"); } + ImGui::AlignTextToFramePadding(); ImGui::Text("Name"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 9991cc2e..c111e2ba 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; @@ -44,6 +44,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool fbAllOps=flags.getBool("fbAllOps",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("NTSC (7.67MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -64,8 +65,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=4; altered=true; } + ImGui::Unindent(); ImGui::Text("Chip type:"); + ImGui::Indent(); if (ImGui::RadioButton("YM3438 (9-bit DAC)",chipType==0)) { chipType=0; altered=true; @@ -78,6 +81,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=2; altered=true; } + ImGui::Unindent(); if (type==DIV_SYSTEM_YM2612_EXT || type==DIV_SYSTEM_YM2612_DUALPCM_EXT || type==DIV_SYSTEM_YM2612_CSM) { if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) { @@ -105,6 +109,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool noEasyNoise=flags.getBool("noEasyNoise",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -133,7 +138,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=6; altered=true; } + ImGui::Unindent(); ImGui::Text("Chip type:"); + ImGui::Indent(); if (ImGui::RadioButton("Sega VDP/Master System",chipType==0)) { chipType=0; altered=true; @@ -174,6 +181,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=9; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { altered=true; @@ -205,6 +213,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } ImGui::Text("Chip revision:"); + ImGui::Indent(); if (ImGui::RadioButton("HuC6280 (original)",chipType==0)) { chipType=0; altered=true; @@ -213,6 +222,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=1; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -235,6 +245,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int echoVol=(signed char)flags.getInt("echoVol",0); ImGui::Text("CPU rate:"); + ImGui::Indent(); if (ImGui::RadioButton("6.18MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -243,7 +254,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=1; altered=true; } + ImGui::Unindent(); ImGui::Text("Sample memory:"); + ImGui::Indent(); if (ImGui::RadioButton("8K (rev A/B/E)",sampleMemSize==0)) { sampleMemSize=0; altered=true; @@ -252,7 +265,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo sampleMemSize=1; altered=true; } + ImGui::Unindent(); ImGui::Text("DAC resolution:"); + ImGui::Indent(); if (ImGui::RadioButton("16-bit (rev A/B/D/F)",pdm==0)) { pdm=false; altered=true; @@ -261,6 +276,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo pdm=true; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("Enable echo",&echo)) { altered=true; } @@ -317,6 +333,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } ImGui::Text("Chip revision:"); + ImGui::Indent(); if (ImGui::RadioButton("Original (DMG)",chipType==0)) { chipType=0; altered=true; @@ -333,8 +350,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=3; altered=true; } + ImGui::Unindent(); ImGui::Text("Wave channel orientation:"); if (chipType==3) { + ImGui::Indent(); if (ImGui::RadioButton("Normal",!invertWave)) { invertWave=false; altered=true; @@ -343,7 +362,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo invertWave=true; altered=true; } + ImGui::Unindent(); } else { + ImGui::Indent(); if (ImGui::RadioButton("Exact data (inverted)",!invertWave)) { invertWave=false; altered=true; @@ -352,6 +373,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo invertWave=true; altered=true; } + ImGui::Unindent(); } if (ImGui::Checkbox("Pretty please one more compat flag when I use arpeggio and my sound length",&enoughAlready)) { altered=true; @@ -373,8 +395,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); int patchSet=flags.getInt("patchSet",0); bool noTopHatFreq=flags.getBool("noTopHatFreq",false); + bool fixedAll=flags.getBool("fixedAll",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("NTSC (3.58MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -391,8 +415,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=3; altered=true; } + ImGui::Unindent(); if (type!=DIV_SYSTEM_VRC7) { ImGui::Text("Patch set:"); + ImGui::Indent(); if (ImGui::RadioButton("Yamaha YM2413",patchSet==0)) { patchSet=0; altered=true; @@ -409,12 +435,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo patchSet=3; altered=true; } + ImGui::Unindent(); } if (type==DIV_SYSTEM_OPLL_DRUMS) { if (ImGui::Checkbox("Ignore top/hi-hat frequency changes",&noTopHatFreq)) { altered=true; } + if (ImGui::Checkbox("Apply fixed frequency to all drums at once",&fixedAll)) { + altered=true; + } } if (altered) { @@ -424,6 +454,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo flags.set("patchSet",patchSet); } flags.set("noTopHatFreq",noTopHatFreq); + flags.set("fixedAll",fixedAll); }); } break; @@ -431,6 +462,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo case DIV_SYSTEM_YM2151: { int clockSel=flags.getInt("clockSel",0); + ImGui::Indent(); if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -443,6 +475,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -460,6 +493,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -472,9 +506,11 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); ImGui::Text("DPCM channel mode:"); + ImGui::Indent(); if (ImGui::RadioButton("DPCM (muffled samples; low CPU usage)",dpcmMode)) { dpcmMode=true; altered=true; @@ -483,6 +519,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo dpcmMode=false; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -503,6 +540,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("NTSC (1.02MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -515,9 +553,11 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); ImGui::Text("Global parameter priority:"); + ImGui::Indent(); if (ImGui::RadioButton("Left to right",!keyPriority)) { keyPriority=false; altered=true; @@ -526,6 +566,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo keyPriority=true; altered=true; } + ImGui::Unindent(); ImGui::Text("Hard reset envelope:"); @@ -576,6 +617,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int ssgVol=flags.getInt("ssgVol",128); int fmVol=flags.getInt("fmVol",256); + ImGui::Indent(); if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) { clockSel=0; altered=true; @@ -584,6 +626,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=1; altered=true; } + ImGui::Unindent(); if (type==DIV_SYSTEM_YM2610_EXT || type==DIV_SYSTEM_YM2610_FULL_EXT || type==DIV_SYSTEM_YM2610B_EXT || type==DIV_SYSTEM_YM2610_CSM || type==DIV_SYSTEM_YM2610B_CSM) { if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) { @@ -626,6 +669,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int stereoSep=flags.getInt("stereoSep",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",clockSel==0)) { clockSel=0; altered=true; @@ -686,8 +730,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=14; altered=true; } + ImGui::Unindent(); if (type==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); + ImGui::Indent(); if (ImGui::RadioButton("AY-3-8910",chipType==0)) { chipType=0; altered=true; @@ -704,6 +750,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=3; altered=true; } + ImGui::Unindent(); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("note: AY-3-8914 is not supported by the VGM format!"); } @@ -744,6 +791,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo case DIV_SYSTEM_SAA1099: { int clockSel=flags.getInt("clockSel",0); + ImGui::Indent(); if (ImGui::RadioButton("SAM Coupé (8MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -756,6 +804,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -779,6 +828,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } rightClickable ImGui::Text("Model:"); + ImGui::Indent(); if (ImGui::RadioButton("Amiga 500 (OCS)",chipType==0)) { chipType=0; altered=true; @@ -787,8 +837,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=1; altered=true; } + ImGui::Unindent(); ImGui::Text("Chip memory:"); + ImGui::Indent(); if (ImGui::RadioButton("2MB (ECS/AGA max)",chipMem==21)) { chipMem=21; altered=true; @@ -805,6 +857,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipMem=18; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("PAL",&clockSel)) { @@ -830,6 +883,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int mixingType=flags.getInt("mixingType",0); ImGui::Text("Mixing mode:"); + ImGui::Indent(); if (ImGui::RadioButton("Mono",mixingType==0)) { mixingType=0; altered=true; @@ -842,6 +896,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo mixingType=2; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("PAL",&clockSel)) { altered=true; @@ -860,6 +915,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int speakerType=flags.getInt("speakerType",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("1.19MHz (PC)",clockSel==0)) { clockSel=0; altered=true; @@ -872,8 +928,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); ImGui::Text("Speaker type:"); + ImGui::Indent(); if (ImGui::RadioButton("Unfiltered",speakerType==0)) { speakerType=0; altered=true; @@ -890,6 +948,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo speakerType=3; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -933,6 +992,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool stereo=flags.getBool("stereo",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("16MHz (Seta 1)",clockSel==0)) { clockSel=0; altered=true; @@ -945,6 +1005,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("Stereo",&stereo)) { altered=true; @@ -962,8 +1023,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); int channels=flags.getInt("channels",0)+1; bool multiplex=flags.getBool("multiplex",false); + bool lenCompensate=flags.getBool("lenCompensate",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) { clockSel=0; altered=true; @@ -976,6 +1039,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); ImGui::Text("Initial channel limit:"); if (CWSliderInt("##N163_InitialChannelLimit",&channels,1,8)) { if (channels<1) channels=1; @@ -985,12 +1049,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo if (ImGui::Checkbox("Disable hissing",&multiplex)) { altered=true; } + if (ImGui::Checkbox("Scale frequency to wave length",&lenCompensate)) { + altered=true; + } if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); flags.set("channels",channels-1); flags.set("multiplex",multiplex); + flags.set("lenCompensate",lenCompensate); }); } break; @@ -1032,6 +1100,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -1056,7 +1125,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=5; altered=true; } + ImGui::Unindent(); ImGui::Text("Output rate:"); + ImGui::Indent(); if (ImGui::RadioButton("FM: clock / 72, SSG: clock / 16",prescale==0)) { prescale=0; altered=true; @@ -1069,6 +1140,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo prescale=2; altered=true; } + ImGui::Unindent(); if (CWSliderInt("SSG Volume",&ssgVol,0,256)) { if (ssgVol<0) ssgVol=0; @@ -1114,6 +1186,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int fmVol=flags.getInt("fmVol",256); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) { clockSel=0; altered=true; @@ -1122,7 +1195,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=1; altered=true; } + ImGui::Unindent(); ImGui::Text("Output rate:"); + ImGui::Indent(); if (ImGui::RadioButton("FM: clock / 144, SSG: clock / 32",prescale==0)) { prescale=0; altered=true; @@ -1135,6 +1210,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo prescale=2; altered=true; } + ImGui::Unindent(); if (CWSliderInt("SSG Volume",&ssgVol,0,256)) { if (ssgVol<0) ssgVol=0; @@ -1174,6 +1250,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int chipType=flags.getInt("chipType",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("8MHz (FM Towns)",clockSel==0)) { clockSel=0; altered=true; @@ -1186,7 +1263,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=2; altered=true; } + ImGui::Unindent(); ImGui::Text("Chip type:"); + ImGui::Indent(); if (ImGui::RadioButton("RF5C68 (10-bit output)",chipType==0)) { chipType=0; altered=true; @@ -1195,6 +1274,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=1; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1208,6 +1288,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("4MHz",clockSel==0)) { clockSel=0; altered=true; @@ -1224,6 +1305,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=3; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1237,6 +1319,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool rateSel=flags.getBool("rateSel",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("1MHz",clockSel==0)) { clockSel=0; altered=true; @@ -1297,7 +1380,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=14; altered=true; } + ImGui::Unindent(); ImGui::Text("Output rate:"); + ImGui::Indent(); if (ImGui::RadioButton("clock / 132",rateSel==0)) { rateSel=false; altered=true; @@ -1306,6 +1391,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo rateSel=true; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1320,6 +1406,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("1.79MHz (NTSC/MSX)",clockSel==0)) { clockSel=0; altered=true; @@ -1336,6 +1423,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=3; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1353,6 +1441,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -1377,6 +1466,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=5; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1392,6 +1482,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool compatPan=flags.getBool("compatPan",false); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("14.32MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -1425,6 +1516,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo chipType=1; altered=true; } + ImGui::Unindent(); if (ImGui::Checkbox("Compatible panning (0800)",&compatPan)) { altered=true; @@ -1443,6 +1535,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("16.9344MHz",clockSel==0)) { clockSel=0; altered=true; @@ -1467,6 +1560,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=5; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1499,6 +1593,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } ImGui::Text("Interpolation:"); + ImGui::Indent(); if (ImGui::RadioButton("None",interpolation==0)) { interpolation=0; altered=true; @@ -1515,6 +1610,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo interpolation=3; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1526,7 +1622,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } break; } - case DIV_SYSTEM_SNES: { // TODO: echo + case DIV_SYSTEM_SNES: { char temp[64]; int vsL=127-(flags.getInt("volScaleL",0)&127); int vsR=127-(flags.getInt("volScaleR",0)&127); @@ -1574,7 +1670,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)) { @@ -1745,6 +1847,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } ImGui::Text("Envelope mode (channel 1-4):"); + ImGui::Indent(); if (ImGui::RadioButton("Capacitor (attack/decay)##EM00",groupEnv[0])) { groupEnv[0]=true; altered=true; @@ -1753,8 +1856,10 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo groupEnv[0]=false; altered=true; } + ImGui::Unindent(); ImGui::Text("Envelope mode (channel 5-8):"); + ImGui::Indent(); if (ImGui::RadioButton("Capacitor (attack/decay)##EM10",groupEnv[1])) { groupEnv[1]=true; altered=true; @@ -1763,6 +1868,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo groupEnv[1]=false; altered=true; } + ImGui::Unindent(); ImGui::Text("Global vibrato:"); @@ -1838,6 +1944,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo bool romMode=flags.getBool("romMode",false); ImGui::Text("Waveform storage mode:"); + ImGui::Indent(); if (ImGui::RadioButton("RAM",!romMode)) { romMode=false; altered=true; @@ -1846,6 +1953,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo romMode=true; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1901,6 +2009,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo int clockSel=flags.getInt("clockSel",0); ImGui::Text("Clock rate:"); + ImGui::Indent(); if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { clockSel=0; altered=true; @@ -1909,6 +2018,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo clockSel=1; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { @@ -1917,6 +2027,44 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } break; } + case DIV_SYSTEM_TED: { + int clockSel=flags.getInt("clockSel",0); + bool keyPriority=flags.getBool("keyPriority",true); + + ImGui::Text("Clock rate:"); + + ImGui::Indent(); + if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) { + clockSel=0; + altered=true; + } + if (ImGui::RadioButton("PAL (1.77MHz)",clockSel==1)) { + clockSel=1; + altered=true; + } + ImGui::Unindent(); + + ImGui::Text("Global parameter priority:"); + + ImGui::Indent(); + if (ImGui::RadioButton("Left to right",!keyPriority)) { + keyPriority=false; + altered=true; + } + if (ImGui::RadioButton("Last used channel",keyPriority)) { + keyPriority=true; + altered=true; + } + ImGui::Unindent(); + + if (altered) { + e->lockSave([&]() { + flags.set("clockSel",clockSel); + flags.set("keyPriority",keyPriority); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: @@ -1958,11 +2106,13 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } altered=true; } + ImGui::Indent(); if (ImGui::InputInt("Hz",&customClock)) { if (customClockMAX_CUSTOM_CLOCK) customClock=MAX_CUSTOM_CLOCK; altered=true; } + ImGui::Unindent(); if (altered) { e->lockSave([&]() { diff --git a/src/gui/sysEx.cpp b/src/gui/sysEx.cpp index 5a72649e..aa86363f 100644 --- a/src/gui/sysEx.cpp +++ b/src/gui/sysEx.cpp @@ -60,8 +60,7 @@ bool FurnaceGUI::parseSysEx(unsigned char* data, size_t len) { op.rs=reader.readC(); reader.readC(); // EBS - ignore op.am=reader.readC(); - // TODO: don't ignore after I add KVS to Furnace - reader.readC(); // KVS - ignore + op.kvs=(reader.readC()>2)?1:0; op.tl=3+((99-reader.readC())*124)/99; unsigned char freq=reader.readC(); logV("OP%d freq: %d",i,freq); diff --git a/src/gui/sysPartNumber.cpp b/src/gui/sysPartNumber.cpp index 585520f7..8a9cd441 100644 --- a/src/gui/sysPartNumber.cpp +++ b/src/gui/sysPartNumber.cpp @@ -77,9 +77,6 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { } break; } - case DIV_SYSTEM_AMIGA: - return "Amiga"; - break; case DIV_SYSTEM_YM2151: return "YM2151"; break; @@ -170,10 +167,10 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { } case DIV_SYSTEM_OPL4: case DIV_SYSTEM_OPL4_DRUMS: - return "OPL4"; + return "YMF278"; break; case DIV_SYSTEM_MULTIPCM: - return "MultiPCM"; + return "YMW258-F"; break; case DIV_SYSTEM_RF5C68:{ int chipType=flags.getInt("chipType",0); @@ -220,9 +217,6 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { case DIV_SYSTEM_QSOUND: return "QSound"; break; - case DIV_SYSTEM_VERA: - return "VERA"; - break; case DIV_SYSTEM_X1_010: return "X1-010"; break; @@ -247,9 +241,6 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { case DIV_SYSTEM_YMZ280B: return "YMZ280B"; break; - case DIV_SYSTEM_NAMCO: - return "Namco WSG"; - break; case DIV_SYSTEM_NAMCO_15XX: return "C15"; break; @@ -259,9 +250,6 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { case DIV_SYSTEM_MSM5232: return "MSM5232"; break; - case DIV_SYSTEM_T6W28: - return "T6W28"; - break; case DIV_SYSTEM_K007232: return "K007232"; break; @@ -280,6 +268,9 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { case DIV_SYSTEM_K053260: return "K053260"; break; + case DIV_SYSTEM_TED: + return "TED"; + break; default: return FurnaceGUI::getSystemName(sys); break; diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index dce89bc9..3905b761 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -593,6 +593,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Duty"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -602,6 +603,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Exponent"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -611,6 +613,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("XOR Point"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -630,6 +633,7 @@ void FurnaceGUI::drawWaveEdit() { for (int i=0; i<16; i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("%d",i+1); ImGui::TableNextColumn(); ImGui::PushID(140+i); @@ -683,6 +687,7 @@ void FurnaceGUI::drawWaveEdit() { for (int i=0; i<4; i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("%d",i+1); ImGui::TableNextColumn(); @@ -727,6 +732,7 @@ void FurnaceGUI::drawWaveEdit() { for (int i=0; i<4; i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("%d",i+1); ImGui::TableNextColumn(); @@ -760,6 +766,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("1"); ImGui::TableNextColumn(); if (ImGui::Checkbox("##ConO1",&waveGenFMCon0[0])) { @@ -784,6 +791,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("2"); ImGui::TableNextColumn(); if (ImGui::Checkbox("##Con11",&waveGenFMCon1[0])) { @@ -808,6 +816,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("3"); ImGui::TableNextColumn(); if (ImGui::Checkbox("##Con21",&waveGenFMCon2[0])) { @@ -832,6 +841,7 @@ void FurnaceGUI::drawWaveEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("4"); ImGui::TableNextColumn(); if (ImGui::Checkbox("##Con31",&waveGenFMCon3[0])) { diff --git a/src/main.cpp b/src/main.cpp index b5484ea3..bf2ac269 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -191,6 +191,7 @@ TAParamResult pVersion(String) { printf("- MAME SegaPCM core by Hiromitsu Shioya and Olivier Galibert (BSD 3-clause)\n"); printf("- QSound core by superctr (BSD 3-clause)\n"); printf("- VICE VIC-20 by Rami Rasanen and viznut (GPLv2)\n"); + printf("- VICE TED by Andreas Boose, Tibor Biczo and Marco van den Heuvel (GPLv2)\n"); printf("- VERA core by Frank van den Hoef (BSD 2-clause)\n"); printf("- SAASound by Dave Hooper and Simon Owen (BSD 3-clause)\n"); printf("- SameBoy by Lior Halphon (MIT)\n");