diff --git a/CMakeLists.txt b/CMakeLists.txt index a4b2a922c..d02350b16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,6 +701,7 @@ src/engine/configEngine.cpp src/engine/dispatchContainer.cpp src/engine/engine.cpp src/engine/export.cpp +src/engine/exportDef.cpp src/engine/fileOpsIns.cpp src/engine/fileOpsSample.cpp src/engine/filter.cpp @@ -718,7 +719,6 @@ src/engine/wavOps.cpp src/engine/vgmOps.cpp src/engine/zsmOps.cpp src/engine/zsm.cpp -src/engine/tiunaOps.cpp src/engine/platform/abstract.cpp src/engine/platform/genesis.cpp @@ -796,6 +796,7 @@ src/engine/platform/dummy.cpp src/engine/export/abstract.cpp src/engine/export/amigaValidation.cpp +src/engine/export/tiuna.cpp src/engine/effect/abstract.cpp src/engine/effect/dummy.cpp diff --git a/demos/ay8910/AyMate.fur b/demos/ay8910/AyMate.fur new file mode 100644 index 000000000..c42647b78 Binary files /dev/null and b/demos/ay8910/AyMate.fur differ diff --git a/demos/gameboy/GB_WaitForMe.fur b/demos/gameboy/GB_WaitForMe.fur index 5e3c82141..499c627a2 100644 Binary files a/demos/gameboy/GB_WaitForMe.fur and b/demos/gameboy/GB_WaitForMe.fur differ diff --git a/demos/msx/OPLL_OperationPleaseLeaveLeave.fur b/demos/msx/OPLL_OperationPleaseLeaveLeave.fur index 390274532..a97c1f660 100644 Binary files a/demos/msx/OPLL_OperationPleaseLeaveLeave.fur and b/demos/msx/OPLL_OperationPleaseLeaveLeave.fur differ diff --git a/demos/multichip/TheOnlyWayIsForward.fur b/demos/multichip/TheOnlyWayIsForward.fur new file mode 100644 index 000000000..faca66ad2 Binary files /dev/null and b/demos/multichip/TheOnlyWayIsForward.fur differ diff --git a/demos/multichip/satellite_interconnects.fur b/demos/multichip/satellite_interconnects.fur new file mode 100644 index 000000000..b8516e4a4 Binary files /dev/null and b/demos/multichip/satellite_interconnects.fur differ diff --git a/demos/nes/Famitune1.fur b/demos/nes/Famitune1.fur new file mode 100644 index 000000000..d01f04961 Binary files /dev/null and b/demos/nes/Famitune1.fur differ diff --git a/doc/2-interface/README.md b/doc/2-interface/README.md index aa16af8d4..7d4300adf 100644 --- a/doc/2-interface/README.md +++ b/doc/2-interface/README.md @@ -18,30 +18,31 @@ the default layout of Furnace is depicted below. - [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) +- [effect list window](effect-list-window.md) - [instrument editor](../4-instrument/README.md) - [wavetable editor](../5-wave/README.md) - [sample editor](../6-sample/README.md) ## advanced topics -- [mixer](../8-advanced/mixer.md) -- [grooves](../8-advanced/grooves.md) -- [channels](../8-advanced/channels.md) -- [pattern manager](../8-advanced/pat-manager.md) -- [chip manager](../8-advanced/chip-manager.md) -- [compatibility flags](../8-advanced/compat-flags.md) - [song comments](../8-advanced/comments.md) -- [piano/input pad](../8-advanced/piano.md) +- [channels](../8-advanced/channels.md) +- [chip manager](../8-advanced/chip-manager.md) +- [pattern manager](../8-advanced/pat-manager.md) +- [mixer](../8-advanced/mixer.md) +- [compatibility flags](../8-advanced/compat-flags.md) - [oscilloscope](../8-advanced/osc.md) - [oscilloscope (per channel)](../8-advanced/chanosc.md) +- [oscilloscope (X-Y)](../8-advanced/xyosc.md) - [clock](../8-advanced/clock.md) -- [register view](../8-advanced/regview.md) +- [grooves](../8-advanced/grooves.md) - [log viewer](../8-advanced/log-viewer.md) +- [register view](../8-advanced/regview.md) - [statistics](../8-advanced/stats.md) +- [memory composition](../8-advanced/memory-composition.md) ## other topics -- [basic mode](basic-mode.md) +- [piano/input pad](../8-advanced/piano.md) - [settings](settings.md) diff --git a/doc/2-interface/export.md b/doc/2-interface/export.md index 8d6f32074..2b68a0a9a 100644 --- a/doc/2-interface/export.md +++ b/doc/2-interface/export.md @@ -2,25 +2,27 @@ Furnace allows you to export your song in several formats. this section deals with describing the available export options. -## export audio +## audio this option allows you to export your song in .wav format. I know I know, no .mp3 or .ogg export yet, but you can use a converter. -there are two parameters: - +- **Export type**: + - **one file**: exports your song to one .wav file. + - **multiple files (one per chip)**: exports the output of each chip to .wav files. + - **multiple files (one per channel)**: exports the output of each channel to .wav files. + - useful for usage with a channel visualizer such as corrscope. +- **Bit depth**: default is 16-bit integer. +- **Sample rate**: affects the quality of the output file. + - default is 44100, "CD quality". + - lower sample rates lose fidelity as upper frequencies disappear. + - higher sample rates gain frequencies that can't be heard at the cost of file size and rendering time. +- **Channels in file**: default is 2 (stereo). Set to 1 for mono. - **Loops**: sets the number of times the song will loop. - does not have effect if the song ends with `FFxx` effect. - **Fade out (seconds)**: sets the fade out time when the song is over. - does not have effect if the song ends with `FFxx` effect. -and three export choices: - -- **one file**: exports your song to one .wav file. -- **multiple files (one per chip)**: exports the output of each chip to .wav files. -- **multiple files (one per channel)**: exports the output of each channel to .wav files. - - useful for usage with a channel visualizer such as corrscope. - -## export VGM +## VGM this option allows exporting to a VGM (Video Game Music) file. these can be played back with VGMPlay (for example). @@ -40,9 +42,6 @@ the following settings exist: - **custom**: allows you to specify how many ticks to add. - `0` is effectively none, disabling loop trail completely. - this option will not appear if the loop modality isn't set to None as there wouldn't be a need to. -- **chips to export**: select which chips are going to be exported. - - due to VGM format limitations, you can only select up to two of each chip type. - - 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 @@ -51,14 +50,11 @@ the following settings exist: - `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. +- **chips to export**: select which chips are going to be exported. + - due to VGM format limitations, you can only select up to two of each chip type. + - some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version. -click on **click to export** to begin exporting. - -## export text - -this option allows you to export your song as a text file. - -## export ZSM +## ZSM ZSM (ZSound Music) is a format designed for the Commander X16 to allow hardware playback. it may contain data for either YM2151 or VERA chips. @@ -72,21 +68,21 @@ the following settings are available: - **loop**: enables loop. if disabled, the song won't loop. - **optimize size**: removes unnecessary commands to reduce size. -click on **Begin Export** to... you know. +## text -## export command stream +this option allows you to export your song as a text file. -this option exports a binary file which contains a dump of the internal command stream produced when playing the song. +## command stream -it's not really useful, unless you're a developer and want to use a command stream dump for some reason (e.g. writing a hardware sound driver). +this option exports a binary file in Furnace's own command stream format (FCS) which contains a dump of the internal command stream produced when playing the song. -- **export**: exports in Furnace's own command stream format (FCS). see `export-tech.md` in `papers/` for details. +it's not really useful, unless you're a developer and want to use a command stream dump for some reason (e.g. writing a hardware sound driver). see `export-tech.md` in `papers/` for details. -## export DMF +## DMF this option allows you to save your song as a .dmf which can be opened in DefleMask. -the following systems are supported when saving as 1.0/legacy: +the following systems are supported when saving as 1.0/legacy (0.12): - Sega Genesis/Mega Drive (YM2612 + SN76489) - Sega Genesis/Mega Drive (YM2612 + SN76489, extended channel 3) - Sega Master System @@ -97,7 +93,7 @@ the following systems are supported when saving as 1.0/legacy: - Arcade (YM2151 + SegaPCM 5-channel compatibility) - Neo Geo CD (DefleMask 1.0+) -the following systems are supported when saving as 1.1.3+: +the following systems are also supported when saving as 1.1.3+: - Sega Master System (with FM expansion) - NES + Konami VRC7 - Famicom Disk System diff --git a/doc/2-interface/keyboard.md b/doc/2-interface/keyboard.md index 317538567..675ba0b55 100644 --- a/doc/2-interface/keyboard.md +++ b/doc/2-interface/keyboard.md @@ -39,7 +39,7 @@ the keys in the "Global hotkeys" section can be used in any window, although not | Panic | `F12` | | | | | **Window activation** | | -| Find/Replace | Ctrl-F | +| Find/Replace | `Ctrl-F` | | Settings | — | | Song Information | — | | Subsongs | — | @@ -63,16 +63,23 @@ the keys in the "Global hotkeys" section can be used in any window, although not | Piano | — | | Oscilloscope (master) | — | | Oscilloscope (per-channel) | — | +| Oscilloscope (X-Y) | — | | Volume Meter | — | | Clock | — | | Register View | — | | Log Viewer | — | | Statistics | — | +| Memory Composition | — | | Effect List | — | | Debug Menu | `Ctrl-Shift-D` | +| Command Stream Player | — | | About | — | | Collapse/expand current window | — | | Close current window | `Shift-Escape` | +| Command Palette | `Ctrl-P` | +| Recent files (Palette) | — | +| Insstruments (Palette) | — | +| Samples (Palette) | — | | | | | **Note input** | | | _see "note input" section after table_ | | @@ -102,8 +109,8 @@ the keys in the "Global hotkeys" section can be used in any window, although not | Move cursor down by one (override Edit Step) | `Shift-End` | | Move cursor to previous channel | — | | Move cursor to next channel | — | -| Move cursor to next channel (overflow) | — | | Move cursor to previous channel (overflow) | — | +| Move cursor to next channel (overflow) | — | | Move cursor to beginning of pattern | `Home` | | Move cursor to end of pattern | `End` | | Move cursor up (coarse) | `PageUp` | @@ -118,6 +125,10 @@ the keys in the "Global hotkeys" section can be used in any window, although not | Expand selection to end of pattern | — | | Expand selection upwards (coarse) | `Shift-PageUp` | | Expand selection downwards (coarse) | `Shift-PageDown` | +| Move selection up by one | `Alt-Up` | +| Move selection down by one | `Alt-Down` | +| Move selection to previous channel | `Alt-Left` | +| Move selection to next channel | `Alt-Right` | | Delete | `Delete` | | Pull delete | `Backspace` | | Insert | `Insert` | @@ -143,102 +154,103 @@ the keys in the "Global hotkeys" section can be used in any window, although not | Clear note input latch | — | | | | | **Instrument list** | | -| Add | `Insert` | -| Duplicate | `Ctrl-D` | -| Open | — | -| Open (replace current) | — | -| Save | — | -| Save (.dmp) | — | -| Move up | `Shift-Up` | -| Move down | `Shift-Down` | -| Delete | — | -| Edit | `Shift-Return` | -| Cursor up | `Up` | -| Cursor down | `Down` | -| Toggle folders/standard view | `Ctrl-V` | +| Add instrument | `Insert` | +| Duplicate instrument | `Ctrl-D` | +| Open instrument | — | +| Open instrument (replace current) | — | +| Save instrument | — | +| Save instrument (.dmp) | — | +| Move instrument up in list | `Shift-Up` | +| Move instrument down in list | `Shift-Down` | +| Delete instrument | — | +| Edit instrument | `Shift-Return` | +| Instrument cursor up | `Up` | +| Instrument cursor down | `Down` | +| Instruments: toggle folders/standard view | `Ctrl-V` | | | | | **Wavetable list** | | -| Add | `Insert` | -| Duplicate | `Ctrl-D` | -| Open | — | -| Open (replace current) | — | -| Save | — | -| Save (.dmw) | — | -| Save (raw) | — | -| Move up | `Shift-Up` | -| Move down | `Shift-Down` | -| Delete | — | -| Edit | `Shift-Return` | -| Cursor up | `Up` | -| Cursor down | `Down` | -| Toggle folders/standard view | `Ctrl-V` | +| Add wavetable | `Insert` | +| Duplicate wavetable | `Ctrl-D` | +| Open wavetable | — | +| Open wavetable (replace current) | — | +| Save wavetable | — | +| Save wavetable (.dmw) | — | +| Save wavetable (raw) | — | +| Move wavetable up in list | `Shift-Up` | +| Move wavetable down in list | `Shift-Down` | +| Delete wavetable | — | +| Edit wavetable | `Shift-Return` | +| Wavetable cursor up | `Up` | +| Wavetable cursor down | `Down` | +| Wavetables: toggle folders/standard view | `Ctrl-V` | | | | | **Sample list** | | -| Add | `Insert` | -| Duplicate | `Ctrl-D` | -| Create wavetable from selection | `Ctrl-W` | -| Open | — | -| Open (replace current) | — | -| Import raw data | — | -| Import raw data (replace current) | — | -| Save | — | -| Save (raw) | — | -| Move up | `Shift-Up` | -| Move down | `Shift-Down` | -| Delete | — | -| Edit | `Shift-Return` | -| Cursor up | `Up` | -| Cursor down | `Down` | -| Preview | — | -| Stop preview | — | -| Toggle folders/standard view | `Ctrl-V` | +| Add sample | `Insert` | +| Duplicate sample | `Ctrl-D` | +| Sample Editor: Create wavetable from selection | `Ctrl-W` | +| Open sample | — | +| Open sample (replace current) | — | +| Import raw sample data | — | +| Import raw sample data (replace current) | — | +| Save sample | — | +| Save sample (raw) | — | +| Move sample up in list | `Shift-Up` | +| Move sample down in list | `Shift-Down` | +| Delete sample | — | +| Edit sample | `Shift-Return` | +| Sample cursor up | `Up` | +| Sample cursor down | `Down` | +| Sample Preview | — | +| Stop sample preview | — | +| Samples: Toggle folders/standard view | `Ctrl-V` | +| Samples: Make me a drum kit | — | | | | | **Orders** | | | Previous order | `Up` | | Next order | `Down` | -| Cursor left | `Left` | -| Cursor right | `Right` | -| Increase value | — | -| Decrease value | — | -| Switch edit mode | — | -| Toggle alter entire row | `Ctrl-L` | -| Add | `Insert` | -| Duplicate | `Ctrl-D` | -| Deep clone | `Ctrl-Shift-D` | -| Duplicate to end of song | `Ctrl-E` | -| Deep clone to end of song | `Ctrl-Shift-E` | -| Remove | `Delete` | -| Move up | `Shift-Up` | -| Move down | `Shift-Down` | -| Replay | — | +| Order cursor left | `Left` | +| Order cursor right | `Right` | +| Increase order value | — | +| Decrease order value | — | +| Switch order edit mode | — | +| Order: Toggle alter entire row | `Ctrl-L` | +| Add order | `Insert` | +| Duplicate order | `Ctrl-D` | +| Deep clone order | `Ctrl-Shift-D` | +| Copy current order to end of song | `Ctrl-E` | +| Deep clone current order to end of song | `Ctrl-Shift-E` | +| Remove order | `Delete` | +| Move order up | `Shift-Up` | +| Move order down | `Shift-Down` | +| Replay order | — | | | | | **Sample editor** | | -| Edit mode: Select | `Shift-I` | -| Edit mode: Draw | `Shift-D` | -| Cut | `Ctrl-X` | -| Copy | `Ctrl-C` | -| Paste | `Ctrl-V` | -| Paste replace | `Ctrl-Shift-V` | -| Paste mix | `Ctrl-Alt-V` | -| Select all | `Ctrl-A` | -| Resize | `Ctrl-R` | -| Resample | `Ctrl-E` | -| Amplify | `Ctrl-B` | -| Normalize | `Ctrl-N` | -| Fade in | `Ctrl-I` | -| Fade out | `Ctrl-O` | -| Insert silence | `Insert` | -| Apply silence | `Shift-Delete` | -| Delete | `Delete` | -| Trim | `Ctrl-Delete` | -| Reverse | `Ctrl-T` | -| Invert | `Ctrl-Shift-T` | -| Signed/unsigned exchange | `Ctrl-U` | -| Apply filter | `Ctrl-F` | -| Preview sample | — | -| Stop sample preview | — | -| Zoom in | `Ctrl-=` | -| Zoom out | `Ctrl--` | -| Toggle auto-zoom | `Ctrl-0` | -| Create instrument from sample | — | -| Set loop to selection | `Ctrl-L` | +| Sample editor mode: Select | `Shift-I` | +| Sample editor mode: Draw | `Shift-D` | +| Sample editor: Cut | `Ctrl-X` | +| Sample editor: Copy | `Ctrl-C` | +| Sample editor: Paste | `Ctrl-V` | +| Sample editor: Paste replace | `Ctrl-Shift-V` | +| Sample editor: Paste mix | `Ctrl-Alt-V` | +| Sample editor: Select all | `Ctrl-A` | +| Sample editor: Resize | `Ctrl-R` | +| Sample editor: Resample | `Ctrl-E` | +| Sample editor: Amplify | `Ctrl-B` | +| Sample editor: Normalize | `Ctrl-N` | +| Sample editor: Fade in | `Ctrl-I` | +| Sample editor: Fade out | `Ctrl-O` | +| Sample editor: Insert silence | `Insert` | +| Sample editor: Apply silence | `Shift-Delete` | +| Sample editor: Delete | `Delete` | +| Sample editor: Trim | `Ctrl-Delete` | +| Sample editor: Reverse | `Ctrl-T` | +| Sample editor: Invert | `Ctrl-Shift-T` | +| Sample editor: Signed/unsigned exchange | `Ctrl-U` | +| Sample editor: Apply filter | `Ctrl-F` | +| Sample editor: Preview sample | — | +| Sample editor: Stop sample preview | — | +| Sample editor: Zoom in | `Ctrl-=` | +| Sample editor: Zoom out | `Ctrl--` | +| Sample editor: Toggle auto-zoom | `Ctrl-0` | +| Sample editor: Create instrument from sample | — | +| Sample editor: Set loop to selection | `Ctrl-L` | diff --git a/doc/2-interface/menu-bar.md b/doc/2-interface/menu-bar.md index ec3c6b1bf..fff10825b 100644 --- a/doc/2-interface/menu-bar.md +++ b/doc/2-interface/menu-bar.md @@ -2,24 +2,20 @@ the menu bar allows you to select from five menus: file, edit, settings, window and help. -items in _italic_ don't appear in basic mode and are only available in advanced mode. - ## file -- **new...**: creates a new song. +- **new...**: opens the new song dialog to choose a system. + - click a system name to create a new song with it. + - some systems have several variants, which are inside a group. - **open...**: opens the file picker, allowing you to select a song to open. - see [file formats](formats.md) for a list of formats Furnace is able to open. - **open recent**: contains a list of the songs you've opened before. - **clear history**: erases the file history. - - **save**: saves the current song. - opens the file picker if this is a new song, or a backup. - **save as...**: opens the file picker, allowing you to save the song under a different name. - -- **export**: allows you to export your song into other formats, such as audio files, VGM and more. see the [export](export.md) page for more information. - +- **export...**: allows you to export your song into other formats, such as audio files, VGM and more. see the [export](export.md) page for more information. - **manage chips**: opens the [Chip Manager](../8-advanced/chip-manager.md) dialog. - - **restore backup**: restores a previously saved backup. - Furnace keeps up to 5 backups of a song. - the backup directory is located in: @@ -28,21 +24,18 @@ items in _italic_ don't appear in basic mode and are only available in advanced - Linux/other: `~/.config/furnace/backups` - this directory grows in size as you use Furnace. remember to delete old backups periodically to save space. - **do NOT rely on the backup system as auto-save!** you should save a restored backup because Furnace will not save backups of backups. - - **exit**: closes Furnace. ## edit - **...**: does nothing except prevent accidental clicks on later menu items if the menu is too tall to fit on the program window. - - **undo**: reverts the last action. - **redo**: repeats what you undid previously. - - **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. - you may be able to paste from OpenMPT as well. -- _**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. @@ -55,81 +48,84 @@ items in _italic_ don't appear in basic mode and are only available in advanced - if the selection is tall, it will select the entire column. - 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. - - **values up/down**: changes values in the current selection by ±1 or ±16. - - **transpose**: transpose notes or change values by a specific amount. - - **interpolate**: fills in gaps in the selection by interpolation between values. -- **change instrument**: changes the instrument number in a selection. -- **gradient/fade**: replace the selection with a "gradient" that goes from the beginning of the selection to the end. +- **change instrument...**: changes the instrument number in a selection. +- **gradient/fade...**: replace the selection with a "gradient" that goes from the beginning of the selection to the end. - does not affect the note column. - **Nibble mode**: when enabled, the fade will be per-nibble (0 to F) rather than per-value (00 to FF). - use for effects like `04xy` (vibrato). -- **scale**: scales values in the selection by a specific amount. +- **scale...**: scales values in the selection by a specific amount. - use to change volume in a selection for example. - **randomize**: replaces the selection with random values. - does not affect the note column. + - **Nibble mode**: when enabled, the randomization will be per-nibble (0 to F) rather than per-value (00 to FF). - **invert values**: `00` becomes `FF`, `01` becomes `FE`, `02` becomes `FD` and so on. - - **flip selection**: flips the selection so it is backwards. - **collapse/expand amount**: allows you to specify how much to collapse/expand in the next two menu items. - **collapse**: shrinks the selected contents. - **expand**: expands the selected contents. - - **collapse pattern**: same as collapse, but affects the entire pattern. - **expand pattern**: same as expand, but affects the entire pattern. - - **collapse song**: same as collapse, but affects the entire song. - it also changes speeds and pattern length to compensate. - **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). - -- **clear**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like. +- **find/replace**: shows [the Find/Replace window](../8-advanced/find-replace.md). +- **clear...**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like. ## settings - **full screen**: expands the Furnace window so it covers your screen. - **lock layout**: prevents you from dragging/resizing docked windows, or docking more. -- **basic mode**: toggles [Basic Mode](basic-mode.md). -- **visualizer**: toggles pattern view particle effects when the song plays. +- **pattern visualizer**: toggles pattern view particle effects when the song plays. - **reset layout**: resets the workspace to its defaults. -- **settings...**: shows the Settings window. these are detailed in [settings.md]. +- **user systems...**: shows the User Systems window. this is detailed in [the User Systems documentation](../8-advanced/user-systems.md). +- **settings...**: shows the Settings window. these are detailed in [the Settings documentation](settings.md). ## window all these menu items show or hide their associated windows. -- [song information](song-info.md) -- [subsongs](song-info.md) -- [speed](song-info.md) -- [instruments](../4-instrument/README.md) -- [wavetables](../5-wave/README.md) -- [samples](../6-sample/README.md) -- [orders](order-list.md) -- [pattern](../3-pattern/README.md) -- _[mixer](../8-advanced/mixer.md)_ -- _[grooves](../8-advanced/grooves.md)_ -- _[channels](../8-advanced/channels.md)_ -- _[pattern manager](../8-advanced/pat-manager.md)_ -- _[chip manager](../8-advanced/chip-manager.md)_ -- _[compatibility flags](../8-advanced/compat-flags.md)_ -- [song comments](../8-advanced/comments.md) - -- [piano](../8-advanced/piano.md) -- [oscilloscope](../8-advanced/osc.md) -- [oscilloscopes (per-channel)](../8-advanced/chanosc.md) -- [clock](../8-advanced/clock.md) -- [register view](../8-advanced/regview.md) -- [log viewer](../8-advanced/log-viewer.md) -- [stats](../8-advanced/stats.md) +- song + - **[song comments](../8-advanced/comments.md)** + - **[song information](song-info.md)** + - **[subsongs](song-info.md)** + - **[channels](../8-advanced/channels.md)** + - **[chip manager](../8-advanced/chip-manager.md)** + - **[orders](order-list.md)** + - **[pattern](../3-pattern/README.md)** + - **[pattern manager](../8-advanced/pat-manager.md)** + - **[mixer](../8-advanced/mixer.md)** + - **[compatibility flags](../8-advanced/compat-flags.md)** +- assets + - **[instruments](../4-instrument/README.md)** + - **[samples](../6-sample/README.md)** + - **[wavetables](../5-wave/README.md)** + - **[instrument editor](../4-instrument/README.md)** + - **[sample editor](../6-sample/README.md)** + - **[wavetable editor](../5-wave/README.md)** +- visualizers + - **[oscilloscope](../8-advanced/osc.md)** + - **[oscilloscope (per-channel)](../8-advanced/chanosc.md)** + - **[oscilloscope (X-Y)](../8-advanced/xyosc.md)** + - volume meter +- tempo + - **[clock](../8-advanced/clock.md)** + - **[grooves](../8-advanced/grooves.md)** + - **[speed](song-info.md)** +- debug + - **[log viewer](../8-advanced/log-viewer.md)** + - **[register view](../8-advanced/regview.md)** + - **[statistics](../8-advanced/stats.md)** + - **[memory composition](../8-advanced/memory-composition.md)** +- **[effect list](../3-pattern/effects.md)** +- **[play/edit controls](play-edit-controls.md)** +- **[piano/input pad](../8-advanced/piano.md)** ## help diff --git a/doc/2-interface/play-edit-controls.md b/doc/2-interface/play-edit-controls.md index d7814ac25..47e4f103b 100644 --- a/doc/2-interface/play-edit-controls.md +++ b/doc/2-interface/play-edit-controls.md @@ -13,6 +13,7 @@ the "Play/Edit Controls" are used to control playback and change parameters of t - **Poly**: turns on polyphony for previewing notes. toggles to **Mono** for monophony (one note at a time only). - **Octave**: sets current input octave. - **Step**: sets edit step. if this is 1, entering a note or effect will move to the next row. if this is a larger number, rows will be skipped. if this is 0, the cursor will stay in place. + - if clicked, Step becomes **Coarse**, which sets the number of rows moved with `PgUp`, `PgDn`, and related movement shortcuts. clicking again will revert it to Step. - **Follow orders**: if on, the selected order in the orders window will follow the song during playback. - **Follow pattern**: if on, the cursor will follow playback and the song will scroll by as it plays. diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index ec61e9371..cfe12c949 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -1,25 +1,31 @@ # settings -the Settings window allows you to change Furnace setting. +the Settings window allows you to change Furnace settings. + +settings are saved when clicking the **OK** or **Apply** buttons at the bottom of the window, and when closing the program. several backups are kept in the Furnace settings directory. -settings are saved when clicking the **OK** or **Apply** buttons at the bottom of the window. ## General ### Program +- **Language**: select the language used for the interface. some languages are incomplete, and are listed with their approximate completion percentage. - **Render backend**: changing this may help with performace or compatibility issues. the available render backends are: + - SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS. + - it is slower than the other backends. + - DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows. + - DirectX 9: use if your hardware is incompatible with DirectX 11. - OpenGL 3.0: works with the majority of graphics chips/cards (from 2010 onwards). default on Linux. - OpenGL 2.0: use if you have a card without OpenGL 3.0 support. - OpenGL 1.1: use if your card doesn't even support OpenGL 2.0. - - DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows. - - SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS. - - it is slower than the other backends. - Software: this is a last resort backend which renders the interface in software. very slow! -- **Render driver**: this setting appears when using the SDL Renderer backend. it allows you to select an SDL render driver. +- **Advanced render backend settings**: only applicable with some render backends. + - **Render driver**: this setting only appears when using the SDL Renderer backend. it allows you to select an SDL render driver. + - OpenGL settings: these only appear when using an OpenGL backend, and should only be adjusted if the display is incorrect. - **VSync**: synchronizes rendering to VBlank and eliminates tearing. - **Frame rate limit**: allows you to set a frame rate limit (in frames per second). - only has effect when VSync is off or not available (e.g. software rendering or force-disabled on driver settings). +- **Display render time**: displays frame rate and frame render time at the right side of the menu bar. - **Late render clear**: this option is only useful when using old versions of Mesa drivers. it force-waits for VBlank by clearing after present, reducing latency. - **Power-saving mode**: saves power by lowering the frame rate to 2fps when idle. - may cause issues under Mesa drivers! @@ -49,6 +55,9 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Remember last values** - **Store instrument name in .fui**: when enabled, saving an instrument will store its name. this may increase file size. - **Load instrument name from .fui**: when enabled, loading an instrument will use the stored name (if present). otherwise, it will use the file name. +- **Auto-fill file name when saving**: pre-fill the file name field when saving or exporting. + - when saving a module, the existing file name will be auto-filled. + - when saving an instrument or sample, its name will be auto-filled. ### New Song @@ -79,6 +88,11 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **New instruments are blank**: when enabled, adding FM instruments will make them blank (rather than loading the default one). +### Configuration +- **Import**: select an exported `.ini` config file to overwrite current settings. +- **Export**: select an `.ini` file to save current settings. +- **Factory Reset**: resets all settings to default and purges settings backups. + ## Audio ### Output @@ -100,9 +114,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - **Sample rate**: audio output rate. - a lower rate decreases quality and isn't really beneficial. - if using PortAudio backend, be careful about this value. -- **Outputs**: number of audio outputs created, up to 16. - - only appears when Backend is JACK. -- **Channels**: mono, stereo or something. +- **Outputs**: number of audio outputs created, up to 16. default is 2 (stereo). - **Buffer size**: size of buffer in both samples and milliseconds. - setting this to a low value may cause stuttering/glitches in playback (known as "underruns" or "xruns"). - setting this to a high value increases latency. @@ -135,6 +147,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o ### MIDI input - **MIDI input**: input device. + - **Rescan MIDI devices**: repopulates list with all currently connected MIDI devices. useful if a device is connected while Furnace is running. - **Note input**: enables note input. disable if you intend to use this device only for binding actions. - **Velocity input**: enables velocity input when entering notes in the pattern. - **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position. @@ -291,6 +304,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **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. for example: - 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`. +- **Keyboard note/value input repeat (hold key to input continuously)** - **Effect input behavior:** - **Move down**: after entering an effect (or effect value), the cursor moves down. - **Move to effect value (otherwise move down)**: after entering an effect, the cursor moves to its value. if entering a value, the cursor moves down. @@ -359,11 +373,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **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** and **Display Korean characters**: only toggle these options if you have enough graphics memory. - - these are a temporary solution until dynamic font atlas is implemented in Dear ImGui. - #### FreeType-specific settings - - **Anti-aliased fonts**: when enabled, fonts will be rendered smooth. - **Support bitmap fonts**: this option allows you to enable the loading of bitmap fonts. - be noted that this may force non-bitmap fonts to undesired sizes! @@ -376,6 +386,13 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Disable**: only rely upon font hinting data. - **Enable**: prefer font hinting data if present. - **Force**: ignore font hinting data. +#### non-specific settings +- **Oversample**: renders the font internally at higher resolution for visual quality. + - higher settings use more video memory. + - for pixel or bitmap fonts, set this to **1x**. +- **Load fallback font**: load an extra font that contains nearly all characters that can be used, in case the selected fonts lack them. uses much video memory +- **Display Japanese characters**, **Display Chinese (Simplified) characters**, **Display Chinese (Traditional) characters** and **Display Korean characters**: only toggle these options if you have enough graphics memory. + - these are a temporary solution until dynamic font atlas is implemented in Dear ImGui. ### Program @@ -386,15 +403,16 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **/path/to/file.fur - Furnace** - **Display system name on title bar** - **Display chip names instead of "multi-system" in title bar** -- **Export options layout:** - - **Sub-menus in File menu**: export options appear in the File menu as sub-menus. - - **Modal window with tabs**: a single "export..." option that opens a dialog with export options. this is the default. - - **Modal windows with options in File menu**: like Sub-menus in File menu, but instead of being sub-menus, selecting one opens a dialog with export settings. - **Status bar:** - **Cursor details** - **File path** - **Cursor details or file path** - **Nothing** +- **Display playback status when playing**: display playback time and current location in the menu bar. +- **Export options layout:** + - **Sub-menus in File menu**: export options appear in the File menu as sub-menus. + - **Modal window with tabs**: a single "export..." option that opens a dialog with export options. this is the default. + - **Modal windows with options in File menu**: like Sub-menus in File menu, but instead of being sub-menus, selecting one opens a dialog with export settings. - **Capitalize menu bar** - **Display add/configure/change/remove chip menus in File menu**: if enabled, the "manage chips" item in the file menu is split into the four listed items for quick access. @@ -532,10 +550,10 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Rounded window corners** - **Rounded buttons** - **Rounded menu corners** +- **Rounded tabs** +- **Rounded scrollbars** - **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like. - - ## Color ### Color scheme @@ -544,9 +562,28 @@ below all the binds, select a key from the dropdown list to add it. it will appe - **Export** - **Reset defaults** - **Guru mode**: exposes all color options (instead of accent colors). -- **General** +- **Interface** + - **Frame shading**: applies a gradient effect to buttons and input boxes. - **Color scheme type:** - **Dark** - **Light** - - **Frame shading**: applies a gradient effect to buttons and input boxes. + - **Accent colors**: select main interface colors. + - **Primary** + - **Secondary** - several more categories... + +## Backup + +### Configuration + +- **Enable backup system**: turn on automatic backups of the current open file. +- **Interval (in seconds)**: time between automatic backups. +- **Backups per file**: maximum number of backups to store for each file. oldest backups are deleted first. + +### Backup Management + +- **Purge before**: + - **Go**: purge all backups from before the selected date. +- total space used by all backups: + - **Refresh**: recalculate space. + - **Delete All**: purge all backups. diff --git a/doc/2-interface/song-info.md b/doc/2-interface/song-info.md index 24a42f95e..f9773807c 100644 --- a/doc/2-interface/song-info.md +++ b/doc/2-interface/song-info.md @@ -3,11 +3,11 @@ - **Name**: the track's title. - **Author**: the author(s) of this track. - **Album**: the associated album name (or the name of the game the song is from). -- **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. +- **System**: the name of the game console or computer the track is designed for. this is automatically set when creating a new tune, but can be changed to anything. 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 an audio 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. available only in advanced mode. +- **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. ## subsongs @@ -23,16 +23,17 @@ this window allows one to create **subsongs** - multiple individual songs within there are multiple ways to set the tempo of a song. -items in _italic_ don't appear in basic mode and are only available in advanced mode. +**Base Tempo**: tempo in beats per minute (BPM). this is affected by the Highlight settings below. +- clicking the Base Tempo button switches to the more technical Tick Rate. **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. +- clicking the Tick Rate button switches to the more traditional Base Tempo BPM setting. **Speed**: the number of ticks per row. - clicking the "Speed" button changes to more complex modes covered in the [grooves](../8-advanced/grooves.md) 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. @@ -40,7 +41,8 @@ _**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rat - 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. - another way to accomplish this with more control over the results is to use grooves. see the page on [grooves](../8-advanced/grooves.md) for details. -_**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! +- to the right, the effective BPM is listed, taking all settings into account. **Highlight**: sets the pattern row highlights: - the first value represents the number of rows per beat. @@ -50,4 +52,4 @@ _**Divider**:_ Changes the effective tick rate. a tick rate of 60Hz and a diviso **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/3-pattern/README.md b/doc/3-pattern/README.md index 31713ea64..77060b5f2 100644 --- a/doc/3-pattern/README.md +++ b/doc/3-pattern/README.md @@ -119,6 +119,10 @@ Shift-Up | expand selection upwards Shift-Down | expand selection downwards Shift-Left | expand selection to the left Shift-Right | expand selection to the right +Alt-Up | move selection up by one +Alt-Down | move selection down by one +Alt-Left | move selection to previous channel +Alt-Right | move selection to next channel Backspace | delete note at cursor and/or pull pattern upwards (configurable) Delete | delete selection Insert | create blank row at cursor position and push pattern diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index 2e870c95d..5ebe571c1 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -89,6 +89,7 @@ in there, you can modify certain data pertaining to your sample, such as the: - **Open**: replaces current sample. - right-clicking brings up a menu: - **import raw...**: brings up a file selector, then presents a dialog to choose the format of the selected file. + - **import raw (replace)...**: same as above, but instead of adding it to the sample list, it replaces the currently selected sample. - **Save**: saves current sample to disk. - right-clicking brings up a menu: - **save raw...**: brings up a file selector, then saves the sample as raw data. diff --git a/doc/7-systems/gba.md b/doc/7-systems/gba.md index 692d8db0d..a0653a5fe 100644 --- a/doc/7-systems/gba.md +++ b/doc/7-systems/gba.md @@ -15,11 +15,11 @@ it features echo and up to 16 voices. - `10xx`: **change wave.** - `11xy`: **configure echo.** - - this effect is kinda odd. this is how it works: - -> How do you echo on GBA -> -> Create an empty instrment and put a very high note of it in channel 1 then do 110x in effect column and set volume column to set feedback and do nothing else on it + - this effect is kinda odd. here's how to use it: + - create an empty instrument and put a very high note of it in channel 1. + - put `110x` in the effect column. + - set volume column to set feedback. + - don't use the channel for anything else. - `12xy`: **toggle invert.** - `x` left channel. diff --git a/doc/7-systems/snes.md b/doc/7-systems/snes.md index 7b39ed87f..978af2106 100644 --- a/doc/7-systems/snes.md +++ b/doc/7-systems/snes.md @@ -59,7 +59,7 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i - `00` to `7F` for 0 to 127. - `80` to `FF` for -128 to -1. - 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. + - see SnesLab for [echo filter explanations and examples](https://sneslab.net/wiki/FIR_Filter#Uses). ## info @@ -67,6 +67,8 @@ this chip uses the [SNES](../4-instrument/snes.md) instrument editor. when two channels are joined for pitch modulation, the channel bar will show `mod` on a bracket tying them together. +when using sample offset commands, be sure to open each involved sample in the sample editor, look to the "Info" section at the top-left, and check the "no BRR filters" box. this prevents sound glitches, at the cost of lowering the sample quality to 4-bit. + ## channel status the following icons are displayed when channel status is enabled in the pattern view: @@ -93,6 +95,8 @@ the following options are available in the Chip Manager window: - **Feedback**: sets how much of the echo output will be fed back into the buffer. - **Echo volume**: sets echo volume. - **Echo filter**: adjusts echo filter. +- **Dec/Hex**: toggles decimal or hexadecimal mode for the filter settings text entry box to the right. + - SnesLab provides [echo filter explanations and examples](https://sneslab.net/wiki/FIR_Filter#Uses). their example filter strings can be pasted directly into the filter settings text entry box if set to Hex mode. ## ADSR diff --git a/doc/8-advanced/README.md b/doc/8-advanced/README.md index 4a9512823..759737e0e 100644 --- a/doc/8-advanced/README.md +++ b/doc/8-advanced/README.md @@ -8,22 +8,27 @@ as listed in the "Edit" menu: as listed in the "Window" menu: -- [mixer](mixer.md) -- [grooves](grooves.md) -- [channel manager](channels.md) -- [pattern manager](pat-manager.md) -- [chip manager](chip-manager.md) -- [compatibility flags](compat-flags.md) -- [song comments](comments.md) - -- [piano](piano.md) -- [oscilloscope](osc.md) -- [oscilloscope (X-Y)](xyosc.md) -- [oscilloscopes (per-channel)](chanosc.md) -- [clock](clock.md) -- [register view](regview.md) -- [log viewer](log-viewer.md) -- [stats](stats.md) +- song + - [song comments](../8-advanced/comments.md) + - [channels](../8-advanced/channels.md) + - [chip manager](../8-advanced/chip-manager.md) + - [pattern manager](../8-advanced/pat-manager.md) + - [mixer](../8-advanced/mixer.md) + - [compatibility flags](../8-advanced/compat-flags.md) +- visualizers + - [oscilloscope](../8-advanced/osc.md) + - [oscilloscope (per-channel)](../8-advanced/chanosc.md) + - [oscilloscope (X-Y)](../8-advanced/xyosc.md) + - volume meter +- tempo + - [clock](../8-advanced/clock.md) + - [grooves](../8-advanced/grooves.md) +- debug + - [log viewer](../8-advanced/log-viewer.md) + - [register view](../8-advanced/regview.md) + - [statistics](../8-advanced/stats.md) + - [memory composition](../8-advanced/memory-composition.md) +- [piano/input pad](../8-advanced/piano.md) other: diff --git a/doc/8-advanced/channels.md b/doc/8-advanced/channels.md index 5b70f00dd..53bc00065 100644 --- a/doc/8-advanced/channels.md +++ b/doc/8-advanced/channels.md @@ -5,8 +5,9 @@ the "Channels" dialog allows manipulation of the song's channels. ![channels dialog](channels.png) each channel has the following options: -- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept. -- crossed-arrows button: click and drag to rearrange pattern data throughout the song. +- **Pat**: uncheck the box to hide the channel from the pattern view. pattern data will be kept. +- **Osc**: uncheck the box to hide the channel from the per-channel oscilloscope view. +- **Swap**: click and drag to rearrange pattern data throughout the song. - note: this does **not** move channels around! it only moves the channel's pattern data. - **Name**: the name displayed at the top of each channel in the pattern view. - the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed. diff --git a/doc/8-advanced/chanosc.md b/doc/8-advanced/chanosc.md index 6a2cd27ed..54163dfb3 100644 --- a/doc/8-advanced/chanosc.md +++ b/doc/8-advanced/chanosc.md @@ -14,6 +14,7 @@ right-clicking the view will display the configuration view shown above: - **Mode 2**: bias slightly toward more columns. - **Mode 3**: always more columns than rows. - **Amplitude**: scales amplitude for all oscilloscope views. +- **Line size**: controls line thickness. - **Gradient**: this allows you to use a gradient for determining the waveforms' colors instead of a single color. see the gradient section for more information. - if this option is off, a color selector will be displayed. right-click on it for some options: - select between the square selector and the color wheel selector. diff --git a/doc/8-advanced/chip-manager.md b/doc/8-advanced/chip-manager.md index 41a187fd9..09388e802 100644 --- a/doc/8-advanced/chip-manager.md +++ b/doc/8-advanced/chip-manager.md @@ -8,6 +8,8 @@ the **chip manager** window allows you to manage chips, including adding, changi **Clone channel data**: when cloning chips, also copy patterns, pattern names, channel names and other parameters to the clone. +**Clone at end**: instead of inserting the clone directly after the cloned chip, add it to the end. + to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to the left. to duplicate a chip, click the **Clone** button. diff --git a/doc/8-advanced/user-systems.md b/doc/8-advanced/user-systems.md new file mode 100644 index 000000000..d60772654 --- /dev/null +++ b/doc/8-advanced/user-systems.md @@ -0,0 +1,22 @@ +# user systems + +combinations of chips and chip configurations can be stored as **user systems** – presets that are easily accessed when starting a new song. + +![user systems window](user-systems.png) + +the `+` button at the top of the **Systems** list will add a new system. + +next to the **Name** field, the **Remove** button removes the current system from the list. + +chip configuration is exactly as in the [chip manager](chip-manager.md) window. + +the **Advanced** field stores additional settings that are set when a new song is started. these are listed in "option=value" format, one per line. +- `tickRate`: sets tick rate. + +**Save and Close**: as it says. + +**Import**: opens a dialog to select a `.cfgu` file, then adds its systems to the list. + +**Import (replace)**: opens a similar dialog, then clears the existing systems list and replaces it with the imported one. + +**Export**: stores the current list of systems in a selected `.cfgu` file. \ No newline at end of file diff --git a/doc/8-advanced/user-systems.png b/doc/8-advanced/user-systems.png new file mode 100644 index 000000000..c300530a3 Binary files /dev/null and b/doc/8-advanced/user-systems.png differ diff --git a/instruments/OPM/Bowed Lead 1.dmp b/instruments/OPM/Bowed Lead 1.dmp new file mode 100644 index 000000000..5e485475a Binary files /dev/null and b/instruments/OPM/Bowed Lead 1.dmp differ diff --git a/instruments/OPM/Brass 4.dmp b/instruments/OPM/Brass 4.dmp new file mode 100644 index 000000000..3509dba50 Binary files /dev/null and b/instruments/OPM/Brass 4.dmp differ diff --git a/instruments/OPM/Brass 5.dmp b/instruments/OPM/Brass 5.dmp new file mode 100644 index 000000000..d062d650e Binary files /dev/null and b/instruments/OPM/Brass 5.dmp differ diff --git a/instruments/OPM/Brass 6.dmp b/instruments/OPM/Brass 6.dmp new file mode 100644 index 000000000..045023bdb Binary files /dev/null and b/instruments/OPM/Brass 6.dmp differ diff --git a/instruments/OPM/Brass 7.dmp b/instruments/OPM/Brass 7.dmp new file mode 100644 index 000000000..26cea36ea Binary files /dev/null and b/instruments/OPM/Brass 7.dmp differ diff --git a/instruments/OPM/Brass 8.dmp b/instruments/OPM/Brass 8.dmp new file mode 100644 index 000000000..55925a13b Binary files /dev/null and b/instruments/OPM/Brass 8.dmp differ diff --git a/instruments/OPM/Dist 2.dmp b/instruments/OPM/Dist 2.dmp new file mode 100644 index 000000000..3b32c9993 Binary files /dev/null and b/instruments/OPM/Dist 2.dmp differ diff --git a/instruments/OPM/Dist Bass 2.dmp b/instruments/OPM/Dist Bass 2.dmp new file mode 100644 index 000000000..93a97392c Binary files /dev/null and b/instruments/OPM/Dist Bass 2.dmp differ diff --git a/instruments/OPM/Elec Bass 1.dmp b/instruments/OPM/Elec Bass 1.dmp new file mode 100644 index 000000000..74357feb7 Binary files /dev/null and b/instruments/OPM/Elec Bass 1.dmp differ diff --git a/instruments/OPM/Electric Piano 2.dmp b/instruments/OPM/Electric Piano 2.dmp new file mode 100644 index 000000000..169689818 Binary files /dev/null and b/instruments/OPM/Electric Piano 2.dmp differ diff --git a/instruments/OPM/FlangerStrings 1.dmp b/instruments/OPM/FlangerStrings 1.dmp new file mode 100644 index 000000000..334caeff2 Binary files /dev/null and b/instruments/OPM/FlangerStrings 1.dmp differ diff --git a/instruments/OPM/FlangerStrings 2.dmp b/instruments/OPM/FlangerStrings 2.dmp new file mode 100644 index 000000000..7425f9ee6 Binary files /dev/null and b/instruments/OPM/FlangerStrings 2.dmp differ diff --git a/instruments/OPM/FlangerStrings 3.dmp b/instruments/OPM/FlangerStrings 3.dmp new file mode 100644 index 000000000..9becf2f8d Binary files /dev/null and b/instruments/OPM/FlangerStrings 3.dmp differ diff --git a/instruments/OPM/Flute 2.dmp b/instruments/OPM/Flute 2.dmp new file mode 100644 index 000000000..97a711fd4 Binary files /dev/null and b/instruments/OPM/Flute 2.dmp differ diff --git a/instruments/OPM/Flute.dmp b/instruments/OPM/Flute.dmp new file mode 100644 index 000000000..ade7fa7e5 Binary files /dev/null and b/instruments/OPM/Flute.dmp differ diff --git a/instruments/OPM/HammeredMetalLd1.dmp b/instruments/OPM/HammeredMetalLd1.dmp new file mode 100644 index 000000000..172a27fd0 Binary files /dev/null and b/instruments/OPM/HammeredMetalLd1.dmp differ diff --git a/instruments/OPM/Impact Synth 4.dmp b/instruments/OPM/Impact Synth 4.dmp new file mode 100644 index 000000000..dbbb86c63 Binary files /dev/null and b/instruments/OPM/Impact Synth 4.dmp differ diff --git a/instruments/OPM/Liquid Lead 3.dmp b/instruments/OPM/Liquid Lead 3.dmp new file mode 100644 index 000000000..a98e80ee6 Binary files /dev/null and b/instruments/OPM/Liquid Lead 3.dmp differ diff --git a/instruments/OPM/Mallet 2.dmp b/instruments/OPM/Mallet 2.dmp new file mode 100644 index 000000000..9299fe1d5 Binary files /dev/null and b/instruments/OPM/Mallet 2.dmp differ diff --git a/instruments/OPM/Mallet 3.dmp b/instruments/OPM/Mallet 3.dmp new file mode 100644 index 000000000..9e55d50bd Binary files /dev/null and b/instruments/OPM/Mallet 3.dmp differ diff --git a/instruments/OPM/Mallet 4.dmp b/instruments/OPM/Mallet 4.dmp new file mode 100644 index 000000000..a9576c4ce Binary files /dev/null and b/instruments/OPM/Mallet 4.dmp differ diff --git a/instruments/OPM/Mellow Lead 1.dmp b/instruments/OPM/Mellow Lead 1.dmp new file mode 100644 index 000000000..9adb3ced0 Binary files /dev/null and b/instruments/OPM/Mellow Lead 1.dmp differ diff --git a/instruments/OPM/Metal Bass 2.dmp b/instruments/OPM/Metal Bass 2.dmp new file mode 100644 index 000000000..2eefd72e5 Binary files /dev/null and b/instruments/OPM/Metal Bass 2.dmp differ diff --git a/instruments/OPM/Metal Bass 4.dmp b/instruments/OPM/Metal Bass 4.dmp new file mode 100644 index 000000000..1c7d8ac58 Binary files /dev/null and b/instruments/OPM/Metal Bass 4.dmp differ diff --git a/instruments/OPM/Metal Bass 5.dmp b/instruments/OPM/Metal Bass 5.dmp new file mode 100644 index 000000000..f3d148fed Binary files /dev/null and b/instruments/OPM/Metal Bass 5.dmp differ diff --git a/instruments/OPM/Metal Bass 6.dmp b/instruments/OPM/Metal Bass 6.dmp new file mode 100644 index 000000000..e6806ba31 Binary files /dev/null and b/instruments/OPM/Metal Bass 6.dmp differ diff --git a/instruments/OPM/Mini Acid 1.dmp b/instruments/OPM/Mini Acid 1.dmp new file mode 100644 index 000000000..22d361897 Binary files /dev/null and b/instruments/OPM/Mini Acid 1.dmp differ diff --git a/instruments/OPM/Octave Synth 1.dmp b/instruments/OPM/Octave Synth 1.dmp new file mode 100644 index 000000000..617e8d3af Binary files /dev/null and b/instruments/OPM/Octave Synth 1.dmp differ diff --git a/instruments/OPM/Piano Low.dmp b/instruments/OPM/Piano Low.dmp new file mode 100644 index 000000000..7b6e66112 Binary files /dev/null and b/instruments/OPM/Piano Low.dmp differ diff --git a/instruments/OPM/Plucked 1.dmp b/instruments/OPM/Plucked 1.dmp new file mode 100644 index 000000000..adbaf68db Binary files /dev/null and b/instruments/OPM/Plucked 1.dmp differ diff --git a/instruments/OPM/Plucked 2.dmp b/instruments/OPM/Plucked 2.dmp new file mode 100644 index 000000000..42df0e2fc Binary files /dev/null and b/instruments/OPM/Plucked 2.dmp differ diff --git a/instruments/OPM/Plucked 3.dmp b/instruments/OPM/Plucked 3.dmp new file mode 100644 index 000000000..368e9b717 Binary files /dev/null and b/instruments/OPM/Plucked 3.dmp differ diff --git a/instruments/OPM/Slap Bass 1.dmp b/instruments/OPM/Slap Bass 1.dmp new file mode 100644 index 000000000..95dd31c43 Binary files /dev/null and b/instruments/OPM/Slap Bass 1.dmp differ diff --git a/instruments/OPM/StringEns 3.dmp b/instruments/OPM/StringEns 3.dmp new file mode 100644 index 000000000..db134804e Binary files /dev/null and b/instruments/OPM/StringEns 3.dmp differ diff --git a/instruments/OPM/Synth Brass 3.dmp b/instruments/OPM/Synth Brass 3.dmp new file mode 100644 index 000000000..1b166cafd Binary files /dev/null and b/instruments/OPM/Synth Brass 3.dmp differ diff --git a/instruments/OPM/Synth Brass 4.dmp b/instruments/OPM/Synth Brass 4.dmp new file mode 100644 index 000000000..3b26a9e2b Binary files /dev/null and b/instruments/OPM/Synth Brass 4.dmp differ diff --git a/instruments/OPM/Synth Brass 5.dmp b/instruments/OPM/Synth Brass 5.dmp new file mode 100644 index 000000000..f567503e6 Binary files /dev/null and b/instruments/OPM/Synth Brass 5.dmp differ diff --git a/instruments/OPM/Synth Brass 6.dmp b/instruments/OPM/Synth Brass 6.dmp new file mode 100644 index 000000000..a8baa1473 Binary files /dev/null and b/instruments/OPM/Synth Brass 6.dmp differ diff --git a/instruments/OPM/Synth Flute.dmp b/instruments/OPM/Synth Flute.dmp new file mode 100644 index 000000000..a2ee31648 Binary files /dev/null and b/instruments/OPM/Synth Flute.dmp differ diff --git a/instruments/OPM/Synth Lead 2.dmp b/instruments/OPM/Synth Lead 2.dmp new file mode 100644 index 000000000..0248a94e3 Binary files /dev/null and b/instruments/OPM/Synth Lead 2.dmp differ diff --git a/instruments/OPM/Synth Lead 3.dmp b/instruments/OPM/Synth Lead 3.dmp new file mode 100644 index 000000000..4a6eacfab Binary files /dev/null and b/instruments/OPM/Synth Lead 3.dmp differ diff --git a/instruments/OPM/Synth Reed 2.dmp b/instruments/OPM/Synth Reed 2.dmp new file mode 100644 index 000000000..e2cafd0a2 Binary files /dev/null and b/instruments/OPM/Synth Reed 2.dmp differ diff --git a/instruments/OPM/Synth Reed 3.dmp b/instruments/OPM/Synth Reed 3.dmp new file mode 100644 index 000000000..a508ee5d0 Binary files /dev/null and b/instruments/OPM/Synth Reed 3.dmp differ diff --git a/instruments/OPM/Synth Reed 4.dmp b/instruments/OPM/Synth Reed 4.dmp new file mode 100644 index 000000000..947190ba2 Binary files /dev/null and b/instruments/OPM/Synth Reed 4.dmp differ diff --git a/instruments/OPM/VariBrass 2.dmp b/instruments/OPM/VariBrass 2.dmp new file mode 100644 index 000000000..0b8605e40 Binary files /dev/null and b/instruments/OPM/VariBrass 2.dmp differ diff --git a/instruments/OPN/bass/A.Bass1.fui b/instruments/OPN/bass/A.Bass1.fui new file mode 100644 index 000000000..46604da86 Binary files /dev/null and b/instruments/OPN/bass/A.Bass1.fui differ diff --git a/instruments/OPN/bass/A.Bass2a.fui b/instruments/OPN/bass/A.Bass2a.fui new file mode 100644 index 000000000..02ca3f21e Binary files /dev/null and b/instruments/OPN/bass/A.Bass2a.fui differ diff --git a/instruments/OPN/bass/A.Bass2b.fui b/instruments/OPN/bass/A.Bass2b.fui new file mode 100644 index 000000000..d643c4f0b Binary files /dev/null and b/instruments/OPN/bass/A.Bass2b.fui differ diff --git a/instruments/OPN/bass/A.Bass2c.fui b/instruments/OPN/bass/A.Bass2c.fui new file mode 100644 index 000000000..d2d24b1a9 Binary files /dev/null and b/instruments/OPN/bass/A.Bass2c.fui differ diff --git a/instruments/OPN/bass/Digi.Bass1.fui b/instruments/OPN/bass/Digi.Bass1.fui new file mode 100644 index 000000000..796a4457e Binary files /dev/null and b/instruments/OPN/bass/Digi.Bass1.fui differ diff --git a/instruments/OPN/bass/Digi.Bass2.fui b/instruments/OPN/bass/Digi.Bass2.fui new file mode 100644 index 000000000..3239f31dd Binary files /dev/null and b/instruments/OPN/bass/Digi.Bass2.fui differ diff --git a/instruments/OPN/bass/Digi.Bass3.fui b/instruments/OPN/bass/Digi.Bass3.fui new file mode 100644 index 000000000..bafe20b76 Binary files /dev/null and b/instruments/OPN/bass/Digi.Bass3.fui differ diff --git a/instruments/OPN/bass/E.Bass1a.fui b/instruments/OPN/bass/E.Bass1a.fui new file mode 100644 index 000000000..5a1e49514 Binary files /dev/null and b/instruments/OPN/bass/E.Bass1a.fui differ diff --git a/instruments/OPN/bass/E.Bass1b.fui b/instruments/OPN/bass/E.Bass1b.fui new file mode 100644 index 000000000..b008696ae Binary files /dev/null and b/instruments/OPN/bass/E.Bass1b.fui differ diff --git a/instruments/OPN/bass/E.Bass1c.fui b/instruments/OPN/bass/E.Bass1c.fui new file mode 100644 index 000000000..1a1fa9933 Binary files /dev/null and b/instruments/OPN/bass/E.Bass1c.fui differ diff --git a/instruments/OPN/bass/E.Bass1d.fui b/instruments/OPN/bass/E.Bass1d.fui new file mode 100644 index 000000000..13db5e3af Binary files /dev/null and b/instruments/OPN/bass/E.Bass1d.fui differ diff --git a/instruments/OPN/bass/E.Bass1e.fui b/instruments/OPN/bass/E.Bass1e.fui new file mode 100644 index 000000000..bdfdecbc6 Binary files /dev/null and b/instruments/OPN/bass/E.Bass1e.fui differ diff --git a/instruments/OPN/bass/E.Bass2a.fui b/instruments/OPN/bass/E.Bass2a.fui new file mode 100644 index 000000000..89fd5678b Binary files /dev/null and b/instruments/OPN/bass/E.Bass2a.fui differ diff --git a/instruments/OPN/bass/E.Bass2b.fui b/instruments/OPN/bass/E.Bass2b.fui new file mode 100644 index 000000000..df090c5c1 Binary files /dev/null and b/instruments/OPN/bass/E.Bass2b.fui differ diff --git a/instruments/OPN/bass/E.Bass2c.fui b/instruments/OPN/bass/E.Bass2c.fui new file mode 100644 index 000000000..7577d468d Binary files /dev/null and b/instruments/OPN/bass/E.Bass2c.fui differ diff --git a/instruments/OPN/bass/E.Bass3a.fui b/instruments/OPN/bass/E.Bass3a.fui new file mode 100644 index 000000000..96f236ccb Binary files /dev/null and b/instruments/OPN/bass/E.Bass3a.fui differ diff --git a/instruments/OPN/bass/E.Bass3b.fui b/instruments/OPN/bass/E.Bass3b.fui new file mode 100644 index 000000000..fe7e9922f Binary files /dev/null and b/instruments/OPN/bass/E.Bass3b.fui differ diff --git a/instruments/OPN/bass/E.Bass3c.fui b/instruments/OPN/bass/E.Bass3c.fui new file mode 100644 index 000000000..e1b3feb47 Binary files /dev/null and b/instruments/OPN/bass/E.Bass3c.fui differ diff --git a/instruments/OPN/bass/E.Bass3d.fui b/instruments/OPN/bass/E.Bass3d.fui new file mode 100644 index 000000000..fedfb8431 Binary files /dev/null and b/instruments/OPN/bass/E.Bass3d.fui differ diff --git a/instruments/OPN/bass/E.Bass3e.fui b/instruments/OPN/bass/E.Bass3e.fui new file mode 100644 index 000000000..64a3c187e Binary files /dev/null and b/instruments/OPN/bass/E.Bass3e.fui differ diff --git a/instruments/OPN/bass/E.Bass3f.fui b/instruments/OPN/bass/E.Bass3f.fui new file mode 100644 index 000000000..ae876d3a7 Binary files /dev/null and b/instruments/OPN/bass/E.Bass3f.fui differ diff --git a/instruments/OPN/bass/E.Bass3g.fui b/instruments/OPN/bass/E.Bass3g.fui new file mode 100644 index 000000000..0bf4008be Binary files /dev/null and b/instruments/OPN/bass/E.Bass3g.fui differ diff --git a/instruments/OPN/bass/E.Bass4a.fui b/instruments/OPN/bass/E.Bass4a.fui new file mode 100644 index 000000000..39c9027dc Binary files /dev/null and b/instruments/OPN/bass/E.Bass4a.fui differ diff --git a/instruments/OPN/bass/E.Bass4b.fui b/instruments/OPN/bass/E.Bass4b.fui new file mode 100644 index 000000000..0d7bf5082 Binary files /dev/null and b/instruments/OPN/bass/E.Bass4b.fui differ diff --git a/instruments/OPN/bass/E.Bass4c.fui b/instruments/OPN/bass/E.Bass4c.fui new file mode 100644 index 000000000..5d093ef60 Binary files /dev/null and b/instruments/OPN/bass/E.Bass4c.fui differ diff --git a/instruments/OPN/bass/E.Bass5.fui b/instruments/OPN/bass/E.Bass5.fui new file mode 100644 index 000000000..9f48b6214 Binary files /dev/null and b/instruments/OPN/bass/E.Bass5.fui differ diff --git a/instruments/OPN/bass/E.Bass6.fui b/instruments/OPN/bass/E.Bass6.fui new file mode 100644 index 000000000..c8f0ee3be Binary files /dev/null and b/instruments/OPN/bass/E.Bass6.fui differ diff --git a/instruments/OPN/bass/E.Bass7.fui b/instruments/OPN/bass/E.Bass7.fui new file mode 100644 index 000000000..a199aa5bd Binary files /dev/null and b/instruments/OPN/bass/E.Bass7.fui differ diff --git a/instruments/OPN/bass/House Bass 2.fui b/instruments/OPN/bass/House Bass 2.fui new file mode 100644 index 000000000..acd2ee3d6 Binary files /dev/null and b/instruments/OPN/bass/House Bass 2.fui differ diff --git a/instruments/OPN/bass/House Bass.fui b/instruments/OPN/bass/House Bass.fui new file mode 100644 index 000000000..c9f50c207 Binary files /dev/null and b/instruments/OPN/bass/House Bass.fui differ diff --git a/instruments/OPN/bass/Metal Bass 1.dmp b/instruments/OPN/bass/Metal Bass 1.dmp new file mode 100644 index 000000000..c7f807e2f Binary files /dev/null and b/instruments/OPN/bass/Metal Bass 1.dmp differ diff --git a/instruments/OPN/bass/Metal Bass 3.dmp b/instruments/OPN/bass/Metal Bass 3.dmp new file mode 100644 index 000000000..afb3374f6 Binary files /dev/null and b/instruments/OPN/bass/Metal Bass 3.dmp differ diff --git a/instruments/OPN/bass/Percussion Bass 2.dmp b/instruments/OPN/bass/Percussion Bass 2.dmp new file mode 100644 index 000000000..1e751ebcc Binary files /dev/null and b/instruments/OPN/bass/Percussion Bass 2.dmp differ diff --git a/instruments/OPN/bass/Percussion Bass.dmp b/instruments/OPN/bass/Percussion Bass.dmp new file mode 100644 index 000000000..ff580b77a Binary files /dev/null and b/instruments/OPN/bass/Percussion Bass.dmp differ diff --git a/instruments/OPN/bass/Slap Bass 2.fui b/instruments/OPN/bass/Slap Bass 2.fui new file mode 100644 index 000000000..7898c13c1 Binary files /dev/null and b/instruments/OPN/bass/Slap Bass 2.fui differ diff --git a/instruments/OPN/bass/Slap Bass 3.fui b/instruments/OPN/bass/Slap Bass 3.fui new file mode 100644 index 000000000..b61505ed7 Binary files /dev/null and b/instruments/OPN/bass/Slap Bass 3.fui differ diff --git a/instruments/OPN/bass/Slap Bass 4.fui b/instruments/OPN/bass/Slap Bass 4.fui new file mode 100644 index 000000000..937c1e89d Binary files /dev/null and b/instruments/OPN/bass/Slap Bass 4.fui differ diff --git a/instruments/OPN/drums/BassD 1.dmp b/instruments/OPN/drums/BassD 1.dmp new file mode 100644 index 000000000..f3e7ea190 Binary files /dev/null and b/instruments/OPN/drums/BassD 1.dmp differ diff --git a/instruments/OPN/drums/Cymbal.fui b/instruments/OPN/drums/Cymbal.fui new file mode 100644 index 000000000..475b4f726 Binary files /dev/null and b/instruments/OPN/drums/Cymbal.fui differ diff --git a/instruments/OPN/drums/Kick Drum.fui b/instruments/OPN/drums/Kick Drum.fui new file mode 100644 index 000000000..286c63767 Binary files /dev/null and b/instruments/OPN/drums/Kick Drum.fui differ diff --git a/instruments/OPN/drums/Snare 4.dmp b/instruments/OPN/drums/Snare 4.dmp new file mode 100644 index 000000000..b335ca03a Binary files /dev/null and b/instruments/OPN/drums/Snare 4.dmp differ diff --git a/instruments/OPN/drums/Snare Drum.fui b/instruments/OPN/drums/Snare Drum.fui new file mode 100644 index 000000000..7c966423d Binary files /dev/null and b/instruments/OPN/drums/Snare Drum.fui differ diff --git a/instruments/OPN/guitar/Synth Guitar.dmp b/instruments/OPN/guitar/Synth Guitar.dmp new file mode 100644 index 000000000..6f7efa205 Binary files /dev/null and b/instruments/OPN/guitar/Synth Guitar.dmp differ diff --git a/instruments/OPN/horn/Brass Lead 1.fui b/instruments/OPN/horn/Brass Lead 1.fui new file mode 100644 index 000000000..eed11f9f8 Binary files /dev/null and b/instruments/OPN/horn/Brass Lead 1.fui differ diff --git a/instruments/OPN/horn/Brass Lead 2.fui b/instruments/OPN/horn/Brass Lead 2.fui new file mode 100644 index 000000000..817a98893 Binary files /dev/null and b/instruments/OPN/horn/Brass Lead 2.fui differ diff --git a/instruments/OPN/horn/Brass Synth 1.dmp b/instruments/OPN/horn/Brass Synth 1.dmp new file mode 100644 index 000000000..3b440a887 Binary files /dev/null and b/instruments/OPN/horn/Brass Synth 1.dmp differ diff --git a/instruments/OPN/horn/ModBrass 1.dmp b/instruments/OPN/horn/ModBrass 1.dmp new file mode 100644 index 000000000..97f8a0aac Binary files /dev/null and b/instruments/OPN/horn/ModBrass 1.dmp differ diff --git a/instruments/OPN/horn/Sharp Hepta-Brass.dmp b/instruments/OPN/horn/Sharp Hepta-Brass.dmp new file mode 100644 index 000000000..2599a07dd Binary files /dev/null and b/instruments/OPN/horn/Sharp Hepta-Brass.dmp differ diff --git a/instruments/OPN/horn/Sharp Horn.dmp b/instruments/OPN/horn/Sharp Horn.dmp new file mode 100644 index 000000000..ed1d71fb9 Binary files /dev/null and b/instruments/OPN/horn/Sharp Horn.dmp differ diff --git a/instruments/OPN/horn/Synth Brass 1.dmp b/instruments/OPN/horn/Synth Brass 1.dmp new file mode 100644 index 000000000..ceb765fe7 Binary files /dev/null and b/instruments/OPN/horn/Synth Brass 1.dmp differ diff --git a/instruments/OPN/horn/Synth Brass 2.dmp b/instruments/OPN/horn/Synth Brass 2.dmp new file mode 100644 index 000000000..818d70cf4 Binary files /dev/null and b/instruments/OPN/horn/Synth Brass 2.dmp differ diff --git a/instruments/OPN/horn/Trumpet 1.fui b/instruments/OPN/horn/Trumpet 1.fui new file mode 100644 index 000000000..d7c97a2f5 Binary files /dev/null and b/instruments/OPN/horn/Trumpet 1.fui differ diff --git a/instruments/OPN/horn/Trumpet 2.fui b/instruments/OPN/horn/Trumpet 2.fui new file mode 100644 index 000000000..111665d21 Binary files /dev/null and b/instruments/OPN/horn/Trumpet 2.fui differ diff --git a/instruments/OPN/horn/VariBrass 1.dmp b/instruments/OPN/horn/VariBrass 1.dmp new file mode 100644 index 000000000..ef970e6d3 Binary files /dev/null and b/instruments/OPN/horn/VariBrass 1.dmp differ diff --git a/instruments/OPN/keys/Digital Clav 1.fui b/instruments/OPN/keys/Digital Clav 1.fui new file mode 100644 index 000000000..3867cfd1d Binary files /dev/null and b/instruments/OPN/keys/Digital Clav 1.fui differ diff --git a/instruments/OPN/keys/Digital Piano 1.fui b/instruments/OPN/keys/Digital Piano 1.fui new file mode 100644 index 000000000..998b3afde Binary files /dev/null and b/instruments/OPN/keys/Digital Piano 1.fui differ diff --git a/instruments/OPN/keys/Digital Piano 2.fui b/instruments/OPN/keys/Digital Piano 2.fui new file mode 100644 index 000000000..e799aedeb Binary files /dev/null and b/instruments/OPN/keys/Digital Piano 2.fui differ diff --git a/instruments/OPN/keys/Digital Piano 3.fui b/instruments/OPN/keys/Digital Piano 3.fui new file mode 100644 index 000000000..3636a6202 Binary files /dev/null and b/instruments/OPN/keys/Digital Piano 3.fui differ diff --git a/instruments/OPN/keys/E. Piano 3 (Glassy).dmp b/instruments/OPN/keys/E. Piano 3 (Glassy).dmp new file mode 100644 index 000000000..a519345ad Binary files /dev/null and b/instruments/OPN/keys/E. Piano 3 (Glassy).dmp differ diff --git a/instruments/OPN/keys/Organ 2 (Digital).dmp b/instruments/OPN/keys/Organ 2 (Digital).dmp new file mode 100644 index 000000000..47ea44cca Binary files /dev/null and b/instruments/OPN/keys/Organ 2 (Digital).dmp differ diff --git a/instruments/OPN/keys/Organ 3.fui b/instruments/OPN/keys/Organ 3.fui new file mode 100644 index 000000000..b952efa90 Binary files /dev/null and b/instruments/OPN/keys/Organ 3.fui differ diff --git a/instruments/OPN/keys/Organ 4.fui b/instruments/OPN/keys/Organ 4.fui new file mode 100644 index 000000000..cdb3c8fa6 Binary files /dev/null and b/instruments/OPN/keys/Organ 4.fui differ diff --git a/instruments/OPN/keys/Soft Organ Lead.dmp b/instruments/OPN/keys/Soft Organ Lead.dmp new file mode 100644 index 000000000..1582e5d7b Binary files /dev/null and b/instruments/OPN/keys/Soft Organ Lead.dmp differ diff --git a/instruments/OPN/percussion/Mallet 1.dmp b/instruments/OPN/percussion/Mallet 1.dmp new file mode 100644 index 000000000..c54a1c6ba Binary files /dev/null and b/instruments/OPN/percussion/Mallet 1.dmp differ diff --git a/instruments/OPN/synth/Liquid Lead.dmp b/instruments/OPN/synth/Liquid Lead.dmp new file mode 100644 index 000000000..92b2f1fbd Binary files /dev/null and b/instruments/OPN/synth/Liquid Lead.dmp differ diff --git a/instruments/OPN/synth/Metal Lead 2.dmp b/instruments/OPN/synth/Metal Lead 2.dmp new file mode 100644 index 000000000..ceba5240e Binary files /dev/null and b/instruments/OPN/synth/Metal Lead 2.dmp differ diff --git a/instruments/OPN/synth/Metal Lead 3.dmp b/instruments/OPN/synth/Metal Lead 3.dmp new file mode 100644 index 000000000..779a7a559 Binary files /dev/null and b/instruments/OPN/synth/Metal Lead 3.dmp differ diff --git a/instruments/OPN/synth/Metal Lead.dmp b/instruments/OPN/synth/Metal Lead.dmp new file mode 100644 index 000000000..716bff888 Binary files /dev/null and b/instruments/OPN/synth/Metal Lead.dmp differ diff --git a/instruments/OPN/synth/ResoUnison-Synth 1.dmp b/instruments/OPN/synth/ResoUnison-Synth 1.dmp new file mode 100644 index 000000000..4b2f38d6c Binary files /dev/null and b/instruments/OPN/synth/ResoUnison-Synth 1.dmp differ diff --git a/instruments/OPN/synth/Saw Lead.fui b/instruments/OPN/synth/Saw Lead.fui new file mode 100644 index 000000000..5b2352051 Binary files /dev/null and b/instruments/OPN/synth/Saw Lead.fui differ diff --git a/instruments/OPN/synth/SeqSynth 1.dmp b/instruments/OPN/synth/SeqSynth 1.dmp new file mode 100644 index 000000000..036bf0ab6 Binary files /dev/null and b/instruments/OPN/synth/SeqSynth 1.dmp differ diff --git a/instruments/OPN/synth/Synth Reed 1.dmp b/instruments/OPN/synth/Synth Reed 1.dmp new file mode 100644 index 000000000..bdbacbb43 Binary files /dev/null and b/instruments/OPN/synth/Synth Reed 1.dmp differ diff --git a/instruments/OPN/wind/Clarinet.fui b/instruments/OPN/wind/Clarinet.fui new file mode 100644 index 000000000..d505247dd Binary files /dev/null and b/instruments/OPN/wind/Clarinet.fui differ diff --git a/instruments/OPN/wind/Even-Calliope.dmp b/instruments/OPN/wind/Even-Calliope.dmp new file mode 100644 index 000000000..55cee7bdf Binary files /dev/null and b/instruments/OPN/wind/Even-Calliope.dmp differ diff --git a/instruments/OPN/wind/Harmonica 2.fui b/instruments/OPN/wind/Harmonica 2.fui new file mode 100644 index 000000000..56d6a1483 Binary files /dev/null and b/instruments/OPN/wind/Harmonica 2.fui differ diff --git a/instruments/OPN/wind/Sax 1.fui b/instruments/OPN/wind/Sax 1.fui new file mode 100644 index 000000000..8d4d8920e Binary files /dev/null and b/instruments/OPN/wind/Sax 1.fui differ diff --git a/instruments/OPN/wind/Sax 2 (Bari).fui b/instruments/OPN/wind/Sax 2 (Bari).fui new file mode 100644 index 000000000..31fb98e75 Binary files /dev/null and b/instruments/OPN/wind/Sax 2 (Bari).fui differ diff --git a/papers/format.md b/papers/format.md index 92f8b0edf..d8e12b7bd 100644 --- a/papers/format.md +++ b/papers/format.md @@ -256,6 +256,8 @@ size | description | - 0xe0: QSound - 19 channels | - 0xe1: PS1 - 24 channels (UNAVAILABLE) | - 0xe2: C64 (6581) with PCM - 4 channels (UNAVAILABLE) + | - 0xe3: Watara Supervision - 4 channels (UNAVAILABLE) + | - 0xe4: µPD1771C - 1 channel (UNAVAILABLE) | - 0xf0: SID2 - 3 channels | - 0xf1: 5E01 - 5 channels | - 0xfc: Pong - 1 channel diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1a382b85e..e9b3255c3 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3589,6 +3589,12 @@ void DivEngine::synchronized(const std::function& what) { BUSY_END; } +void DivEngine::synchronizedSoft(const std::function& what) { + BUSY_BEGIN_SOFT; + what(); + BUSY_END; +} + void DivEngine::lockSave(const std::function& what) { saveLock.lock(); what(); @@ -3916,6 +3922,9 @@ bool DivEngine::preInit(bool noSafeMode) { // register systems if (!systemsRegistered) registerSystems(); + // register ROM exports + if (!romExportsRegistered) registerROMExports(); + // TODO: re-enable with a better approach // see issue #1581 /* diff --git a/src/engine/engine.h b/src/engine/engine.h index 6979f2c26..ac897d136 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -54,8 +54,8 @@ class DivWorkPool; #define DIV_UNSTABLE -#define DIV_VERSION "Import Test" -#define DIV_ENGINE_VERSION 216 +#define DIV_VERSION "dev217" +#define DIV_ENGINE_VERSION 217 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -465,6 +465,7 @@ class DivEngine { bool midiIsDirectProgram; bool lowLatency; bool systemsRegistered; + bool romExportsRegistered; bool hasLoadedSomething; bool midiOutClock; bool midiOutTime; @@ -518,6 +519,7 @@ class DivEngine { static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS]; static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS]; static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS]; + static DivROMExportDef* romExportDefs[DIV_ROM_MAX]; DivCSPlayer* cmdStreamInt; @@ -635,6 +637,7 @@ class DivEngine { bool deinitAudioBackend(bool dueToSwitchMaster=false); void registerSystems(); + void registerROMExports(); void initSongWithDesc(const char* description, bool inBase64=true, bool oldVol=false); void exchangeIns(int one, int two); @@ -665,6 +668,7 @@ class DivEngine { // add every export method here friend class DivROMExport; friend class DivExportAmigaValidation; + friend class DivExportTiuna; public: DivSong song; @@ -895,6 +899,9 @@ class DivEngine { // get sys definition const DivSysDef* getSystemDef(DivSystem sys); + // get ROM export definition + const DivROMExportDef* getROMExportDef(DivROMExportOptions opt); + // convert sample rate format int fileToDivRate(int frate); int divToFileRate(int drate); @@ -1305,6 +1312,9 @@ class DivEngine { // perform secure/sync operation void synchronized(const std::function& what); + // perform secure/sync operation (soft) + void synchronizedSoft(const std::function& what); + // perform secure/sync song operation void lockSave(const std::function& what); @@ -1372,6 +1382,7 @@ class DivEngine { midiIsDirectProgram(false), lowLatency(false), systemsRegistered(false), + romExportsRegistered(false), hasLoadedSomething(false), midiOutClock(false), midiOutTime(false), @@ -1478,6 +1489,7 @@ class DivEngine { memset(pitchTable,0,4096*sizeof(int)); memset(effectSlotMap,-1,4096*sizeof(short)); memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*)); + memset(romExportDefs,0,DIV_ROM_MAX*sizeof(void*)); memset(walked,0,8192); memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*))); memset(exportChannelMask,1,DIV_MAX_CHANS*sizeof(bool)); diff --git a/src/engine/export.cpp b/src/engine/export.cpp index 68794069e..33544e252 100644 --- a/src/engine/export.cpp +++ b/src/engine/export.cpp @@ -20,6 +20,7 @@ #include "engine.h" #include "export/amigaValidation.h" +#include "export/tiuna.h" DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) { DivROMExport* exporter=NULL; @@ -27,6 +28,9 @@ DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) { case DIV_ROM_AMIGA_VALIDATION: exporter=new DivExportAmigaValidation; break; + case DIV_ROM_TIUNA: + exporter=new DivExportTiuna; + break; default: exporter=new DivROMExport; break; diff --git a/src/engine/export.h b/src/engine/export.h index 9445ecfe9..f9439fc95 100644 --- a/src/engine/export.h +++ b/src/engine/export.h @@ -29,6 +29,8 @@ class DivEngine; enum DivROMExportOptions { DIV_ROM_ABSTRACT=0, DIV_ROM_AMIGA_VALIDATION, + DIV_ROM_ZSM, + DIV_ROM_TIUNA, DIV_ROM_MAX }; @@ -52,39 +54,54 @@ struct DivROMExportProgress { class DivROMExport { protected: - std::vector exportLog; + DivConfig conf; std::vector output; - std::mutex logLock; void logAppend(String what); public: + std::vector exportLog; + std::mutex logLock; + + void setConf(DivConfig& c); virtual bool go(DivEngine* eng); virtual void abort(); virtual void wait(); std::vector& getResult(); virtual bool hasFailed(); virtual bool isRunning(); - virtual DivROMExportProgress getProgress(); + virtual DivROMExportProgress getProgress(int index=0); virtual ~DivROMExport() {} }; +#define logAppendf(...) logAppend(fmt::sprintf(__VA_ARGS__)) + +enum DivROMExportReqPolicy { + // exactly these chips. + DIV_REQPOL_EXACT=0, + // specified chips must be present but any amount of them is acceptable. + DIV_REQPOL_ANY, + // at least one of the specified chips. + DIV_REQPOL_LAX +}; + struct DivROMExportDef { const char* name; const char* author; const char* description; - DivSystem requisites[32]; - int requisitesLen; + const char* fileType; + const char* fileExt; + std::vector requisites; bool multiOutput; + DivROMExportReqPolicy requisitePolicy; - DivROMExportDef(const char* n, const char* a, const char* d, std::initializer_list req, bool multiOut): + DivROMExportDef(const char* n, const char* a, const char* d, const char* ft, const char* fe, std::initializer_list req, bool multiOut, DivROMExportReqPolicy reqPolicy): name(n), author(a), description(d), - multiOutput(multiOut) { - requisitesLen=0; - memset(requisites,0,32*sizeof(DivSystem)); - for (DivSystem i: req) { - requisites[requisitesLen++]=i; - } + fileType(ft), + fileExt(fe), + multiOutput(multiOut), + requisitePolicy(reqPolicy) { + requisites=req; } }; diff --git a/src/engine/export/abstract.cpp b/src/engine/export/abstract.cpp index c33c38502..c6d0ec799 100644 --- a/src/engine/export/abstract.cpp +++ b/src/engine/export/abstract.cpp @@ -36,9 +36,9 @@ bool DivROMExport::hasFailed() { return true; } -DivROMExportProgress DivROMExport::getProgress() { +DivROMExportProgress DivROMExport::getProgress(int index) { DivROMExportProgress ret; - ret.name="Test"; + ret.name=""; ret.amount=0.0f; return ret; } @@ -55,3 +55,7 @@ void DivROMExport::wait() { bool DivROMExport::isRunning() { return false; } + +void DivROMExport::setConf(DivConfig& c) { + conf=c; +} diff --git a/src/engine/export/amigaValidation.cpp b/src/engine/export/amigaValidation.cpp index e9a8a8b84..5fce7a920 100644 --- a/src/engine/export/amigaValidation.cpp +++ b/src/engine/export/amigaValidation.cpp @@ -70,6 +70,7 @@ void DivExportAmigaValidation::run() { bool done=false; // sample.bin + logAppend("writing samples..."); SafeWriter* sample=new SafeWriter; sample->init(); for (int i=0; i<256; i++) { @@ -79,6 +80,7 @@ void DivExportAmigaValidation::run() { if (sample->tell()&1) sample->writeC(0); // seq.bin + logAppend("making sequence..."); SafeWriter* seq=new SafeWriter; seq->init(); @@ -239,6 +241,7 @@ void DivExportAmigaValidation::run() { EXTERN_BUSY_END; // wave.bin + logAppend("writing wavetables..."); SafeWriter* wave=new SafeWriter; wave->init(); for (WaveEntry& i: waves) { @@ -246,6 +249,7 @@ void DivExportAmigaValidation::run() { } // sbook.bin + logAppend("writing sample book..."); SafeWriter* sbook=new SafeWriter; sbook->init(); for (SampleBookEntry& i: sampleBook) { @@ -255,6 +259,7 @@ void DivExportAmigaValidation::run() { } // wbook.bin + logAppend("writing wavetable book..."); SafeWriter* wbook=new SafeWriter; wbook->init(); for (WaveEntry& i: waves) { @@ -272,6 +277,8 @@ void DivExportAmigaValidation::run() { output.push_back(DivROMExportOutput("wave.bin",wave)); output.push_back(DivROMExportOutput("seq.bin",seq)); + logAppend("finished!"); + running=false; } @@ -296,3 +303,7 @@ void DivExportAmigaValidation::abort() { bool DivExportAmigaValidation::isRunning() { return running; } + +bool DivExportAmigaValidation::hasFailed() { + return false; +} diff --git a/src/engine/export/amigaValidation.h b/src/engine/export/amigaValidation.h index 42703a36e..df8131652 100644 --- a/src/engine/export/amigaValidation.h +++ b/src/engine/export/amigaValidation.h @@ -29,6 +29,7 @@ class DivExportAmigaValidation: public DivROMExport { public: bool go(DivEngine* e); bool isRunning(); + bool hasFailed(); void abort(); void wait(); ~DivExportAmigaValidation() {} diff --git a/src/engine/tiunaOps.cpp b/src/engine/export/tiuna.cpp similarity index 64% rename from src/engine/tiunaOps.cpp rename to src/engine/export/tiuna.cpp index 8a045cdf8..397cee34f 100644 --- a/src/engine/tiunaOps.cpp +++ b/src/engine/export/tiuna.cpp @@ -17,13 +17,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "tiuna.h" +#include "../engine.h" +#include "../ta-log.h" +#include #include #include #include #include -#include "engine.h" -#include "../fileutils.h" -#include "../ta-log.h" struct TiunaNew { short pitch; @@ -180,140 +181,161 @@ static void writeCmd(std::vector& cmds, TiunaCmd& cmd, unsigned char } } -SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, int firstBankSize, int otherBankSize) { - stop(); - repeatPattern=false; - shallStop=false; - setOrder(0); - BUSY_BEGIN_SOFT; - // determine loop point - // bool stopped=false; - int loopOrder=0; - int loopOrderRow=0; - int loopEnd=0; - walkSong(loopOrder,loopOrderRow,loopEnd); - logI("loop point: %d %d",loopOrder,loopOrderRow); - - SafeWriter* w=new SafeWriter; - w->init(); - - int tiaIdx=-1; - - for (int i=0; itoggleRegisterDump(true); - break; - } - } - if (tiaIdx<0) { - lastError="selected TIA system not found"; - return NULL; - } - - // write patterns - // bool writeLoop=false; - bool done=false; - playSub(false); - +void DivExportTiuna::run() { + int loopOrder, loopOrderRow, loopEnd; int tick=0; - // int loopTick=-1; - TiunaLast last[2]; - TiunaNew news[2]; + SafeWriter* w; std::map allCmds[2]; - while (!done) { - // TODO implement loop - // if (loopTick<0 && loopOrder==curOrder && loopOrderRow==curRow - // && (ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0 - // ) { - // writeLoop=true; - // loopTick=tick; - // // invalidate last register state so it always force an absolute write after loop - // for (int i=0; i<2; i++) { - // last[i]=TiunaLast(); - // last[i].pitch=-1; - // last[i].ins=-1; - // last[i].vol=-1; - // } - // } - if (nextTick(false,true) || !playing) { - // stopped=!playing; - done=true; - break; - } - for (int i=0; i<2; i++) { - news[i]=TiunaNew(); - } - // get register dumps - std::vector& writes=disCont[tiaIdx].dispatch->getRegisterWrites(); - for (const DivRegWrite& i: writes) { - switch (i.addr) { - case 0xfffe0000: - case 0xfffe0001: - news[i.addr&1].pitch=i.val; - break; - case 0xfffe0002: - news[0].sync=i.val; - break; - case 0x15: - case 0x16: - news[i.addr-0x15].ins=i.val; - break; - case 0x19: - case 0x1a: - news[i.addr-0x19].vol=i.val; - break; - default: break; - } - } - writes.clear(); - // collect changes - for (int i=0; i<2; i++) { - TiunaCmd cmds; - bool hasCmd=false; - if (news[i].pitch>=0 && (last[i].forcePitch || news[i].pitch!=last[i].pitch)) { - int dt=news[i].pitch-last[i].pitch; - if (!last[i].forcePitch && abs(dt)<=16) { - if (dt<0) cmds.pitchChange=15-dt; - else cmds.pitchChange=dt-1; - } - else cmds.pitchSet=news[i].pitch; - last[i].pitch=news[i].pitch; - last[i].forcePitch=false; - hasCmd=true; - } - if (news[i].ins>=0 && news[i].ins!=last[i].ins) { - cmds.ins=news[i].ins; - last[i].ins=news[i].ins; - hasCmd=true; - } - if (news[i].vol>=0 && news[i].vol!=last[i].vol) { - cmds.vol=(news[i].vol-last[i].vol)&0xf; - last[i].vol=news[i].vol; - hasCmd=true; - } - if (news[i].sync>=0) { - cmds.sync=news[i].sync; - hasCmd=true; - } - if (hasCmd) allCmds[i][tick]=cmds; - } - cmdStream.clear(); - tick++; - } - for (int i=0; igetRegisterWrites().clear(); - disCont[i].dispatch->toggleRegisterDump(false); - } - remainingLoops=-1; - playing=false; - freelance=false; - extValuePresent=false; - BUSY_END; + // config + String baseLabel=conf.getString("baseLabel","song"); + int firstBankSize=conf.getInt("firstBankSize",3072); + int otherBankSize=conf.getInt("otherBankSize",4096-48); + int tiaIdx=conf.getInt("sysToExport",-1); + + e->stop(); + e->repeatPattern=false; + e->shallStop=false; + e->setOrder(0); + e->synchronizedSoft([&]() { + // determine loop point + // bool stopped=false; + loopOrder=0; + loopOrderRow=0; + loopEnd=0; + e->walkSong(loopOrder,loopOrderRow,loopEnd); + logAppendf("loop point: %d %d",loopOrder,loopOrderRow); + + w=new SafeWriter; + w->init(); + + if (tiaIdx<0 || tiaIdx>=e->song.systemLen) { + tiaIdx=-1; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_TIA) { + tiaIdx=i; + break; + } + } + if (tiaIdx<0) { + logAppend("ERROR: selected TIA system not found"); + failed=true; + running=false; + return; + } + } else if (e->song.system[tiaIdx]!=DIV_SYSTEM_TIA) { + logAppend("ERROR: selected chip is not a TIA!"); + failed=true; + running=false; + return; + } + + e->disCont[tiaIdx].dispatch->toggleRegisterDump(true); + + // write patterns + // bool writeLoop=false; + logAppend("recording sequence..."); + bool done=false; + e->playSub(false); + + // int loopTick=-1; + TiunaLast last[2]; + TiunaNew news[2]; + while (!done) { + // TODO implement loop + // if (loopTick<0 && loopOrder==curOrder && loopOrderRow==curRow + // && (ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0 + // ) { + // writeLoop=true; + // loopTick=tick; + // // invalidate last register state so it always force an absolute write after loop + // for (int i=0; i<2; i++) { + // last[i]=TiunaLast(); + // last[i].pitch=-1; + // last[i].ins=-1; + // last[i].vol=-1; + // } + // } + if (e->nextTick(false,true) || !e->playing) { + // stopped=!playing; + done=true; + break; + } + for (int i=0; i<2; i++) { + news[i]=TiunaNew(); + } + // get register dumps + std::vector& writes=e->disCont[tiaIdx].dispatch->getRegisterWrites(); + for (const DivRegWrite& i: writes) { + switch (i.addr) { + case 0xfffe0000: + case 0xfffe0001: + news[i.addr&1].pitch=i.val; + break; + case 0xfffe0002: + news[0].sync=i.val; + break; + case 0x15: + case 0x16: + news[i.addr-0x15].ins=i.val; + break; + case 0x19: + case 0x1a: + news[i.addr-0x19].vol=i.val; + break; + default: break; + } + } + writes.clear(); + // collect changes + for (int i=0; i<2; i++) { + TiunaCmd cmds; + bool hasCmd=false; + if (news[i].pitch>=0 && (last[i].forcePitch || news[i].pitch!=last[i].pitch)) { + int dt=news[i].pitch-last[i].pitch; + if (!last[i].forcePitch && abs(dt)<=16) { + if (dt<0) cmds.pitchChange=15-dt; + else cmds.pitchChange=dt-1; + } + else cmds.pitchSet=news[i].pitch; + last[i].pitch=news[i].pitch; + last[i].forcePitch=false; + hasCmd=true; + } + if (news[i].ins>=0 && news[i].ins!=last[i].ins) { + cmds.ins=news[i].ins; + last[i].ins=news[i].ins; + hasCmd=true; + } + if (news[i].vol>=0 && news[i].vol!=last[i].vol) { + cmds.vol=(news[i].vol-last[i].vol)&0xf; + last[i].vol=news[i].vol; + hasCmd=true; + } + if (news[i].sync>=0) { + cmds.sync=news[i].sync; + hasCmd=true; + } + if (hasCmd) allCmds[i][tick]=cmds; + } + e->cmdStream.clear(); + tick++; + } + for (int i=0; isong.systemLen; i++) { + e->disCont[i].dispatch->getRegisterWrites().clear(); + e->disCont[i].dispatch->toggleRegisterDump(false); + } + + e->remainingLoops=-1; + e->playing=false; + e->freelance=false; + e->extValuePresent=false; + }); + + if (failed) return; // render commands + logAppend("rendering commands..."); std::vector renderedCmds; w->writeText(fmt::format( "; Generated by Furnace " DIV_VERSION "\n" @@ -321,7 +343,7 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, "; Author: {}\n" "; Album: {}\n" "; Subsong #{}: {}\n\n", - song.name,song.author,song.category,curSubSongIndex+1,curSubSong->name + e->song.name,e->song.author,e->song.category,e->curSubSongIndex+1,e->curSubSong->name )); for (int i=0; i<2; i++) { TiunaCmd lastCmd; @@ -349,15 +371,30 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, int cmdSize=renderedCmds.size(); bool* processed=new bool[cmdSize]; memset(processed,0,cmdSize*sizeof(bool)); - logI("max cmId: %d",(MAX(firstBankSize/1024,1))*256); - while (firstBankSize>768 && cmId<(MAX(firstBankSize/1024,1))*256) { - logI("start CM %04x...",cmId); + logAppend("compressing! this may take a while."); + int maxCmId=(MAX(firstBankSize/1024,1))*256; + int lastMaxPMVal=100000; + logAppendf("max cmId: %d",maxCmId); + logAppendf("commands: %d",cmdSize); + while (firstBankSize>768 && cmId potentialMatches; - logD("scan %d size...",cmdSize-1); for (int i=0; i=cmdSize-1) break; + progress[1].amount=(float)i/(float)(cmdSize-1); std::vector match; int ch=renderedCmds[i].ch; for (int j=i+1; jmaxPMVal) { maxPMVal=i.second.bytesSaved; @@ -440,16 +476,19 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, } } int maxPMLen=potentialMatches[maxPMIdx].length; - logV("the other step..."); for (const int i: potentialMatches[maxPMIdx].pos) { confirmedMatches.push_back({i,i+maxPMLen,0,cmId}); memset(processed+i,1,maxPMLen); //std::fill(processed.begin()+i,processed.begin()+(i+maxPMLen),true); } callTicks.push_back(potentialMatches[maxPMIdx].ticks); - logI("CM %04x added: pos=%d,len=%d,matches=%d,saved=%d",cmId,maxPMIdx,maxPMLen,potentialMatches[maxPMIdx].pos.size(),maxPMVal); + logAppendf("CM %04x added: pos=%d,len=%d,matches=%d,saved=%d",cmId,maxPMIdx,maxPMLen,potentialMatches[maxPMIdx].pos.size(),maxPMVal); + lastMaxPMVal=maxPMVal; cmId++; } + progress[0].amount=1.0f; + progress[1].amount=1.0f; + logAppend("generating data..."); delete[] processed; std::sort(confirmedMatches.begin(),confirmedMatches.end(),[](const TiunaMatch& l, const TiunaMatch& r){ return l.poswriteC('\n'); if (totalSize>firstBankSize) { - lastError="first bank is not large enough to contain call table"; - return NULL; + logAppend("ERROR: first bank is not large enough to contain call table"); + failed=true; + running=false; + return; } int curBank=0; @@ -572,13 +615,50 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, } w->writeText(" .text x\"e0\"\n .endsection\n"); totalSize++; - logI("total size: %d bytes (%d banks)",totalSize,curBank+1); - - //FILE* f=ps_fopen("confirmedMatches.txt","wb"); - //if (f!=NULL) { - // fwrite(dbg.getFinalBuf(),1,dbg.size(),f); - // fclose(f); - //} + logAppendf("total size: %d bytes (%d banks)",totalSize,curBank+1); - return w; + output.push_back(DivROMExportOutput("export.asm",w)); + + logAppend("finished!"); + + running=false; +} + +bool DivExportTiuna::go(DivEngine* eng) { + progress[0].name="Compression"; + progress[0].amount=0.0f; + progress[1].name="Confirmed Matches"; + progress[1].amount=0.0f; + + e=eng; + running=true; + failed=false; + mustAbort=false; + exportThread=new std::thread(&DivExportTiuna::run,this); + return true; +} + +void DivExportTiuna::wait() { + if (exportThread!=NULL) { + exportThread->join(); + delete exportThread; + } +} + +void DivExportTiuna::abort() { + mustAbort=true; + wait(); +} + +bool DivExportTiuna::isRunning() { + return running; +} + +bool DivExportTiuna::hasFailed() { + return failed; +} + +DivROMExportProgress DivExportTiuna::getProgress(int index) { + if (index<0 || index>2) return progress[2]; + return progress[index]; } diff --git a/src/engine/export/tiuna.h b/src/engine/export/tiuna.h new file mode 100644 index 000000000..ae1a661a9 --- /dev/null +++ b/src/engine/export/tiuna.h @@ -0,0 +1,38 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "../export.h" + +#include + +class DivExportTiuna: public DivROMExport { + DivEngine* e; + std::thread* exportThread; + DivROMExportProgress progress[3]; + bool running, failed, mustAbort; + void run(); + public: + bool go(DivEngine* e); + bool isRunning(); + bool hasFailed(); + void abort(); + void wait(); + DivROMExportProgress getProgress(int index=0); + ~DivExportTiuna() {} +}; diff --git a/src/engine/exportDef.cpp b/src/engine/exportDef.cpp new file mode 100644 index 000000000..00ee94a36 --- /dev/null +++ b/src/engine/exportDef.cpp @@ -0,0 +1,63 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "engine.h" + +DivROMExportDef* DivEngine::romExportDefs[DIV_ROM_MAX]; + +const DivROMExportDef* DivEngine::getROMExportDef(DivROMExportOptions opt) { + return romExportDefs[opt]; +} + +void DivEngine::registerROMExports() { + logD("registering ROM exports..."); + + romExportDefs[DIV_ROM_AMIGA_VALIDATION]=new DivROMExportDef( + "Amiga Validation", "tildearrow", + "a test export for ensuring Amiga emulation is accurate. do not use!", + NULL, NULL, + {DIV_SYSTEM_AMIGA}, + true, DIV_REQPOL_EXACT + ); + + romExportDefs[DIV_ROM_ZSM]=new DivROMExportDef( + "Commander X16 ZSM", "ZeroByteOrg and MooingLemur", + "Commander X16 Zsound Music File.\n" + "for use with Melodius, Calliope and/or ZSMKit:\n" + "- https://github.com/mooinglemur/zsmkit (development)\n" + "- https://github.com/mooinglemur/melodius (player)\n" + "- https://github.com/ZeroByteOrg/calliope (player)\n", + "ZSM file", ".zsm", + { + DIV_SYSTEM_YM2151, DIV_SYSTEM_VERA + }, + false, DIV_REQPOL_LAX + ); + + romExportDefs[DIV_ROM_TIUNA]=new DivROMExportDef( + "Atari 2600 (TIunA)", "Natt Akuma", + "advanced driver with software tuning support.\n" + "see https://github.com/AYCEdemo/twin-tiuna for code.", + "assembly files", ".asm", + { + DIV_SYSTEM_TIA + }, + false, DIV_REQPOL_ANY + ); +} diff --git a/src/engine/fileOps/fur.cpp b/src/engine/fileOps/fur.cpp index 0251c410e..5483d8fb9 100644 --- a/src/engine/fileOps/fur.cpp +++ b/src/engine/fileOps/fur.cpp @@ -2094,6 +2094,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { ds.systemFlags[i].set("oldPitch",true); } } + } else if (ds.version<217) { + for (int i=0; isamples); + if (flags&8) { // compressed sample unsigned int ret=0; logV("decompression begin... (%d)",s->samples); @@ -672,62 +674,66 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } logV("got: %d",ret); } else { - if (s->depth==DIV_SAMPLE_DEPTH_16BIT) { - if (flags&4) { // downmix stereo - for (unsigned int i=0; isamples; i++) { - short l; - if (convert&2) { - l=reader.readS_BE(); - } else { - l=reader.readS(); + try { + if (s->depth==DIV_SAMPLE_DEPTH_16BIT) { + if (flags&4) { // downmix stereo + for (unsigned int i=0; isamples; i++) { + short l; + if (convert&2) { + l=reader.readS_BE(); + } else { + l=reader.readS(); + } + if (!(convert&1)) { + l^=0x8000; + } + s->data16[i]=l; } - if (!(convert&1)) { - l^=0x8000; + for (unsigned int i=0; isamples; i++) { + short r; + if (convert&2) { + r=reader.readS_BE(); + } else { + r=reader.readS(); + } + if (!(convert&1)) { + r^=0x8000; + } + s->data16[i]=(s->data16[i]+r)>>1; } - s->data16[i]=l; - } - for (unsigned int i=0; isamples; i++) { - short r; - if (convert&2) { - r=reader.readS_BE(); - } else { - r=reader.readS(); + } else { + for (unsigned int i=0; isamples; i++) { + if (convert&2) { + s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000); + } else { + s->data16[i]=reader.readS()^((convert&1)?0:0x8000); + } } - if (!(convert&1)) { - r^=0x8000; - } - s->data16[i]=(s->data16[i]+r)>>1; } } else { - for (unsigned int i=0; isamples; i++) { - if (convert&2) { - s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000); - } else { - s->data16[i]=reader.readS()^((convert&1)?0:0x8000); + if (flags&4) { // downmix stereo + for (unsigned int i=0; isamples; i++) { + signed char l=reader.readC(); + if (!(convert&1)) { + l^=0x80; + } + s->data8[i]=l; + } + for (unsigned int i=0; isamples; i++) { + signed char r=reader.readC(); + if (!(convert&1)) { + r^=0x80; + } + s->data8[i]=(s->data8[i]+r)>>1; + } + } else { + for (unsigned int i=0; isamples; i++) { + s->data8[i]=reader.readC()^((convert&1)?0:0x80); } } } - } else { - if (flags&4) { // downmix stereo - for (unsigned int i=0; isamples; i++) { - signed char l=reader.readC(); - if (!(convert&1)) { - l^=0x80; - } - s->data8[i]=l; - } - for (unsigned int i=0; isamples; i++) { - signed char r=reader.readC(); - if (!(convert&1)) { - r^=0x80; - } - s->data8[i]=(s->data8[i]+r)>>1; - } - } else { - for (unsigned int i=0; isamples; i++) { - s->data8[i]=reader.readC()^((convert&1)?0:0x80); - } - } + } catch (EndOfFileException& e) { + logW("premature end of file..."); } } diff --git a/src/engine/fileOps/s3m.cpp b/src/engine/fileOps/s3m.cpp index d13c1c7c2..9e212d8d3 100644 --- a/src/engine/fileOps/s3m.cpp +++ b/src/engine/fileOps/s3m.cpp @@ -51,6 +51,7 @@ static void readSbiOpData(sbi_t& sbi, SafeReader& reader) { bool DivEngine::loadS3M(unsigned char* file, size_t len) { struct InvalidHeaderException {}; bool success=false; + bool opl2=!getConfInt("s3mOPL3",0); char magic[4]={0,0,0,0}; SafeReader reader=SafeReader(file,len); warnings=""; @@ -273,11 +274,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { bool hasPCM=false; bool hasFM=false; int numChans=0; + int realNumChans=0; - for (int i=0; i<32; i++) { - if (chanSettings[i]==255) continue; - if ((chanSettings[i]&127)>=32) continue; - if ((chanSettings[i]&127)>=16) { + for (int ch=0; ch<32; ch++) { + if (chanSettings[ch]!=255) realNumChans++; + } + + for (int ch=0; ch<32; ch++) { + if (chanSettings[ch]==255) continue; + if ((chanSettings[ch]&127)>=32) continue; + if ((chanSettings[ch]&127)>=16) { hasFM=true; } else { hasPCM=true; @@ -287,34 +293,69 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { if (hasFM && hasPCM) break; } - int pcmChan=hasFM?9:0; + int pcmChan=hasFM?(opl2 ? 9 : 18):0; int fmChan=hasPCM?32:0; int invalidChan=40; - for (int i=0; i<32; i++) { - if (chanSettings[i]==255) { - chanMap[i]=invalidChan++; + for (int ch=0; ch<32; ch++) { + if (chanSettings[ch]==255) { + chanMap[ch]=invalidChan++; continue; } - if ((chanSettings[i]&127)>=32) { - chanMap[i]=invalidChan++; + if ((chanSettings[ch]&127)>=32) { + chanMap[ch]=invalidChan++; continue; } - if ((chanSettings[i]&127)>=16) { - chanMap[i]=fmChan++; + if ((chanSettings[ch]&127)>=16) { + chanMap[ch]=fmChan++; } else { - chanMap[i]=pcmChan++; + chanMap[ch]=pcmChan++; } } + char buffer[40]; + int chanIndex = 1; + if (hasPCM) { - for (int i=pcmChan; i<32; i++) { - ds.subsong[0]->chanShow[i]=false; - ds.subsong[0]->chanShowChanOsc[i]=false; + for(int ch = 0; ch < pcmChan - (realNumChans - (hasFM ? 9 : 0)); ch++) + { + ds.subsong[0]->chanShow[ch]=false; + ds.subsong[0]->chanShowChanOsc[ch]=false; + } + + for (int ch=pcmChan; ch<32; ch++) { + ds.subsong[0]->chanShow[ch]=false; + ds.subsong[0]->chanShowChanOsc[ch]=false; + } + + for(int ch = 0; ch < 32; ch++) + { + if(ds.subsong[0]->chanShow[ch]) + { + snprintf(buffer, 40, _("Channel %d"), chanIndex); + ds.subsong[0]->chanName[ch] = buffer; + chanIndex++; + } + } + } + + if (hasFM && !opl2) { + for (int ch=(hasPCM?32:0) + 9; ch<(hasPCM?32:0) + 18; ch++) { + ds.subsong[0]->chanShow[ch]=false; + ds.subsong[0]->chanShowChanOsc[ch]=false; + } + + chanIndex = 1; + + for (int ch=(hasPCM?32:0); ch<(hasPCM?32:0) + 9; ch++) { + snprintf(buffer, 40, _("FM %d"), chanIndex); + ds.subsong[0]->chanName[ch] = buffer; + chanIndex++; } } logV("numChans: %d",numChans); + logV("realNumChans: %d",realNumChans); ds.systemName="PC"; if (hasPCM) { @@ -327,7 +368,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { ds.systemLen++; } if (hasFM) { - ds.system[ds.systemLen]=DIV_SYSTEM_OPL2; + ds.system[ds.systemLen]=opl2 ? DIV_SYSTEM_OPL2 : DIV_SYSTEM_OPL3; ds.systemVol[ds.systemLen]=1.0f; ds.systemPan[ds.systemLen]=0; ds.systemLen++; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index b073e423e..4e5357722 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -155,43 +155,76 @@ void DivPlatformAY8910::runDAC() { } void DivPlatformAY8910::runTFX() { - if (selCore) return; + /* + developer's note: if you are checking for intellivision + make sure to add "&& selCore" + because for some reason, the register remap doesn't work + when the user uses AtomicSSG core + */ int timerPeriod, output; for (int i=0; i<3; i++) { if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) { + if (chan[i].tfx.mode == -1 && !isMuted[i]) { + /* + bug: if in the timer FX macro the user enables + and then disables PWM while there is no volume macro + there is now a random chance that the resulting output + is silent or has volume set incorrectly + i've tried to implement a fix, but it seems to be + ineffective, so... + TODO: actually implement a proper fix + */ + if (intellivision && chan[i].curPSGMode.getEnvelope()) { + immWrite(0x08+i,(chan[i].outVol&0xc)<<2); + continue; + } else { + immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2)); + continue; + } + } chan[i].tfx.counter += 1; if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) { chan[i].tfx.counter = 0; chan[i].tfx.out ^= 1; - output = MAX(0, ((chan[i].tfx.out) ? (chan[i].outVol&15) : (chan[i].tfx.lowBound-(15-chan[i].outVol)))); - output &= 15; + output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol))); + // TODO: fix this stupid crackling noise that happens + // everytime the volume changes + output = (output <= 0) ? 0 : output; // underflow + output = (output >= 15) ? 15 : output; // overflow + output &= 15; // i don't know if i need this but i'm too scared to remove it if (!isMuted[i]) { - immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2)); + if (intellivision && selCore) { + immWrite(0x0b+i,(output&0xc)<<2); + } else { + immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2)); + } } } if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) { chan[i].tfx.counter = 0; if (!isMuted[i]) { - immWrite(0xd, ayEnvMode); + if (intellivision && selCore) { + immWrite(0xa, ayEnvMode); + } else { + immWrite(0xd, ayEnvMode); + } } } if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) { chan[i].tfx.counter = 0; } - if (chan[i].tfx.mode == -1 && !isMuted[i]) { - if (intellivision && chan[i].curPSGMode.getEnvelope()) { - immWrite(0x08+i,(chan[i].outVol&0xc)<<2); - } else { - immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2)); - } - } } if (chan[i].tfx.num > 0) { - timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num; - } else { - timerPeriod = chan[i].freq*chan[i].tfx.den; - } + timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num; + } else { + timerPeriod = chan[i].freq*chan[i].tfx.den; + } if (chan[i].tfx.num > 0 && chan[i].tfx.den > 0) chan[i].tfx.period=timerPeriod+chan[i].tfx.offset; + // stupid pitch correction because: + // YM2149 half-clock and Sunsoft 5B: timers run an octave too high + // on AtomicSSG core timers run 2 octaves too high + if (clockSel || sunsoft) chan[i].tfx.period = chan[i].tfx.period * 2; + if (selCore) chan[i].tfx.period = chan[i].tfx.period * 4; } } @@ -388,6 +421,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].tfx.counter = 0; + chan[i].tfx.out = 0; if (chan[i].nextPSGMode.val&8) { if (dumpWrites) addWrite(0xffff0002+(i<<8),0); if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) { @@ -726,12 +760,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_STD_NOISE_MODE: - if (c.value&0xf0 && !(chan[c.chan].nextPSGMode.val&8)) { - chan[c.chan].nextPSGMode.val|=16; - chan[c.chan].tfx.mode = (c.value&3); - } if (!(chan[c.chan].nextPSGMode.val&8)) { - if (c.value<16) { + chan[c.chan].nextPSGMode.val|=16; + chan[c.chan].tfx.mode=(((c.value&0xf0)>>4)&3)-1; + if ((c.value&15)<16) { chan[c.chan].nextPSGMode.val=(c.value+1)&7; chan[c.chan].nextPSGMode.val|=chan[c.chan].curPSGMode.val&16; if (chan[c.chan].active) { @@ -805,9 +837,16 @@ int DivPlatformAY8910::dispatch(DivCommand c) { updateOutSel(true); immWrite(14+(c.value?1:0),(c.value?portBVal:portAVal)); break; - case DIV_CMD_AY_AUTO_PWM: - chan[c.chan].tfx.offset=c.value; + case DIV_CMD_AY_NOISE_MASK_AND: + chan[c.chan].tfx.num=c.value>>4; + chan[c.chan].tfx.den=c.value&15; break; + case DIV_CMD_AY_AUTO_PWM: { + // best way i could find to do signed :/ + signed char signVal=c.value; + chan[c.chan].tfx.offset=signVal; + break; + } case DIV_CMD_SAMPLE_MODE: if (c.value>0) { chan[c.chan].nextPSGMode.val|=8; diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 916234f0e..6b40575b9 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -39,14 +39,25 @@ const char** DivPlatformMSM6295::getRegisterSheet() { } u8 DivPlatformMSM6295::read_byte(u32 address) { - if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) { + if (adpcmMem==NULL) { return 0; } if (isBanked) { if (address<0x400) { - return adpcmMem[(bank[(address>>8)&0x3]<<16)|(address&0x3ff)]; + unsigned int bankedAddress=(bank[(address>>8)&0x3]<<16)|(address&0x3ff); + if (bankedAddress>=getSampleMemCapacity(0)) { + return 0; + } + return adpcmMem[bankedAddress&0xffffff]; } - return adpcmMem[(bank[(address>>16)&0x3]<<16)|(address&0xffff)]; + unsigned int bankedAddress=(bank[(address>>16)&0x3]<<16)|(address&0xffff); + if (bankedAddress>=getSampleMemCapacity(0)) { + return 0; + } + return adpcmMem[bankedAddress&0xffffff]; + } + if (address>=getSampleMemCapacity(0)) { + return 0; } return adpcmMem[address&0x3ffff]; } diff --git a/src/engine/platform/sound/vera_psg.c b/src/engine/platform/sound/vera_psg.c index be080ace9..597aa5974 100644 --- a/src/engine/platform/sound/vera_psg.c +++ b/src/engine/platform/sound/vera_psg.c @@ -5,6 +5,7 @@ // Chip revisions // 0: V 0.3.0 // 1: V 47.0.0 (9-bit volume, phase reset on mute) +// 2: V 47.0.2 (Pulse Width XOR on Saw and Triangle) #include "vera_psg.h" @@ -88,8 +89,8 @@ render(struct VERA_PSG* psg, int16_t *left, int16_t *right) uint8_t v = 0; switch (ch->waveform) { case WF_PULSE: v = (ch->phase >> 10) > ch->pw ? 0 : 63; break; - case WF_SAWTOOTH: v = ch->phase >> 11; break; - case WF_TRIANGLE: v = (ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F); break; + case WF_SAWTOOTH: v = (ch->phase >> 11) ^ (psg->chipType < 2 ? 0 : (ch->pw ^ 0x3f) & 0x3f); break; + case WF_TRIANGLE: v = ((ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F)) ^ (psg->chipType < 2 ? 0 : (ch->pw ^ 0x3f) & 0x3f); break; case WF_NOISE: v = ch->noiseval; break; } int8_t sv = (v ^ 0x20); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index e4e78933e..b6b7cf831 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -532,7 +532,7 @@ void DivPlatformVERA::poke(std::vector& wlist) { } void DivPlatformVERA::setFlags(const DivConfig& flags) { - psg->chipType=flags.getInt("chipType",1); + psg->chipType=flags.getInt("chipType",2); chipClock=25000000; CHECK_CUSTOM_CLOCK; rate=chipClock/512; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index c61415abd..3b3234934 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1647,26 +1647,34 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { chan[i].panPos+=chan[i].panRate; chan[i].panPos&=255; - // calculate... + // calculate inverted... switch (chan[i].panPos&0xc0) { case 0: // center -> right - chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2); - chan[i].panR=0xff; + chan[i].panL=((chan[i].panPos&0x3f)<<2); + chan[i].panR=0; break; case 0x40: // right -> center - chan[i].panL=(chan[i].panPos&0x3f)<<2; - chan[i].panR=0xff; + chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2); + chan[i].panR=0; break; case 0x80: // center -> left - chan[i].panL=0xff; - chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2); + chan[i].panL=0; + chan[i].panR=((chan[i].panPos&0x3f)<<2); break; case 0xc0: // left -> center - chan[i].panL=0xff; - chan[i].panR=(chan[i].panPos&0x3f)<<2; + chan[i].panL=0; + chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2); break; } + // multiply by depth + chan[i].panL=(chan[i].panL*chan[i].panDepth)/15; + chan[i].panR=(chan[i].panR*chan[i].panDepth)/15; + + // then invert it to get final panning + chan[i].panL^=0xff; + chan[i].panR^=0xff; + dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR)); } diff --git a/src/engine/song.h b/src/engine/song.h index 8912ec4a1..5976b904f 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -141,6 +141,8 @@ enum DivSystem { DIV_SYSTEM_5E01, DIV_SYSTEM_BIFURCATOR, DIV_SYSTEM_SID2, + + DIV_SYSTEM_MAX }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 093369fc2..a8e50461e 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -433,6 +433,7 @@ void DivEngine::registerSystems() { {0x25, {DIV_CMD_AY_ENVELOPE_SLIDE, _("25xx: Envelope slide up"), negEffectVal}}, {0x26, {DIV_CMD_AY_ENVELOPE_SLIDE, _("26xx: Envelope slide down")}}, {0x29, {DIV_CMD_AY_AUTO_ENVELOPE, _("29xy: Set auto-envelope (x: numerator; y: denominator)")}}, + {0x2c, {DIV_CMD_AY_AUTO_PWM, _("2Cxx: Set timer period offset (bit 7: sign)")}}, {0x2e, {DIV_CMD_AY_IO_WRITE, _("2Exx: Write to I/O port A"), constVal<0>, effectVal}}, {0x2f, {DIV_CMD_AY_IO_WRITE, _("2Fxx: Write to I/O port B"), constVal<1>, effectVal}}, }; diff --git a/src/engine/zsm.cpp b/src/engine/zsm.cpp index 87dbde8d2..75cc3d9da 100644 --- a/src/engine/zsm.cpp +++ b/src/engine/zsm.cpp @@ -133,14 +133,6 @@ void DivZSM::writePSG(unsigned char a, unsigned char v) { } else if (a>=64) { return writePCM(a-64,v); } - if (optimize) { - if ((a&3)==3 && v>64) { - // Pulse width on non-pulse waves is nonsense and wasteful - // No need to preserve state here because the next write that - // selects pulse will also set the pulse width in this register - v&=0xc0; - } - } if (psgState[psg_PREV][a]==v) { if (psgState[psg_NEW][a]!=v) { // NEW value is being reset to the same as PREV value diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 13c877186..e3093cde3 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -75,7 +75,7 @@ const char* aboutLine[]={ "Polski: freq-mod, PoznańskiSzybkowiec", "Português (Brasil): Kagamiin~", "Русский: Background2982, LTVA", - "Slovenčina: Mr. Hassium", + "Slovenčina: Wegfrei", "Svenska: RevvoBolt", "ไทย: akumanatt", "", @@ -206,6 +206,7 @@ const char* aboutLine[]={ "Ultraprogramer", "UserSniper", "Weeppiko", + "Wegfrei", "Xan", "Yuzu4K", "Zabir", diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 594554c57..b08c60532 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -36,6 +36,8 @@ static float oscDebugMax=1.0; static float oscDebugPower=1.0; static int oscDebugRepeat=1; static int numApples=1; +static int getGainChan=0; +static int getGainVol=0; static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) { if (cmd!=NULL) { @@ -721,6 +723,13 @@ void FurnaceGUI::drawDebug() { ImGui::TreePop(); } #endif + if (ImGui::TreeNode("Get Gain Test")) { + float realVol=e->getGain(getGainChan,getGainVol); + ImGui::InputInt("Chan",&getGainChan); + ImGui::InputInt("Vol",&getGainVol); + ImGui::Text("result: %.0f%%",realVol*100.0f); + ImGui::TreePop(); + } if (ImGui::TreeNode("User Interface")) { if (ImGui::Button("Inspect")) { inspectorOpen=!inspectorOpen; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index fff5a85d9..bf5b89519 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -676,6 +676,17 @@ void FurnaceGUI::doAction(int what) { latchTarget=0; latchNibble=false; break; + case GUI_ACTION_PAT_ABSORB_INSTRUMENT: { + DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false); + if (!pat) break; + for (int i=cursor.y; i>=0; i--) { + if (pat->data[i][2] >= 0) { + curIns=pat->data[i][2]; + break; + } + } + break; + } case GUI_ACTION_INS_LIST_ADD: if (settings.insTypeMenu) { diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 3eba0298c..c492a8e17 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -130,7 +130,7 @@ const bool mobileButtonPersist[32]={ // page 1 false, false, - false, + true, false, true, true, diff --git a/src/gui/exportOptions.cpp b/src/gui/exportOptions.cpp index 4b4360839..bb223aac4 100644 --- a/src/gui/exportOptions.cpp +++ b/src/gui/exportOptions.cpp @@ -239,6 +239,107 @@ void FurnaceGUI::drawExportVGM(bool onWindow) { } } +void FurnaceGUI::drawExportROM(bool onWindow) { + exitDisabledTimer=1; + + const DivROMExportDef* def=e->getROMExportDef(romTarget); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##ROMTarget",def==NULL?"