Merge branch 'master' of https://github.com/tildearrow/furnace into ymf278b
This commit is contained in:
commit
7fb7d32bd5
|
@ -682,6 +682,14 @@ src/engine/fileOps/text.cpp
|
||||||
src/engine/fileOps/tfm.cpp
|
src/engine/fileOps/tfm.cpp
|
||||||
src/engine/fileOps/xm.cpp
|
src/engine/fileOps/xm.cpp
|
||||||
|
|
||||||
|
src/engine/fileOps/p.cpp
|
||||||
|
src/engine/fileOps/p86.cpp
|
||||||
|
src/engine/fileOps/pdx.cpp
|
||||||
|
src/engine/fileOps/ppc.cpp
|
||||||
|
src/engine/fileOps/pps.cpp
|
||||||
|
src/engine/fileOps/pvi.cpp
|
||||||
|
src/engine/fileOps/pzi.cpp
|
||||||
|
|
||||||
src/engine/blip_buf.c
|
src/engine/blip_buf.c
|
||||||
src/engine/brrUtils.c
|
src/engine/brrUtils.c
|
||||||
src/engine/safeReader.cpp
|
src/engine/safeReader.cpp
|
||||||
|
|
BIN
demos/arcade/U.N. Owen Was Her (NS2).fur
Normal file
BIN
demos/arcade/U.N. Owen Was Her (NS2).fur
Normal file
Binary file not shown.
|
@ -18,30 +18,31 @@ the default layout of Furnace is depicted below.
|
||||||
- [play/edit controls](play-edit-controls.md)
|
- [play/edit controls](play-edit-controls.md)
|
||||||
- [instrument/wavetable/sample list](asset-list.md)
|
- [instrument/wavetable/sample list](asset-list.md)
|
||||||
- [song information](song-info.md)
|
- [song information](song-info.md)
|
||||||
- [effect list window](effect-list-window.md)
|
|
||||||
- [pattern view](../3-pattern/README.md)
|
- [pattern view](../3-pattern/README.md)
|
||||||
|
- [effect list window](effect-list-window.md)
|
||||||
- [instrument editor](../4-instrument/README.md)
|
- [instrument editor](../4-instrument/README.md)
|
||||||
- [wavetable editor](../5-wave/README.md)
|
- [wavetable editor](../5-wave/README.md)
|
||||||
- [sample editor](../6-sample/README.md)
|
- [sample editor](../6-sample/README.md)
|
||||||
|
|
||||||
## advanced topics
|
## 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)
|
- [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](../8-advanced/osc.md)
|
||||||
- [oscilloscope (per channel)](../8-advanced/chanosc.md)
|
- [oscilloscope (per channel)](../8-advanced/chanosc.md)
|
||||||
|
- [oscilloscope (X-Y)](../8-advanced/xyosc.md)
|
||||||
- [clock](../8-advanced/clock.md)
|
- [clock](../8-advanced/clock.md)
|
||||||
- [register view](../8-advanced/regview.md)
|
- [grooves](../8-advanced/grooves.md)
|
||||||
- [log viewer](../8-advanced/log-viewer.md)
|
- [log viewer](../8-advanced/log-viewer.md)
|
||||||
|
- [register view](../8-advanced/regview.md)
|
||||||
- [statistics](../8-advanced/stats.md)
|
- [statistics](../8-advanced/stats.md)
|
||||||
|
- [memory composition](../8-advanced/memory-composition.md)
|
||||||
|
|
||||||
## other topics
|
## other topics
|
||||||
|
|
||||||
- [basic mode](basic-mode.md)
|
- [piano/input pad](../8-advanced/piano.md)
|
||||||
- [settings](settings.md)
|
- [settings](settings.md)
|
||||||
|
|
|
@ -2,25 +2,27 @@
|
||||||
|
|
||||||
Furnace allows you to export your song in several formats. this section deals with describing the available export options.
|
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.
|
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.
|
- **Loops**: sets the number of times the song will loop.
|
||||||
- does not have effect if the song ends with `FFxx` effect.
|
- does not have effect if the song ends with `FFxx` effect.
|
||||||
- **Fade out (seconds)**: sets the fade out time when the song is over.
|
- **Fade out (seconds)**: sets the fade out time when the song is over.
|
||||||
- does not have effect if the song ends with `FFxx` effect.
|
- does not have effect if the song ends with `FFxx` effect.
|
||||||
|
|
||||||
and three export choices:
|
## VGM
|
||||||
|
|
||||||
- **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
|
|
||||||
|
|
||||||
this option allows exporting to a VGM (Video Game Music) file. these can be played back with VGMPlay (for example).
|
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.
|
- **custom**: allows you to specify how many ticks to add.
|
||||||
- `0` is effectively none, disabling loop trail completely.
|
- `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.
|
- 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.
|
- **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 ...`
|
- 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
|
- `ll`: length, a 32-bit little-endian number
|
||||||
|
@ -51,14 +50,11 @@ the following settings exist:
|
||||||
- `pp`: pattern index (one per channel)
|
- `pp`: pattern index (one per channel)
|
||||||
- **direct stream mode**: this option allows DualPCM to work. don't use this for other chips.
|
- **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.
|
- 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.
|
## ZSM
|
||||||
|
|
||||||
## export text
|
|
||||||
|
|
||||||
this option allows you to export your song as a text file.
|
|
||||||
|
|
||||||
## export ZSM
|
|
||||||
|
|
||||||
ZSM (ZSound Music) is a format designed for the Commander X16 to allow hardware playback.
|
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.
|
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.
|
- **loop**: enables loop. if disabled, the song won't loop.
|
||||||
- **optimize size**: removes unnecessary commands to reduce size.
|
- **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.
|
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)
|
||||||
- Sega Genesis/Mega Drive (YM2612 + SN76489, extended channel 3)
|
- Sega Genesis/Mega Drive (YM2612 + SN76489, extended channel 3)
|
||||||
- Sega Master System
|
- Sega Master System
|
||||||
|
@ -97,7 +93,7 @@ the following systems are supported when saving as 1.0/legacy:
|
||||||
- Arcade (YM2151 + SegaPCM 5-channel compatibility)
|
- Arcade (YM2151 + SegaPCM 5-channel compatibility)
|
||||||
- Neo Geo CD (DefleMask 1.0+)
|
- 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)
|
- Sega Master System (with FM expansion)
|
||||||
- NES + Konami VRC7
|
- NES + Konami VRC7
|
||||||
- Famicom Disk System
|
- Famicom Disk System
|
||||||
|
|
|
@ -39,7 +39,7 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
||||||
| Panic | `F12` |
|
| Panic | `F12` |
|
||||||
| | |
|
| | |
|
||||||
| **Window activation** | |
|
| **Window activation** | |
|
||||||
| Find/Replace | Ctrl-F |
|
| Find/Replace | `Ctrl-F` |
|
||||||
| Settings | — |
|
| Settings | — |
|
||||||
| Song Information | — |
|
| Song Information | — |
|
||||||
| Subsongs | — |
|
| Subsongs | — |
|
||||||
|
@ -63,16 +63,23 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
||||||
| Piano | — |
|
| Piano | — |
|
||||||
| Oscilloscope (master) | — |
|
| Oscilloscope (master) | — |
|
||||||
| Oscilloscope (per-channel) | — |
|
| Oscilloscope (per-channel) | — |
|
||||||
|
| Oscilloscope (X-Y) | — |
|
||||||
| Volume Meter | — |
|
| Volume Meter | — |
|
||||||
| Clock | — |
|
| Clock | — |
|
||||||
| Register View | — |
|
| Register View | — |
|
||||||
| Log Viewer | — |
|
| Log Viewer | — |
|
||||||
| Statistics | — |
|
| Statistics | — |
|
||||||
|
| Memory Composition | — |
|
||||||
| Effect List | — |
|
| Effect List | — |
|
||||||
| Debug Menu | `Ctrl-Shift-D` |
|
| Debug Menu | `Ctrl-Shift-D` |
|
||||||
|
| Command Stream Player | — |
|
||||||
| About | — |
|
| About | — |
|
||||||
| Collapse/expand current window | — |
|
| Collapse/expand current window | — |
|
||||||
| Close current window | `Shift-Escape` |
|
| Close current window | `Shift-Escape` |
|
||||||
|
| Command Palette | `Ctrl-P` |
|
||||||
|
| Recent files (Palette) | — |
|
||||||
|
| Insstruments (Palette) | — |
|
||||||
|
| Samples (Palette) | — |
|
||||||
| | |
|
| | |
|
||||||
| **Note input** | |
|
| **Note input** | |
|
||||||
| _see "note input" section after table_ | |
|
| _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 down by one (override Edit Step) | `Shift-End` |
|
||||||
| Move cursor to previous channel | — |
|
| Move cursor to previous channel | — |
|
||||||
| Move cursor to next channel | — |
|
| Move cursor to next channel | — |
|
||||||
| Move cursor to next channel (overflow) | — |
|
|
||||||
| Move cursor to previous channel (overflow) | — |
|
| Move cursor to previous channel (overflow) | — |
|
||||||
|
| Move cursor to next channel (overflow) | — |
|
||||||
| Move cursor to beginning of pattern | `Home` |
|
| Move cursor to beginning of pattern | `Home` |
|
||||||
| Move cursor to end of pattern | `End` |
|
| Move cursor to end of pattern | `End` |
|
||||||
| Move cursor up (coarse) | `PageUp` |
|
| 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 to end of pattern | — |
|
||||||
| Expand selection upwards (coarse) | `Shift-PageUp` |
|
| Expand selection upwards (coarse) | `Shift-PageUp` |
|
||||||
| Expand selection downwards (coarse) | `Shift-PageDown` |
|
| 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` |
|
| Delete | `Delete` |
|
||||||
| Pull delete | `Backspace` |
|
| Pull delete | `Backspace` |
|
||||||
| Insert | `Insert` |
|
| 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 | — |
|
| Clear note input latch | — |
|
||||||
| | |
|
| | |
|
||||||
| **Instrument list** | |
|
| **Instrument list** | |
|
||||||
| Add | `Insert` |
|
| Add instrument | `Insert` |
|
||||||
| Duplicate | `Ctrl-D` |
|
| Duplicate instrument | `Ctrl-D` |
|
||||||
| Open | — |
|
| Open instrument | — |
|
||||||
| Open (replace current) | — |
|
| Open instrument (replace current) | — |
|
||||||
| Save | — |
|
| Save instrument | — |
|
||||||
| Save (.dmp) | — |
|
| Save instrument (.dmp) | — |
|
||||||
| Move up | `Shift-Up` |
|
| Move instrument up in list | `Shift-Up` |
|
||||||
| Move down | `Shift-Down` |
|
| Move instrument down in list | `Shift-Down` |
|
||||||
| Delete | — |
|
| Delete instrument | — |
|
||||||
| Edit | `Shift-Return` |
|
| Edit instrument | `Shift-Return` |
|
||||||
| Cursor up | `Up` |
|
| Instrument cursor up | `Up` |
|
||||||
| Cursor down | `Down` |
|
| Instrument cursor down | `Down` |
|
||||||
| Toggle folders/standard view | `Ctrl-V` |
|
| Instruments: toggle folders/standard view | `Ctrl-V` |
|
||||||
| | |
|
| | |
|
||||||
| **Wavetable list** | |
|
| **Wavetable list** | |
|
||||||
| Add | `Insert` |
|
| Add wavetable | `Insert` |
|
||||||
| Duplicate | `Ctrl-D` |
|
| Duplicate wavetable | `Ctrl-D` |
|
||||||
| Open | — |
|
| Open wavetable | — |
|
||||||
| Open (replace current) | — |
|
| Open wavetable (replace current) | — |
|
||||||
| Save | — |
|
| Save wavetable | — |
|
||||||
| Save (.dmw) | — |
|
| Save wavetable (.dmw) | — |
|
||||||
| Save (raw) | — |
|
| Save wavetable (raw) | — |
|
||||||
| Move up | `Shift-Up` |
|
| Move wavetable up in list | `Shift-Up` |
|
||||||
| Move down | `Shift-Down` |
|
| Move wavetable down in list | `Shift-Down` |
|
||||||
| Delete | — |
|
| Delete wavetable | — |
|
||||||
| Edit | `Shift-Return` |
|
| Edit wavetable | `Shift-Return` |
|
||||||
| Cursor up | `Up` |
|
| Wavetable cursor up | `Up` |
|
||||||
| Cursor down | `Down` |
|
| Wavetable cursor down | `Down` |
|
||||||
| Toggle folders/standard view | `Ctrl-V` |
|
| Wavetables: toggle folders/standard view | `Ctrl-V` |
|
||||||
| | |
|
| | |
|
||||||
| **Sample list** | |
|
| **Sample list** | |
|
||||||
| Add | `Insert` |
|
| Add sample | `Insert` |
|
||||||
| Duplicate | `Ctrl-D` |
|
| Duplicate sample | `Ctrl-D` |
|
||||||
| Create wavetable from selection | `Ctrl-W` |
|
| Sample Editor: Create wavetable from selection | `Ctrl-W` |
|
||||||
| Open | — |
|
| Open sample | — |
|
||||||
| Open (replace current) | — |
|
| Open sample (replace current) | — |
|
||||||
| Import raw data | — |
|
| Import raw sample data | — |
|
||||||
| Import raw data (replace current) | — |
|
| Import raw sample data (replace current) | — |
|
||||||
| Save | — |
|
| Save sample | — |
|
||||||
| Save (raw) | — |
|
| Save sample (raw) | — |
|
||||||
| Move up | `Shift-Up` |
|
| Move sample up in list | `Shift-Up` |
|
||||||
| Move down | `Shift-Down` |
|
| Move sample down in list | `Shift-Down` |
|
||||||
| Delete | — |
|
| Delete sample | — |
|
||||||
| Edit | `Shift-Return` |
|
| Edit sample | `Shift-Return` |
|
||||||
| Cursor up | `Up` |
|
| Sample cursor up | `Up` |
|
||||||
| Cursor down | `Down` |
|
| Sample cursor down | `Down` |
|
||||||
| Preview | — |
|
| Sample Preview | — |
|
||||||
| Stop preview | — |
|
| Stop sample preview | — |
|
||||||
| Toggle folders/standard view | `Ctrl-V` |
|
| Samples: Toggle folders/standard view | `Ctrl-V` |
|
||||||
|
| Samples: Make me a drum kit | — |
|
||||||
| | |
|
| | |
|
||||||
| **Orders** | |
|
| **Orders** | |
|
||||||
| Previous order | `Up` |
|
| Previous order | `Up` |
|
||||||
| Next order | `Down` |
|
| Next order | `Down` |
|
||||||
| Cursor left | `Left` |
|
| Order cursor left | `Left` |
|
||||||
| Cursor right | `Right` |
|
| Order cursor right | `Right` |
|
||||||
| Increase value | — |
|
| Increase order value | — |
|
||||||
| Decrease value | — |
|
| Decrease order value | — |
|
||||||
| Switch edit mode | — |
|
| Switch order edit mode | — |
|
||||||
| Toggle alter entire row | `Ctrl-L` |
|
| Order: Toggle alter entire row | `Ctrl-L` |
|
||||||
| Add | `Insert` |
|
| Add order | `Insert` |
|
||||||
| Duplicate | `Ctrl-D` |
|
| Duplicate order | `Ctrl-D` |
|
||||||
| Deep clone | `Ctrl-Shift-D` |
|
| Deep clone order | `Ctrl-Shift-D` |
|
||||||
| Duplicate to end of song | `Ctrl-E` |
|
| Copy current order to end of song | `Ctrl-E` |
|
||||||
| Deep clone to end of song | `Ctrl-Shift-E` |
|
| Deep clone current order to end of song | `Ctrl-Shift-E` |
|
||||||
| Remove | `Delete` |
|
| Remove order | `Delete` |
|
||||||
| Move up | `Shift-Up` |
|
| Move order up | `Shift-Up` |
|
||||||
| Move down | `Shift-Down` |
|
| Move order down | `Shift-Down` |
|
||||||
| Replay | — |
|
| Replay order | — |
|
||||||
| | |
|
| | |
|
||||||
| **Sample editor** | |
|
| **Sample editor** | |
|
||||||
| Edit mode: Select | `Shift-I` |
|
| Sample editor mode: Select | `Shift-I` |
|
||||||
| Edit mode: Draw | `Shift-D` |
|
| Sample editor mode: Draw | `Shift-D` |
|
||||||
| Cut | `Ctrl-X` |
|
| Sample editor: Cut | `Ctrl-X` |
|
||||||
| Copy | `Ctrl-C` |
|
| Sample editor: Copy | `Ctrl-C` |
|
||||||
| Paste | `Ctrl-V` |
|
| Sample editor: Paste | `Ctrl-V` |
|
||||||
| Paste replace | `Ctrl-Shift-V` |
|
| Sample editor: Paste replace | `Ctrl-Shift-V` |
|
||||||
| Paste mix | `Ctrl-Alt-V` |
|
| Sample editor: Paste mix | `Ctrl-Alt-V` |
|
||||||
| Select all | `Ctrl-A` |
|
| Sample editor: Select all | `Ctrl-A` |
|
||||||
| Resize | `Ctrl-R` |
|
| Sample editor: Resize | `Ctrl-R` |
|
||||||
| Resample | `Ctrl-E` |
|
| Sample editor: Resample | `Ctrl-E` |
|
||||||
| Amplify | `Ctrl-B` |
|
| Sample editor: Amplify | `Ctrl-B` |
|
||||||
| Normalize | `Ctrl-N` |
|
| Sample editor: Normalize | `Ctrl-N` |
|
||||||
| Fade in | `Ctrl-I` |
|
| Sample editor: Fade in | `Ctrl-I` |
|
||||||
| Fade out | `Ctrl-O` |
|
| Sample editor: Fade out | `Ctrl-O` |
|
||||||
| Insert silence | `Insert` |
|
| Sample editor: Insert silence | `Insert` |
|
||||||
| Apply silence | `Shift-Delete` |
|
| Sample editor: Apply silence | `Shift-Delete` |
|
||||||
| Delete | `Delete` |
|
| Sample editor: Delete | `Delete` |
|
||||||
| Trim | `Ctrl-Delete` |
|
| Sample editor: Trim | `Ctrl-Delete` |
|
||||||
| Reverse | `Ctrl-T` |
|
| Sample editor: Reverse | `Ctrl-T` |
|
||||||
| Invert | `Ctrl-Shift-T` |
|
| Sample editor: Invert | `Ctrl-Shift-T` |
|
||||||
| Signed/unsigned exchange | `Ctrl-U` |
|
| Sample editor: Signed/unsigned exchange | `Ctrl-U` |
|
||||||
| Apply filter | `Ctrl-F` |
|
| Sample editor: Apply filter | `Ctrl-F` |
|
||||||
| Preview sample | — |
|
| Sample editor: Preview sample | — |
|
||||||
| Stop sample preview | — |
|
| Sample editor: Stop sample preview | — |
|
||||||
| Zoom in | `Ctrl-=` |
|
| Sample editor: Zoom in | `Ctrl-=` |
|
||||||
| Zoom out | `Ctrl--` |
|
| Sample editor: Zoom out | `Ctrl--` |
|
||||||
| Toggle auto-zoom | `Ctrl-0` |
|
| Sample editor: Toggle auto-zoom | `Ctrl-0` |
|
||||||
| Create instrument from sample | — |
|
| Sample editor: Create instrument from sample | — |
|
||||||
| Set loop to selection | `Ctrl-L` |
|
| Sample editor: Set loop to selection | `Ctrl-L` |
|
||||||
|
|
|
@ -2,24 +2,20 @@
|
||||||
|
|
||||||
the menu bar allows you to select from five menus: file, edit, settings, window and help.
|
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
|
## 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.
|
- **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.
|
- 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.
|
- **open recent**: contains a list of the songs you've opened before.
|
||||||
- **clear history**: erases the file history.
|
- **clear history**: erases the file history.
|
||||||
|
|
||||||
- **save**: saves the current song.
|
- **save**: saves the current song.
|
||||||
- opens the file picker if this is a new song, or a backup.
|
- 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.
|
- **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.
|
- **manage chips**: opens the [Chip Manager](../8-advanced/chip-manager.md) dialog.
|
||||||
|
|
||||||
- **restore backup**: restores a previously saved backup.
|
- **restore backup**: restores a previously saved backup.
|
||||||
- Furnace keeps up to 5 backups of a song.
|
- Furnace keeps up to 5 backups of a song.
|
||||||
- the backup directory is located in:
|
- 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`
|
- Linux/other: `~/.config/furnace/backups`
|
||||||
- this directory grows in size as you use Furnace. remember to delete old backups periodically to save space.
|
- 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.
|
- **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.
|
- **exit**: closes Furnace.
|
||||||
|
|
||||||
## edit
|
## edit
|
||||||
|
|
||||||
- **...**: does nothing except prevent accidental clicks on later menu items if the menu is too tall to fit on the program window.
|
- **...**: 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.
|
- **undo**: reverts the last action.
|
||||||
- **redo**: repeats what you undid previously.
|
- **redo**: repeats what you undid previously.
|
||||||
|
|
||||||
- **cut**: moves the current selection in the pattern view to clipboard.
|
- **cut**: moves the current selection in the pattern view to clipboard.
|
||||||
- **copy**: copies the current selection in the pattern view to clipboard.
|
- **copy**: copies the current selection in the pattern view to clipboard.
|
||||||
- **paste**: inserts the clipboard's contents in the cursor position.
|
- **paste**: inserts the clipboard's contents in the cursor position.
|
||||||
- you may be able to paste from OpenMPT as well.
|
- 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**: 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 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.
|
- **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 the selection is tall, it will select the entire column.
|
||||||
- if a column is already selected, it will select the entire channel.
|
- if a column is already selected, it will select the entire channel.
|
||||||
- if a channel is already selected, it will select the entire pattern.
|
- 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)
|
||||||
- _**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)
|
||||||
- _**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.
|
- **note/octave up/down**: transposes notes in the current selection.
|
||||||
|
|
||||||
- **values up/down**: changes values in the current selection by ±1 or ±16.
|
- **values up/down**: changes values in the current selection by ±1 or ±16.
|
||||||
|
|
||||||
- **transpose**: transpose notes or change values by a specific amount.
|
- **transpose**: transpose notes or change values by a specific amount.
|
||||||
|
|
||||||
- **interpolate**: fills in gaps in the selection by interpolation between values.
|
- **interpolate**: fills in gaps in the selection by interpolation between values.
|
||||||
- **change instrument**: changes the instrument number in a selection.
|
- **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.
|
- **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.
|
- 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).
|
- **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).
|
- 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.
|
- use to change volume in a selection for example.
|
||||||
- **randomize**: replaces the selection with random values.
|
- **randomize**: replaces the selection with random values.
|
||||||
- does not affect the note column.
|
- 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.
|
- **invert values**: `00` becomes `FF`, `01` becomes `FE`, `02` becomes `FD` and so on.
|
||||||
|
|
||||||
- **flip selection**: flips the selection so it is backwards.
|
- **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/expand amount**: allows you to specify how much to collapse/expand in the next two menu items.
|
||||||
- **collapse**: shrinks the selected contents.
|
- **collapse**: shrinks the selected contents.
|
||||||
- **expand**: expands the selected contents.
|
- **expand**: expands the selected contents.
|
||||||
|
|
||||||
- **collapse pattern**: same as collapse, but affects the entire pattern.
|
- **collapse pattern**: same as collapse, but affects the entire pattern.
|
||||||
- **expand pattern**: same as expand, 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.
|
- **collapse song**: same as collapse, but affects the entire song.
|
||||||
- it also changes speeds and pattern length to compensate.
|
- it also changes speeds and pattern length to compensate.
|
||||||
- **expand song**: same as expand, but affects the entire song.
|
- **expand song**: same as expand, but affects the entire song.
|
||||||
- it also changes speeds and pattern length to compensate.
|
- it also changes speeds and pattern length to compensate.
|
||||||
|
- **find/replace**: shows [the Find/Replace window](../8-advanced/find-replace.md).
|
||||||
- _**find/replace**:_ shows [the Find/Replace window](../8-advanced/find-replace.md).
|
- **clear...**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like.
|
||||||
|
|
||||||
- **clear**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like.
|
|
||||||
|
|
||||||
## settings
|
## settings
|
||||||
|
|
||||||
- **full screen**: expands the Furnace window so it covers your screen.
|
- **full screen**: expands the Furnace window so it covers your screen.
|
||||||
- **lock layout**: prevents you from dragging/resizing docked windows, or docking more.
|
- **lock layout**: prevents you from dragging/resizing docked windows, or docking more.
|
||||||
- **basic mode**: toggles [Basic Mode](basic-mode.md).
|
- **pattern visualizer**: toggles pattern view particle effects when the song plays.
|
||||||
- **visualizer**: toggles pattern view particle effects when the song plays.
|
|
||||||
- **reset layout**: resets the workspace to its defaults.
|
- **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
|
## window
|
||||||
|
|
||||||
all these menu items show or hide their associated windows.
|
all these menu items show or hide their associated windows.
|
||||||
|
|
||||||
- [song information](song-info.md)
|
- song
|
||||||
- [subsongs](song-info.md)
|
- **[song comments](../8-advanced/comments.md)**
|
||||||
- [speed](song-info.md)
|
- **[song information](song-info.md)**
|
||||||
- [instruments](../4-instrument/README.md)
|
- **[subsongs](song-info.md)**
|
||||||
- [wavetables](../5-wave/README.md)
|
- **[channels](../8-advanced/channels.md)**
|
||||||
- [samples](../6-sample/README.md)
|
- **[chip manager](../8-advanced/chip-manager.md)**
|
||||||
- [orders](order-list.md)
|
- **[orders](order-list.md)**
|
||||||
- [pattern](../3-pattern/README.md)
|
- **[pattern](../3-pattern/README.md)**
|
||||||
- _[mixer](../8-advanced/mixer.md)_
|
- **[pattern manager](../8-advanced/pat-manager.md)**
|
||||||
- _[grooves](../8-advanced/grooves.md)_
|
- **[mixer](../8-advanced/mixer.md)**
|
||||||
- _[channels](../8-advanced/channels.md)_
|
- **[compatibility flags](../8-advanced/compat-flags.md)**
|
||||||
- _[pattern manager](../8-advanced/pat-manager.md)_
|
- assets
|
||||||
- _[chip manager](../8-advanced/chip-manager.md)_
|
- **[instruments](../4-instrument/README.md)**
|
||||||
- _[compatibility flags](../8-advanced/compat-flags.md)_
|
- **[samples](../6-sample/README.md)**
|
||||||
- [song comments](../8-advanced/comments.md)
|
- **[wavetables](../5-wave/README.md)**
|
||||||
|
- **[instrument editor](../4-instrument/README.md)**
|
||||||
- [piano](../8-advanced/piano.md)
|
- **[sample editor](../6-sample/README.md)**
|
||||||
- [oscilloscope](../8-advanced/osc.md)
|
- **[wavetable editor](../5-wave/README.md)**
|
||||||
- [oscilloscopes (per-channel)](../8-advanced/chanosc.md)
|
- visualizers
|
||||||
- [clock](../8-advanced/clock.md)
|
- **[oscilloscope](../8-advanced/osc.md)**
|
||||||
- [register view](../8-advanced/regview.md)
|
- **[oscilloscope (per-channel)](../8-advanced/chanosc.md)**
|
||||||
- [log viewer](../8-advanced/log-viewer.md)
|
- **[oscilloscope (X-Y)](../8-advanced/xyosc.md)**
|
||||||
- [stats](../8-advanced/stats.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
|
## help
|
||||||
|
|
||||||
|
|
|
@ -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).
|
- **Poly**: turns on polyphony for previewing notes. toggles to **Mono** for monophony (one note at a time only).
|
||||||
- **Octave**: sets current input octave.
|
- **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.
|
- **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 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.
|
- **Follow pattern**: if on, the cursor will follow playback and the song will scroll by as it plays.
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
# settings
|
# 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
|
## General
|
||||||
|
|
||||||
### Program
|
### 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:
|
- **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 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 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.
|
- 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!
|
- 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.
|
- **VSync**: synchronizes rendering to VBlank and eliminates tearing.
|
||||||
- **Frame rate limit**: allows you to set a frame rate limit (in frames per second).
|
- **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).
|
- 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.
|
- **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.
|
- **Power-saving mode**: saves power by lowering the frame rate to 2fps when idle.
|
||||||
- may cause issues under Mesa drivers!
|
- 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**
|
- **Remember last values**
|
||||||
- **Store instrument name in .fui**: when enabled, saving an instrument will store its name. this may increase file size.
|
- **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.
|
- **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
|
### 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).
|
- **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
|
## Audio
|
||||||
|
|
||||||
### Output
|
### Output
|
||||||
|
@ -100,9 +114,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
||||||
- **Sample rate**: audio output rate.
|
- **Sample rate**: audio output rate.
|
||||||
- a lower rate decreases quality and isn't really beneficial.
|
- a lower rate decreases quality and isn't really beneficial.
|
||||||
- if using PortAudio backend, be careful about this value.
|
- if using PortAudio backend, be careful about this value.
|
||||||
- **Outputs**: number of audio outputs created, up to 16.
|
- **Outputs**: number of audio outputs created, up to 16. default is 2 (stereo).
|
||||||
- only appears when Backend is JACK.
|
|
||||||
- **Channels**: mono, stereo or something.
|
|
||||||
- **Buffer size**: size of buffer in both samples and milliseconds.
|
- **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 low value may cause stuttering/glitches in playback (known as "underruns" or "xruns").
|
||||||
- setting this to a high value increases latency.
|
- 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
|
||||||
|
|
||||||
- **MIDI input**: input device.
|
- **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.
|
- **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.
|
- **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.
|
- **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:
|
- **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 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`.
|
- 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:**
|
- **Effect input behavior:**
|
||||||
- **Move down**: after entering an effect (or effect value), the cursor moves down.
|
- **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.
|
- **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.
|
- **Pattern font** font for the pattern view, the order list, and related.
|
||||||
- if "Custom...", a file path selector will appear.
|
- if "Custom...", a file path selector will appear.
|
||||||
- **Size**: font size.
|
- **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
|
#### FreeType-specific settings
|
||||||
|
|
||||||
- **Anti-aliased fonts**: when enabled, fonts will be rendered smooth.
|
- **Anti-aliased fonts**: when enabled, fonts will be rendered smooth.
|
||||||
- **Support bitmap fonts**: this option allows you to enable the loading of bitmap fonts.
|
- **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!
|
- 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.
|
- **Disable**: only rely upon font hinting data.
|
||||||
- **Enable**: prefer font hinting data if present.
|
- **Enable**: prefer font hinting data if present.
|
||||||
- **Force**: ignore font hinting data.
|
- **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
|
### 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**
|
- **/path/to/file.fur - Furnace**
|
||||||
- **Display system name on title bar**
|
- **Display system name on title bar**
|
||||||
- **Display chip names instead of "multi-system" in 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:**
|
- **Status bar:**
|
||||||
- **Cursor details**
|
- **Cursor details**
|
||||||
- **File path**
|
- **File path**
|
||||||
- **Cursor details or file path**
|
- **Cursor details or file path**
|
||||||
- **Nothing**
|
- **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**
|
- **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.
|
- **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 window corners**
|
||||||
- **Rounded buttons**
|
- **Rounded buttons**
|
||||||
- **Rounded menu corners**
|
- **Rounded menu corners**
|
||||||
|
- **Rounded tabs**
|
||||||
|
- **Rounded scrollbars**
|
||||||
- **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like.
|
- **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Color
|
## Color
|
||||||
|
|
||||||
### Color scheme
|
### Color scheme
|
||||||
|
@ -544,9 +562,28 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
||||||
- **Export**
|
- **Export**
|
||||||
- **Reset defaults**
|
- **Reset defaults**
|
||||||
- **Guru mode**: exposes all color options (instead of accent colors).
|
- **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:**
|
- **Color scheme type:**
|
||||||
- **Dark**
|
- **Dark**
|
||||||
- **Light**
|
- **Light**
|
||||||
- **Frame shading**: applies a gradient effect to buttons and input boxes.
|
- **Accent colors**: select main interface colors.
|
||||||
|
- **Primary**
|
||||||
|
- **Secondary**
|
||||||
- several more categories...
|
- 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.
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
- **Name**: the track's title.
|
- **Name**: the track's title.
|
||||||
- **Author**: the author(s) of this track.
|
- **Author**: the author(s) of this track.
|
||||||
- **Album**: the associated album name (or the name of the game the song is from).
|
- **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.
|
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
|
## 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.
|
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.
|
**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).
|
- 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.
|
**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.
|
- 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 tick rate to 150 BPM (60 Hz) and speed to 6.
|
||||||
- set the first virtual tempo number (numerator) to 200.
|
- set the first virtual tempo number (numerator) to 200.
|
||||||
- set the second virtual tempo number (denominator) to 150.
|
- 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.
|
- 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.
|
- 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:
|
**Highlight**: sets the pattern row highlights:
|
||||||
- the first value represents the number of rows per beat.
|
- 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.)
|
**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.
|
||||||
|
|
|
@ -119,6 +119,10 @@ Shift-Up | expand selection upwards
|
||||||
Shift-Down | expand selection downwards
|
Shift-Down | expand selection downwards
|
||||||
Shift-Left | expand selection to the left
|
Shift-Left | expand selection to the left
|
||||||
Shift-Right | expand selection to the right
|
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)
|
Backspace | delete note at cursor and/or pull pattern upwards (configurable)
|
||||||
Delete | delete selection
|
Delete | delete selection
|
||||||
Insert | create blank row at cursor position and push pattern
|
Insert | create blank row at cursor position and push pattern
|
||||||
|
|
|
@ -89,6 +89,7 @@ in there, you can modify certain data pertaining to your sample, such as the:
|
||||||
- **Open**: replaces current sample.
|
- **Open**: replaces current sample.
|
||||||
- right-clicking brings up a menu:
|
- 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...**: 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.
|
- **Save**: saves current sample to disk.
|
||||||
- right-clicking brings up a menu:
|
- right-clicking brings up a menu:
|
||||||
- **save raw...**: brings up a file selector, then saves the sample as raw data.
|
- **save raw...**: brings up a file selector, then saves the sample as raw data.
|
||||||
|
|
|
@ -15,11 +15,11 @@ it features echo and up to 16 voices.
|
||||||
|
|
||||||
- `10xx`: **change wave.**
|
- `10xx`: **change wave.**
|
||||||
- `11xy`: **configure echo.**
|
- `11xy`: **configure echo.**
|
||||||
- this effect is kinda odd. this is how it works:
|
- 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.
|
||||||
> How do you echo on GBA
|
- put `110x` in the effect column.
|
||||||
>
|
- set volume column to set feedback.
|
||||||
> 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
|
- don't use the channel for anything else.
|
||||||
|
|
||||||
- `12xy`: **toggle invert.**
|
- `12xy`: **toggle invert.**
|
||||||
- `x` left channel.
|
- `x` left channel.
|
||||||
|
|
|
@ -59,7 +59,7 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i
|
||||||
- `00` to `7F` for 0 to 127.
|
- `00` to `7F` for 0 to 127.
|
||||||
- `80` to `FF` for -128 to -1.
|
- `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.
|
- 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
|
## 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 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
|
## channel status
|
||||||
|
|
||||||
the following icons are displayed when channel status is enabled in the pattern view:
|
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.
|
- **Feedback**: sets how much of the echo output will be fed back into the buffer.
|
||||||
- **Echo volume**: sets echo volume.
|
- **Echo volume**: sets echo volume.
|
||||||
- **Echo filter**: adjusts echo filter.
|
- **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
|
## ADSR
|
||||||
|
|
||||||
|
|
|
@ -8,22 +8,27 @@ as listed in the "Edit" menu:
|
||||||
|
|
||||||
as listed in the "Window" menu:
|
as listed in the "Window" menu:
|
||||||
|
|
||||||
- [mixer](mixer.md)
|
- song
|
||||||
- [grooves](grooves.md)
|
- [song comments](../8-advanced/comments.md)
|
||||||
- [channel manager](channels.md)
|
- [channels](../8-advanced/channels.md)
|
||||||
- [pattern manager](pat-manager.md)
|
- [chip manager](../8-advanced/chip-manager.md)
|
||||||
- [chip manager](chip-manager.md)
|
- [pattern manager](../8-advanced/pat-manager.md)
|
||||||
- [compatibility flags](compat-flags.md)
|
- [mixer](../8-advanced/mixer.md)
|
||||||
- [song comments](comments.md)
|
- [compatibility flags](../8-advanced/compat-flags.md)
|
||||||
|
- visualizers
|
||||||
- [piano](piano.md)
|
- [oscilloscope](../8-advanced/osc.md)
|
||||||
- [oscilloscope](osc.md)
|
- [oscilloscope (per-channel)](../8-advanced/chanosc.md)
|
||||||
- [oscilloscope (X-Y)](xyosc.md)
|
- [oscilloscope (X-Y)](../8-advanced/xyosc.md)
|
||||||
- [oscilloscopes (per-channel)](chanosc.md)
|
- volume meter
|
||||||
- [clock](clock.md)
|
- tempo
|
||||||
- [register view](regview.md)
|
- [clock](../8-advanced/clock.md)
|
||||||
- [log viewer](log-viewer.md)
|
- [grooves](../8-advanced/grooves.md)
|
||||||
- [stats](stats.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:
|
other:
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,9 @@ the "Channels" dialog allows manipulation of the song's channels.
|
||||||

|

|
||||||
|
|
||||||
each channel has the following options:
|
each channel has the following options:
|
||||||
- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept.
|
- **Pat**: 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.
|
- **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.
|
- 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.
|
- **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.
|
- the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed.
|
||||||
|
|
|
@ -14,6 +14,7 @@ right-clicking the view will display the configuration view shown above:
|
||||||
- **Mode 2**: bias slightly toward more columns.
|
- **Mode 2**: bias slightly toward more columns.
|
||||||
- **Mode 3**: always more columns than rows.
|
- **Mode 3**: always more columns than rows.
|
||||||
- **Amplitude**: scales amplitude for all oscilloscope views.
|
- **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.
|
- **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:
|
- 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.
|
- select between the square selector and the color wheel selector.
|
||||||
|
|
|
@ -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 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  button to the left.
|
to move a chip around, click and drag the  button to the left.
|
||||||
|
|
||||||
to duplicate a chip, click the **Clone** button.
|
to duplicate a chip, click the **Clone** button.
|
||||||
|
|
22
doc/8-advanced/user-systems.md
Normal file
22
doc/8-advanced/user-systems.md
Normal file
|
@ -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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
BIN
doc/8-advanced/user-systems.png
Normal file
BIN
doc/8-advanced/user-systems.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
|
@ -620,6 +620,17 @@ class DivEngine {
|
||||||
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
|
|
||||||
|
//sample banks
|
||||||
|
void loadP(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPPC(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPPS(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPVI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPDX(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadPZI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
void loadP86(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
||||||
|
|
||||||
bool initAudioBackend();
|
bool initAudioBackend();
|
||||||
|
@ -1034,7 +1045,8 @@ class DivEngine {
|
||||||
int addSamplePtr(DivSample* which);
|
int addSamplePtr(DivSample* which);
|
||||||
|
|
||||||
// get sample from file
|
// get sample from file
|
||||||
DivSample* sampleFromFile(const char* path);
|
//DivSample* sampleFromFile(const char* path);
|
||||||
|
std::vector<DivSample*> sampleFromFile(const char* path);
|
||||||
|
|
||||||
// get raw sample
|
// get raw sample
|
||||||
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles, int rate);
|
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles, int rate);
|
||||||
|
|
|
@ -63,3 +63,34 @@ enum DivFurVariants: int {
|
||||||
DIV_FUR_VARIANT_VANILLA=0,
|
DIV_FUR_VARIANT_VANILLA=0,
|
||||||
DIV_FUR_VARIANT_B=1,
|
DIV_FUR_VARIANT_B=1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// MIDI-related
|
||||||
|
struct midibank_t {
|
||||||
|
String name;
|
||||||
|
uint8_t bankMsb,
|
||||||
|
bankLsb;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reused patch data structures
|
||||||
|
|
||||||
|
// SBI and some other OPL containers
|
||||||
|
|
||||||
|
struct sbi_t {
|
||||||
|
uint8_t Mcharacteristics,
|
||||||
|
Ccharacteristics,
|
||||||
|
Mscaling_output,
|
||||||
|
Cscaling_output,
|
||||||
|
Meg_AD,
|
||||||
|
Ceg_AD,
|
||||||
|
Meg_SR,
|
||||||
|
Ceg_SR,
|
||||||
|
Mwave,
|
||||||
|
Cwave,
|
||||||
|
FeedConnect;
|
||||||
|
};
|
||||||
|
|
||||||
|
//bool stringNotBlank(String& str);
|
||||||
|
// detune needs extra translation from register to furnace format
|
||||||
|
//uint8_t fmDtRegisterToFurnace(uint8_t&& dtNative);
|
||||||
|
|
||||||
|
//void readSbiOpData(sbi_t& sbi, SafeReader& reader);
|
||||||
|
|
|
@ -639,6 +639,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
|
||||||
logD("seek not needed...");
|
logD("seek not needed...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logV("reading sample data (%d)",s->samples);
|
||||||
|
|
||||||
if (flags&8) { // compressed sample
|
if (flags&8) { // compressed sample
|
||||||
unsigned int ret=0;
|
unsigned int ret=0;
|
||||||
logV("decompression begin... (%d)",s->samples);
|
logV("decompression begin... (%d)",s->samples);
|
||||||
|
@ -672,62 +674,66 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
logV("got: %d",ret);
|
logV("got: %d",ret);
|
||||||
} else {
|
} else {
|
||||||
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
try {
|
||||||
if (flags&4) { // downmix stereo
|
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||||
for (unsigned int i=0; i<s->samples; i++) {
|
if (flags&4) { // downmix stereo
|
||||||
short l;
|
for (unsigned int i=0; i<s->samples; i++) {
|
||||||
if (convert&2) {
|
short l;
|
||||||
l=reader.readS_BE();
|
if (convert&2) {
|
||||||
} else {
|
l=reader.readS_BE();
|
||||||
l=reader.readS();
|
} else {
|
||||||
|
l=reader.readS();
|
||||||
|
}
|
||||||
|
if (!(convert&1)) {
|
||||||
|
l^=0x8000;
|
||||||
|
}
|
||||||
|
s->data16[i]=l;
|
||||||
}
|
}
|
||||||
if (!(convert&1)) {
|
for (unsigned int i=0; i<s->samples; i++) {
|
||||||
l^=0x8000;
|
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;
|
} else {
|
||||||
}
|
for (unsigned int i=0; i<s->samples; i++) {
|
||||||
for (unsigned int i=0; i<s->samples; i++) {
|
if (convert&2) {
|
||||||
short r;
|
s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000);
|
||||||
if (convert&2) {
|
} else {
|
||||||
r=reader.readS_BE();
|
s->data16[i]=reader.readS()^((convert&1)?0:0x8000);
|
||||||
} else {
|
}
|
||||||
r=reader.readS();
|
|
||||||
}
|
}
|
||||||
if (!(convert&1)) {
|
|
||||||
r^=0x8000;
|
|
||||||
}
|
|
||||||
s->data16[i]=(s->data16[i]+r)>>1;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (unsigned int i=0; i<s->samples; i++) {
|
if (flags&4) { // downmix stereo
|
||||||
if (convert&2) {
|
for (unsigned int i=0; i<s->samples; i++) {
|
||||||
s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000);
|
signed char l=reader.readC();
|
||||||
} else {
|
if (!(convert&1)) {
|
||||||
s->data16[i]=reader.readS()^((convert&1)?0:0x8000);
|
l^=0x80;
|
||||||
|
}
|
||||||
|
s->data8[i]=l;
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<s->samples; 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; i<s->samples; i++) {
|
||||||
|
s->data8[i]=reader.readC()^((convert&1)?0:0x80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} catch (EndOfFileException& e) {
|
||||||
if (flags&4) { // downmix stereo
|
logW("premature end of file...");
|
||||||
for (unsigned int i=0; i<s->samples; i++) {
|
|
||||||
signed char l=reader.readC();
|
|
||||||
if (!(convert&1)) {
|
|
||||||
l^=0x80;
|
|
||||||
}
|
|
||||||
s->data8[i]=l;
|
|
||||||
}
|
|
||||||
for (unsigned int i=0; i<s->samples; 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; i<s->samples; i++) {
|
|
||||||
s->data8[i]=reader.readC()^((convert&1)?0:0x80);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
124
src/engine/fileOps/p.cpp
Normal file
124
src/engine/fileOps/p.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//P VOX ADPCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 - 0x03FF 256 * {
|
||||||
|
Sample start (uint32_t)
|
||||||
|
- 0x00000000 = unused
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
MSM6258 ADPCM encoding
|
||||||
|
nibble-swapped VOX / Dialogic ADPCM
|
||||||
|
Mono
|
||||||
|
Sample rate?
|
||||||
|
16000Hz seems fine
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define P_BANK_SIZE 256
|
||||||
|
#define P_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
} P_HEADER;
|
||||||
|
|
||||||
|
|
||||||
|
void DivEngine::loadP(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
P_HEADER headers[P_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < P_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI_BE();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < P_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = P_SAMPLE_RATE;
|
||||||
|
s->centerRate = P_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_VOX;
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_len = 0;
|
||||||
|
|
||||||
|
if(i < P_BANK_SIZE - 1)
|
||||||
|
{
|
||||||
|
sample_len = headers[i + 1].start_pointer - headers[i].start_pointer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sample_len = (int)reader.size() - headers[i].start_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sample_len > 0)
|
||||||
|
{
|
||||||
|
s->init(sample_len * 2);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_len; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte = (curr_byte << 4) | (curr_byte >> 4);
|
||||||
|
|
||||||
|
s->dataVOX[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
logI("p: start %d len %d", headers[i].start_pointer, sample_len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
142
src/engine/fileOps/p86.cpp
Normal file
142
src/engine/fileOps/p86.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//P86 8-bit PCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (12b)
|
||||||
|
"PCM86 DATA(\n)(\0)"
|
||||||
|
0x000C Targeted P86DRV version (1b)
|
||||||
|
version <high nibble>.<low nibble>
|
||||||
|
0x000D File Length (3b)
|
||||||
|
0x0010 - 0x060F 256 * {
|
||||||
|
|
||||||
|
Pointer to Sample Data Start (3b)
|
||||||
|
Length of Sample Data (3b)
|
||||||
|
|
||||||
|
(0x000000 0x000000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
8-Bit Signed
|
||||||
|
Mono
|
||||||
|
16540Hz
|
||||||
|
(above sample rate according to KAJA's documentation
|
||||||
|
any sample rate possible, for different base note & octave)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define P86_BANK_SIZE 256
|
||||||
|
#define P86_SAMPLE_RATE 16540
|
||||||
|
|
||||||
|
#define P86_FILE_SIG "PCM86 DATA\n\0"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
uint32_t sample_length;
|
||||||
|
} P86_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
uint32_t read_3bytes(SafeReader& reader)
|
||||||
|
{
|
||||||
|
unsigned char arr[3];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
arr[i] = (unsigned char)reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (arr[0] | (arr[1] << 8) | (arr[2] << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivEngine::loadP86(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
P86_HEADER headers[P86_BANK_SIZE];
|
||||||
|
|
||||||
|
String file_sig = reader.readString(12);
|
||||||
|
if(file_sig != P86_FILE_SIG) return;
|
||||||
|
|
||||||
|
uint8_t version = reader.readC();
|
||||||
|
UNUSED(version);
|
||||||
|
|
||||||
|
uint32_t file_size = read_3bytes(reader);
|
||||||
|
UNUSED(file_size);
|
||||||
|
|
||||||
|
for(int i = 0; i < P86_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = read_3bytes(reader);
|
||||||
|
headers[i].sample_length = read_3bytes(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < P86_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 && headers[i].sample_length != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = P86_SAMPLE_RATE;
|
||||||
|
s->centerRate = P86_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte += 0x80;
|
||||||
|
|
||||||
|
s->data8[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("p86: start %06X len %06X", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
101
src/engine/fileOps/pdx.cpp
Normal file
101
src/engine/fileOps/pdx.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PDX 8-bit OKI ADPCM sample bank
|
||||||
|
|
||||||
|
/* File format
|
||||||
|
The file starts with a header with 96 8-byte pairs, with each pair containing offset and length of ADPCM data chunks for each note value, like so:
|
||||||
|
|
||||||
|
[[byte pointer to sample in file -- 32-bit unsigned integer (4 bytes)] [empty: $0000 -- 16-bit integer] [length of sample, amount in bytes -- 16-bit unsigned integer] ×96] [ADPCM data] EOF
|
||||||
|
|
||||||
|
The first sample (1) is mapped to 0x80 (= the lowest note within MXDRV) and the last sample (96) is mapped to 0xDF (= the highest note within MXDRV).
|
||||||
|
|
||||||
|
|
||||||
|
Samples are encoded in 4-bit OKI ADPCM encoded nibbles, where each byte contains 2 nibbles: [nibble 2 << 4 || nibble 1]
|
||||||
|
|
||||||
|
|
||||||
|
Unfortunately, sample rates for each samples are not defined within the .PDX file and have to be set manually with the appropriate command for that at play-time */
|
||||||
|
|
||||||
|
#define PDX_BANK_SIZE 96
|
||||||
|
#define PDX_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int start_pointer;
|
||||||
|
unsigned short sample_length;
|
||||||
|
} PDX_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
void DivEngine::loadPDX(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PDX_HEADER headers[PDX_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PDX_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI_BE();
|
||||||
|
unsigned short empty = (unsigned short)reader.readS_BE(); //skip 1st 2 bytes
|
||||||
|
UNUSED(empty);
|
||||||
|
headers[i].sample_length = (unsigned short)reader.readS_BE();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PDX_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 && headers[i].sample_length != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PDX_SAMPLE_RATE;
|
||||||
|
s->centerRate = PDX_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_VOX;
|
||||||
|
s->init(headers[i].sample_length * 2);
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(unsigned short j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte = (curr_byte << 4) | (curr_byte >> 4);
|
||||||
|
|
||||||
|
s->dataVOX[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pdx: start %d len %d", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
142
src/engine/fileOps/ppc.cpp
Normal file
142
src/engine/fileOps/ppc.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PPC PMD's YM2608 ADPCM-B sample bank
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
General
|
||||||
|
========================================
|
||||||
|
|
||||||
|
ADPCM RAM addresses: see docs/common.txt {
|
||||||
|
|
||||||
|
address_start = 0x0026
|
||||||
|
file_header_size = 0x0420
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
========================================
|
||||||
|
Header
|
||||||
|
========================================
|
||||||
|
|
||||||
|
0x0000 Identifier (30b)
|
||||||
|
"ADPCM DATA for PMD ver.4.4- "
|
||||||
|
0x001E Address of End of Data (32B blocks) in ADPCM RAM (1h)
|
||||||
|
File Size == address -> file offset
|
||||||
|
|
||||||
|
0x0020 - 0x041F 256 * {
|
||||||
|
|
||||||
|
Start of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
End of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
|
||||||
|
(0x0000 0x0000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
========================================
|
||||||
|
Body
|
||||||
|
========================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Yamaha ADPCM-B encoding (4-Bit Signed ADPCM)
|
||||||
|
Mono
|
||||||
|
16kHz
|
||||||
|
(above sample rate according to KAJA's documentation
|
||||||
|
any sample rate possible, for different base note & octave)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PPC_FILE_SIG "ADPCM DATA for PMD ver.4.4- "
|
||||||
|
|
||||||
|
#define PPC_BANK_SIZE 256
|
||||||
|
#define PPC_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t end_pointer;
|
||||||
|
} PPC_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
#define ADPCM_DATA_START 0x0420
|
||||||
|
|
||||||
|
void DivEngine::loadPPC(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
String file_sig = reader.readString(30);
|
||||||
|
unsigned short end_of_data = (unsigned short)reader.readS();
|
||||||
|
UNUSED(end_of_data);
|
||||||
|
|
||||||
|
if(file_sig != PPC_FILE_SIG) return;
|
||||||
|
|
||||||
|
PPC_HEADER headers[PPC_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PPC_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].end_pointer = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PPC_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if((headers[i].start_pointer != 0 || headers[i].end_pointer != 0) && headers[i].start_pointer < headers[i].end_pointer)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PPC_SAMPLE_RATE;
|
||||||
|
s->centerRate = PPC_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_ADPCM_B;
|
||||||
|
s->init((headers[i].end_pointer - headers[i].start_pointer) * 32 * 2);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_length = (headers[i].end_pointer - headers[i].start_pointer) * 32;
|
||||||
|
|
||||||
|
//reader.seek(ADPCM_DATA_START + headers[i].start_pointer * 32, SEEK_SET);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte=(curr_byte<<4)|(curr_byte>>4);
|
||||||
|
|
||||||
|
s->dataB[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logI("ppc: start %d end %d len in bytes %d", headers[i].start_pointer, headers[i].end_pointer, (headers[i].end_pointer - headers[i].start_pointer) * 32);
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
125
src/engine/fileOps/pps.cpp
Normal file
125
src/engine/fileOps/pps.cpp
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PPS AY-3-8910 sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 - 0x0053 14 * {
|
||||||
|
|
||||||
|
Pointer to Sample Data Start (1h)
|
||||||
|
Length of Sample Data (1h)
|
||||||
|
"Pitch"(?) (1b)
|
||||||
|
Volume Reduction (1b)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
(0x0000 0x0000 [0x00 0x00] -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
4-Bit Unsigned
|
||||||
|
(afaict)
|
||||||
|
Mono
|
||||||
|
16Hz
|
||||||
|
(based on tests, maybe alternatively 8kHz)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PPS_BANK_SIZE 14
|
||||||
|
#define PPS_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t sample_length;
|
||||||
|
uint8_t _pitch;
|
||||||
|
uint8_t _vol;
|
||||||
|
} PPS_HEADER;
|
||||||
|
|
||||||
|
|
||||||
|
void DivEngine::loadPPS(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PPS_HEADER headers[PPS_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PPS_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].sample_length = (unsigned short)reader.readS();
|
||||||
|
headers[i]._pitch = (unsigned char)reader.readC();
|
||||||
|
headers[i]._vol = (unsigned char)reader.readC();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PPS_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(headers[i].start_pointer != 0 || headers[i].sample_length != 0
|
||||||
|
|| headers[i]._pitch != 0 || headers[i]._vol != 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PPS_SAMPLE_RATE;
|
||||||
|
s->centerRate = PPS_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length * 2); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for(int j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
|
||||||
|
s->data8[sample_pos] = (curr_byte >> 4) | (curr_byte & 0xf0);
|
||||||
|
s->data8[sample_pos] += 0x80;
|
||||||
|
sample_pos++;
|
||||||
|
s->data8[sample_pos] = (curr_byte << 4) | (curr_byte & 0xf);
|
||||||
|
s->data8[sample_pos] += 0x80;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pps: start %d len %d", headers[i].start_pointer, headers[i].sample_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
158
src/engine/fileOps/pvi.cpp
Normal file
158
src/engine/fileOps/pvi.cpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PVI YM2608 ADPCM-B sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
General
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
ADPCM RAM addresses: see docs/common.txt {
|
||||||
|
|
||||||
|
address_start = 0x0000
|
||||||
|
file_header_size = 0x0210
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (4b)
|
||||||
|
"PVI2"
|
||||||
|
0x0004 - 0x0007 Unknown Settings (4b)
|
||||||
|
Unknown mappings PCME switches <-> Values
|
||||||
|
"0x10 0x00 0x10 0x02" in all example files
|
||||||
|
First 1h may be Start Address in ADPCM RAM?
|
||||||
|
0x0008 - 0x0009 "Delta-N" playback frequency (1h)
|
||||||
|
Default 0x49BA "== 16kHz"??
|
||||||
|
0x000A Unknown (1b)
|
||||||
|
RAM type? (Docs indicate values 0 or 8, all example files have value 0x02?)
|
||||||
|
0x000B Amount of defined Samples (1b)
|
||||||
|
0x000C - 0x000F Unknown (4b)
|
||||||
|
Padding?
|
||||||
|
"0x00 0x00 0x00 0x00" in all example files
|
||||||
|
0x0010 - 0x020F 128 * {
|
||||||
|
|
||||||
|
Start of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
End of Sample (32b blocks) in ADPCM RAM (1h)
|
||||||
|
|
||||||
|
(0x0000 0x0000 -> no sample for this instrument ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Yamaha ADPCM-B encoding (4-Bit Signed ADPCM)
|
||||||
|
Mono
|
||||||
|
Sample rate as specified earlier
|
||||||
|
(examples i have seems Stereo or 32kHz despite "16kHz" playback frequency setting?)
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PVIV2_FILE_SIG "PVI2"
|
||||||
|
#define PVIV1_FILE_SIG "PVI1"
|
||||||
|
|
||||||
|
#define PVI_BANK_SIZE 128
|
||||||
|
#define PVI_SAMPLE_RATE 16000
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start_pointer;
|
||||||
|
uint16_t end_pointer;
|
||||||
|
} PVI_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
#define ADPCM_DATA_START 0x0210
|
||||||
|
|
||||||
|
void DivEngine::loadPVI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
String file_sig = reader.readString(4);
|
||||||
|
if(file_sig != PVIV1_FILE_SIG && file_sig != PVIV2_FILE_SIG) return;
|
||||||
|
|
||||||
|
unsigned int unknown_settings = (unsigned int)reader.readI();
|
||||||
|
UNUSED(unknown_settings);
|
||||||
|
unsigned short delta_n = (unsigned short)reader.readS();
|
||||||
|
UNUSED(delta_n);
|
||||||
|
unsigned char one_byte = (unsigned char)reader.readC();
|
||||||
|
UNUSED(one_byte);
|
||||||
|
unsigned char amount_of_samples = (unsigned char)reader.readC();
|
||||||
|
UNUSED(amount_of_samples);
|
||||||
|
unsigned int padding = (unsigned int)reader.readI();
|
||||||
|
UNUSED(padding);
|
||||||
|
|
||||||
|
PVI_HEADER headers[PVI_BANK_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < PVI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned short)reader.readS();
|
||||||
|
headers[i].end_pointer = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PVI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if((headers[i].start_pointer != 0 || headers[i].end_pointer != 0) && headers[i].start_pointer < headers[i].end_pointer)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = PVI_SAMPLE_RATE;
|
||||||
|
s->centerRate = PVI_SAMPLE_RATE;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_ADPCM_B;
|
||||||
|
s->init((headers[i].end_pointer - headers[i].start_pointer) * 32 * 2);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
int sample_length = (headers[i].end_pointer - headers[i].start_pointer) * 32;
|
||||||
|
|
||||||
|
reader.seek(ADPCM_DATA_START + headers[i].start_pointer * 32, SEEK_SET);
|
||||||
|
|
||||||
|
for(int j = 0; j < sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
//curr_byte=(curr_byte<<4)|(curr_byte>>4);
|
||||||
|
|
||||||
|
s->dataB[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logI("pvi: start %d end %d len in bytes %d", headers[i].start_pointer, headers[i].end_pointer, (headers[i].end_pointer - headers[i].start_pointer) * 32);
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
155
src/engine/fileOps/pzi.cpp
Normal file
155
src/engine/fileOps/pzi.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* 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 "fileOpsCommon.h"
|
||||||
|
|
||||||
|
class DivEngine;
|
||||||
|
|
||||||
|
//PZI 8-bit PCM sample bank
|
||||||
|
|
||||||
|
/* =======================================
|
||||||
|
Header
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
0x0000 Identifier (4b)
|
||||||
|
"PZI1"
|
||||||
|
0x0004 - 0x001F Unknown (28b)
|
||||||
|
Part of identifier? Settings?
|
||||||
|
All (\0)s in all example files
|
||||||
|
0x0020 - 0x091F 128 * {
|
||||||
|
|
||||||
|
Start of Sample after header (2h)
|
||||||
|
Length of Sample (2h)
|
||||||
|
Offset of loop start from sample start (2h)
|
||||||
|
Offset of loop end from sample start (2h)
|
||||||
|
Sample rate (1h)
|
||||||
|
|
||||||
|
(0xFFFFFFFF 0xFFFFFFFF loop offsets -> no loop information)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
Body
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Stream of Sample Data {
|
||||||
|
|
||||||
|
Unsigned 8-Bit
|
||||||
|
Mono
|
||||||
|
Sample rate as specified in header
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#define PZI_BANK_SIZE 128
|
||||||
|
|
||||||
|
#define PZI_FILE_SIG "PZI1"
|
||||||
|
|
||||||
|
#define NO_LOOP (0xFFFFFFFFU)
|
||||||
|
|
||||||
|
#define SAMPLE_DATA_OFFSET 0x0920
|
||||||
|
|
||||||
|
#define MAX_SANITY_CAP 9999999
|
||||||
|
|
||||||
|
#define HEADER_JUNK_SIZE 28
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t start_pointer;
|
||||||
|
uint32_t sample_length;
|
||||||
|
uint32_t loop_start;
|
||||||
|
uint32_t loop_end;
|
||||||
|
uint16_t sample_rate;
|
||||||
|
} PZI_HEADER;
|
||||||
|
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
|
void DivEngine::loadPZI(SafeReader& reader, std::vector<DivSample*>& ret, String& stripPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
PZI_HEADER headers[PZI_BANK_SIZE];
|
||||||
|
|
||||||
|
String file_sig = reader.readString(4);
|
||||||
|
if(file_sig != PZI_FILE_SIG) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < HEADER_JUNK_SIZE; i++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
UNUSED(curr_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PZI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
headers[i].start_pointer = (unsigned int)reader.readI();
|
||||||
|
headers[i].sample_length = (unsigned int)reader.readI();
|
||||||
|
headers[i].loop_start = (unsigned int)reader.readI();
|
||||||
|
headers[i].loop_end = (unsigned int)reader.readI();
|
||||||
|
headers[i].sample_rate = (unsigned short)reader.readS();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < PZI_BANK_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (headers[i].start_pointer < MAX_SANITY_CAP && headers[i].sample_length < MAX_SANITY_CAP &&
|
||||||
|
headers[i].loop_start < MAX_SANITY_CAP && headers[i].loop_end < MAX_SANITY_CAP &&
|
||||||
|
headers[i].start_pointer > 0 && headers[i].sample_length > 0)
|
||||||
|
{
|
||||||
|
DivSample* s = new DivSample;
|
||||||
|
|
||||||
|
s->rate = headers[i].sample_rate;
|
||||||
|
s->centerRate = headers[i].sample_rate;
|
||||||
|
s->depth = DIV_SAMPLE_DEPTH_8BIT;
|
||||||
|
s->init(headers[i].sample_length); //byte per sample
|
||||||
|
|
||||||
|
reader.seek((int)headers[i].start_pointer + SAMPLE_DATA_OFFSET, SEEK_SET);
|
||||||
|
|
||||||
|
int sample_pos = 0;
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < headers[i].sample_length; j++)
|
||||||
|
{
|
||||||
|
unsigned char curr_byte = (unsigned char)reader.readC();
|
||||||
|
curr_byte += 0x80;
|
||||||
|
|
||||||
|
s->data8[sample_pos] = curr_byte;
|
||||||
|
sample_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers[i].loop_start != NO_LOOP && headers[i].loop_end != NO_LOOP)
|
||||||
|
{
|
||||||
|
s->loop = true;
|
||||||
|
s->loopMode = DIV_SAMPLE_LOOP_FORWARD;
|
||||||
|
s->loopStart = headers[i].loop_start;
|
||||||
|
s->loopEnd = headers[i].loop_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push_back(s);
|
||||||
|
|
||||||
|
logI("pzi: start %d len %d sample rate %d loop start %d loop end %d", headers[i].start_pointer, headers[i].sample_length,
|
||||||
|
headers[i].sample_rate, headers[i].loop_start, headers[i].loop_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EndOfFileException& e)
|
||||||
|
{
|
||||||
|
lastError=_("premature end of file");
|
||||||
|
logE("premature end of file");
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,21 +19,6 @@
|
||||||
|
|
||||||
#include "fileOpsCommon.h"
|
#include "fileOpsCommon.h"
|
||||||
|
|
||||||
// SBI and some other OPL containers
|
|
||||||
struct sbi_t {
|
|
||||||
uint8_t Mcharacteristics,
|
|
||||||
Ccharacteristics,
|
|
||||||
Mscaling_output,
|
|
||||||
Cscaling_output,
|
|
||||||
Meg_AD,
|
|
||||||
Ceg_AD,
|
|
||||||
Meg_SR,
|
|
||||||
Ceg_SR,
|
|
||||||
Mwave,
|
|
||||||
Cwave,
|
|
||||||
FeedConnect;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void readSbiOpData(sbi_t& sbi, SafeReader& reader) {
|
static void readSbiOpData(sbi_t& sbi, SafeReader& reader) {
|
||||||
sbi.Mcharacteristics = reader.readC();
|
sbi.Mcharacteristics = reader.readC();
|
||||||
sbi.Ccharacteristics = reader.readC();
|
sbi.Ccharacteristics = reader.readC();
|
||||||
|
@ -51,6 +36,7 @@ static void readSbiOpData(sbi_t& sbi, SafeReader& reader) {
|
||||||
bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
||||||
struct InvalidHeaderException {};
|
struct InvalidHeaderException {};
|
||||||
bool success=false;
|
bool success=false;
|
||||||
|
bool opl2=!getConfInt("s3mOPL3",0);
|
||||||
char magic[4]={0,0,0,0};
|
char magic[4]={0,0,0,0};
|
||||||
SafeReader reader=SafeReader(file,len);
|
SafeReader reader=SafeReader(file,len);
|
||||||
warnings="";
|
warnings="";
|
||||||
|
@ -273,11 +259,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
||||||
bool hasPCM=false;
|
bool hasPCM=false;
|
||||||
bool hasFM=false;
|
bool hasFM=false;
|
||||||
int numChans=0;
|
int numChans=0;
|
||||||
|
int realNumChans=0;
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int ch=0; ch<32; ch++) {
|
||||||
if (chanSettings[i]==255) continue;
|
if (chanSettings[ch]!=255) realNumChans++;
|
||||||
if ((chanSettings[i]&127)>=32) continue;
|
}
|
||||||
if ((chanSettings[i]&127)>=16) {
|
|
||||||
|
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;
|
hasFM=true;
|
||||||
} else {
|
} else {
|
||||||
hasPCM=true;
|
hasPCM=true;
|
||||||
|
@ -287,34 +278,69 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
||||||
if (hasFM && hasPCM) break;
|
if (hasFM && hasPCM) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pcmChan=hasFM?9:0;
|
int pcmChan=hasFM?(opl2 ? 9 : 18):0;
|
||||||
int fmChan=hasPCM?32:0;
|
int fmChan=hasPCM?32:0;
|
||||||
int invalidChan=40;
|
int invalidChan=40;
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int ch=0; ch<32; ch++) {
|
||||||
if (chanSettings[i]==255) {
|
if (chanSettings[ch]==255) {
|
||||||
chanMap[i]=invalidChan++;
|
chanMap[ch]=invalidChan++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((chanSettings[i]&127)>=32) {
|
if ((chanSettings[ch]&127)>=32) {
|
||||||
chanMap[i]=invalidChan++;
|
chanMap[ch]=invalidChan++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((chanSettings[i]&127)>=16) {
|
if ((chanSettings[ch]&127)>=16) {
|
||||||
chanMap[i]=fmChan++;
|
chanMap[ch]=fmChan++;
|
||||||
} else {
|
} else {
|
||||||
chanMap[i]=pcmChan++;
|
chanMap[ch]=pcmChan++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char buffer[40];
|
||||||
|
int chanIndex = 1;
|
||||||
|
|
||||||
if (hasPCM) {
|
if (hasPCM) {
|
||||||
for (int i=pcmChan; i<32; i++) {
|
for(int ch = 0; ch < pcmChan - (realNumChans - (hasFM ? 9 : 0)); ch++)
|
||||||
ds.subsong[0]->chanShow[i]=false;
|
{
|
||||||
ds.subsong[0]->chanShowChanOsc[i]=false;
|
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("numChans: %d",numChans);
|
||||||
|
logV("realNumChans: %d",realNumChans);
|
||||||
|
|
||||||
ds.systemName="PC";
|
ds.systemName="PC";
|
||||||
if (hasPCM) {
|
if (hasPCM) {
|
||||||
|
@ -327,7 +353,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
||||||
ds.systemLen++;
|
ds.systemLen++;
|
||||||
}
|
}
|
||||||
if (hasFM) {
|
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.systemVol[ds.systemLen]=1.0f;
|
||||||
ds.systemPan[ds.systemLen]=0;
|
ds.systemPan[ds.systemLen]=0;
|
||||||
ds.systemLen++;
|
ds.systemLen++;
|
||||||
|
|
|
@ -24,10 +24,12 @@
|
||||||
#include "sfWrapper.h"
|
#include "sfWrapper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DivSample* DivEngine::sampleFromFile(const char* path) {
|
std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
|
||||||
|
std::vector<DivSample*> ret;
|
||||||
|
|
||||||
if (song.sample.size()>=256) {
|
if (song.sample.size()>=256) {
|
||||||
lastError="too many samples!";
|
lastError="too many samples!";
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
BUSY_BEGIN;
|
BUSY_BEGIN;
|
||||||
warnings="";
|
warnings="";
|
||||||
|
@ -58,6 +60,110 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
}
|
}
|
||||||
extS+=i;
|
extS+=i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(extS == ".pps" || extS == ".ppc" || extS == ".pvi" ||
|
||||||
|
extS == ".pdx" || extS == ".pzi" || extS == ".p86" ||
|
||||||
|
extS == ".p") //sample banks!
|
||||||
|
{
|
||||||
|
String stripPath;
|
||||||
|
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||||
|
if (pathReduxEnd==NULL) {
|
||||||
|
stripPath=pathRedux;
|
||||||
|
} else {
|
||||||
|
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||||
|
stripPath+=*i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* f=ps_fopen(path,"rb");
|
||||||
|
if (f==NULL) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
unsigned char* buf;
|
||||||
|
ssize_t len;
|
||||||
|
if (fseek(f,0,SEEK_END)!=0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
len=ftell(f);
|
||||||
|
if (len<0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (len==(SIZE_MAX>>1)) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (len==0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (fseek(f,0,SEEK_SET)!=0) {
|
||||||
|
lastError=strerror(errno);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
buf=new unsigned char[len];
|
||||||
|
if (fread(buf,1,len,f)!=(size_t)len) {
|
||||||
|
logW("did not read entire sample bank file buffer!");
|
||||||
|
lastError=_("did not read entire sample bank file!");
|
||||||
|
delete[] buf;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
SafeReader reader = SafeReader(buf,len);
|
||||||
|
|
||||||
|
if(extS == ".pps")
|
||||||
|
{
|
||||||
|
loadPPS(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".ppc")
|
||||||
|
{
|
||||||
|
loadPPC(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pvi")
|
||||||
|
{
|
||||||
|
loadPVI(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pdx")
|
||||||
|
{
|
||||||
|
loadPDX(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".pzi")
|
||||||
|
{
|
||||||
|
loadPZI(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".p86")
|
||||||
|
{
|
||||||
|
loadP86(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
if(extS == ".p")
|
||||||
|
{
|
||||||
|
loadP(reader,ret,stripPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((int)ret.size() > 0)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
for(DivSample* s: ret)
|
||||||
|
{
|
||||||
|
s->name = fmt::sprintf("%s sample %d", stripPath, counter);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buf; //done with buffer
|
||||||
|
BUSY_END;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
||||||
size_t len=0;
|
size_t len=0;
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
|
@ -68,7 +174,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(f,0,SEEK_END)<0) {
|
if (fseek(f,0,SEEK_END)<0) {
|
||||||
|
@ -76,7 +182,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
len=ftell(f);
|
len=ftell(f);
|
||||||
|
@ -86,7 +192,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="file is empty!";
|
lastError="file is empty!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len==(SIZE_MAX>>1)) {
|
if (len==(SIZE_MAX>>1)) {
|
||||||
|
@ -94,7 +200,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="file is invalid!";
|
lastError="file is invalid!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(f,0,SEEK_SET)<0) {
|
if (fseek(f,0,SEEK_SET)<0) {
|
||||||
|
@ -102,7 +208,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extS==".dmc") {
|
if (extS==".dmc") {
|
||||||
|
@ -120,7 +226,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="wait... is that right? no I don't think so...";
|
lastError="wait... is that right? no I don't think so...";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* dataBuf=sample->dataDPCM;
|
unsigned char* dataBuf=sample->dataDPCM;
|
||||||
|
@ -147,14 +253,14 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="BRR sample is empty!";
|
lastError="BRR sample is empty!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
} else if ((len%9)!=0) {
|
} else if ((len%9)!=0) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError="possibly corrupt BRR sample!";
|
lastError="possibly corrupt BRR sample!";
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,16 +269,17 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||||
delete sample;
|
delete sample;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return sample;
|
ret.push_back(sample);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_SNDFILE
|
#ifndef HAVE_SNDFILE
|
||||||
lastError="Furnace was not compiled with libsndfile!";
|
lastError="Furnace was not compiled with libsndfile!";
|
||||||
return NULL;
|
return ret;
|
||||||
#else
|
#else
|
||||||
SF_INFO si;
|
SF_INFO si;
|
||||||
SFWrapper sfWrap;
|
SFWrapper sfWrap;
|
||||||
|
@ -186,13 +293,13 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
} else {
|
} else {
|
||||||
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
||||||
}
|
}
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
if (si.frames>16777215) {
|
if (si.frames>16777215) {
|
||||||
lastError="this sample is too big! max sample size is 16777215.";
|
lastError="this sample is too big! max sample size is 16777215.";
|
||||||
sfWrap.doClose();
|
sfWrap.doClose();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
void* buf=NULL;
|
void* buf=NULL;
|
||||||
sf_count_t sampleLen=sizeof(short);
|
sf_count_t sampleLen=sizeof(short);
|
||||||
|
@ -298,7 +405,8 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||||
if (sample->centerRate>64000) sample->centerRate=64000;
|
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||||
sfWrap.doClose();
|
sfWrap.doClose();
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
return sample;
|
ret.push_back(sample);
|
||||||
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
||||||
if (adpcmBMem==NULL) {
|
if (adpcmBMem==NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return adpcmBMem[address&0xffffff];
|
return adpcmBMem[address&0x3ffff];
|
||||||
case ymfm::ACCESS_PCM:
|
case ymfm::ACCESS_PCM:
|
||||||
if (pcmMem==NULL) {
|
if (pcmMem==NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1663,26 +1663,34 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
||||||
chan[i].panPos+=chan[i].panRate;
|
chan[i].panPos+=chan[i].panRate;
|
||||||
chan[i].panPos&=255;
|
chan[i].panPos&=255;
|
||||||
|
|
||||||
// calculate...
|
// calculate inverted...
|
||||||
switch (chan[i].panPos&0xc0) {
|
switch (chan[i].panPos&0xc0) {
|
||||||
case 0: // center -> right
|
case 0: // center -> right
|
||||||
chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2);
|
chan[i].panL=((chan[i].panPos&0x3f)<<2);
|
||||||
chan[i].panR=0xff;
|
chan[i].panR=0;
|
||||||
break;
|
break;
|
||||||
case 0x40: // right -> center
|
case 0x40: // right -> center
|
||||||
chan[i].panL=(chan[i].panPos&0x3f)<<2;
|
chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2);
|
||||||
chan[i].panR=0xff;
|
chan[i].panR=0;
|
||||||
break;
|
break;
|
||||||
case 0x80: // center -> left
|
case 0x80: // center -> left
|
||||||
chan[i].panL=0xff;
|
chan[i].panL=0;
|
||||||
chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2);
|
chan[i].panR=((chan[i].panPos&0x3f)<<2);
|
||||||
break;
|
break;
|
||||||
case 0xc0: // left -> center
|
case 0xc0: // left -> center
|
||||||
chan[i].panL=0xff;
|
chan[i].panL=0;
|
||||||
chan[i].panR=(chan[i].panPos&0x3f)<<2;
|
chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2);
|
||||||
break;
|
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));
|
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@ static float oscDebugMax=1.0;
|
||||||
static float oscDebugPower=1.0;
|
static float oscDebugPower=1.0;
|
||||||
static int oscDebugRepeat=1;
|
static int oscDebugRepeat=1;
|
||||||
static int numApples=1;
|
static int numApples=1;
|
||||||
|
static int getGainChan=0;
|
||||||
|
static int getGainVol=0;
|
||||||
|
|
||||||
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
||||||
if (cmd!=NULL) {
|
if (cmd!=NULL) {
|
||||||
|
@ -721,6 +723,13 @@ void FurnaceGUI::drawDebug() {
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
#endif
|
#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::TreeNode("User Interface")) {
|
||||||
if (ImGui::Button("Inspect")) {
|
if (ImGui::Button("Inspect")) {
|
||||||
inspectorOpen=!inspectorOpen;
|
inspectorOpen=!inspectorOpen;
|
||||||
|
|
|
@ -676,6 +676,17 @@ void FurnaceGUI::doAction(int what) {
|
||||||
latchTarget=0;
|
latchTarget=0;
|
||||||
latchNibble=false;
|
latchNibble=false;
|
||||||
break;
|
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:
|
case GUI_ACTION_INS_LIST_ADD:
|
||||||
if (settings.insTypeMenu) {
|
if (settings.insTypeMenu) {
|
||||||
|
|
341
src/gui/gui.cpp
341
src/gui/gui.cpp
|
@ -3828,8 +3828,9 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
int sampleCountBefore=e->song.sampleLen;
|
int sampleCountBefore=e->song.sampleLen;
|
||||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
|
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
|
||||||
|
std::vector<DivSample*> samples = e->sampleFromFile(ev.drop.file);
|
||||||
DivWavetable* droppedWave=NULL;
|
DivWavetable* droppedWave=NULL;
|
||||||
DivSample* droppedSample=NULL;
|
//DivSample* droppedSample=NULL;
|
||||||
if (!instruments.empty()) {
|
if (!instruments.empty()) {
|
||||||
if (e->song.sampleLen!=sampleCountBefore) {
|
if (e->song.sampleLen!=sampleCountBefore) {
|
||||||
e->renderSamplesP();
|
e->renderSamplesP();
|
||||||
|
@ -3854,10 +3855,24 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
nextWindow=GUI_WINDOW_WAVE_LIST;
|
nextWindow=GUI_WINDOW_WAVE_LIST;
|
||||||
MARK_MODIFIED;
|
MARK_MODIFIED;
|
||||||
} else if ((droppedSample=e->sampleFromFile(ev.drop.file))!=NULL) {
|
}
|
||||||
|
else if (!samples.empty())
|
||||||
|
{
|
||||||
|
if (e->song.sampleLen!=sampleCountBefore) {
|
||||||
|
//e->renderSamplesP();
|
||||||
|
}
|
||||||
|
if (!e->getWarnings().empty())
|
||||||
|
{
|
||||||
|
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||||
|
}
|
||||||
int sampleCount=-1;
|
int sampleCount=-1;
|
||||||
sampleCount=e->addSamplePtr(droppedSample);
|
for (DivSample* s: samples)
|
||||||
if (sampleCount>=0 && settings.selectAssetOnLoad) {
|
{
|
||||||
|
sampleCount=e->addSamplePtr(s);
|
||||||
|
}
|
||||||
|
//sampleCount=e->addSamplePtr(droppedSample);
|
||||||
|
if (sampleCount>=0 && settings.selectAssetOnLoad)
|
||||||
|
{
|
||||||
curSample=sampleCount;
|
curSample=sampleCount;
|
||||||
updateSampleTex=true;
|
updateSampleTex=true;
|
||||||
}
|
}
|
||||||
|
@ -5319,24 +5334,43 @@ bool FurnaceGUI::loop() {
|
||||||
String errs=_("there were some errors while loading samples:\n");
|
String errs=_("there were some errors while loading samples:\n");
|
||||||
bool warn=false;
|
bool warn=false;
|
||||||
for (String i: fileDialog->getFileName()) {
|
for (String i: fileDialog->getFileName()) {
|
||||||
DivSample* s=e->sampleFromFile(i.c_str());
|
std::vector<DivSample*> samples=e->sampleFromFile(i.c_str());
|
||||||
if (s==NULL) {
|
if (samples.empty()) {
|
||||||
if (fileDialog->getFileName().size()>1) {
|
if (fileDialog->getFileName().size()>1) {
|
||||||
warn=true;
|
warn=true;
|
||||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||||
} else {;
|
} else {;
|
||||||
showError(e->getLastError());
|
showError(e->getLastError());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (e->addSamplePtr(s)==-1) {
|
else
|
||||||
if (fileDialog->getFileName().size()>1) {
|
{
|
||||||
warn=true;
|
if((int)samples.size() == 1)
|
||||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
{
|
||||||
} else {
|
if (e->addSamplePtr(samples[0]) == -1)
|
||||||
showError(e->getLastError());
|
{
|
||||||
|
if (fileDialog->getFileName().size()>1)
|
||||||
|
{
|
||||||
|
warn=true;
|
||||||
|
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError(e->getLastError());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
MARK_MODIFIED;
|
{
|
||||||
|
MARK_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (DivSample* s: samples) { //ask which samples to load!
|
||||||
|
pendingSamples.push_back(std::make_pair(s,false));
|
||||||
|
}
|
||||||
|
displayPendingSamples=true;
|
||||||
|
replacePendingSample = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5345,24 +5379,44 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI_FILE_SAMPLE_OPEN_REPLACE: {
|
case GUI_FILE_SAMPLE_OPEN_REPLACE:
|
||||||
DivSample* s=e->sampleFromFile(copyOfName.c_str());
|
{
|
||||||
if (s==NULL) {
|
std::vector<DivSample*> samples=e->sampleFromFile(copyOfName.c_str());
|
||||||
|
if (samples.empty())
|
||||||
|
{
|
||||||
showError(e->getLastError());
|
showError(e->getLastError());
|
||||||
} else {
|
}
|
||||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
else
|
||||||
e->lockEngine([this,s]() {
|
{
|
||||||
// if it crashes here please tell me...
|
if((int)samples.size() == 1)
|
||||||
DivSample* oldSample=e->song.sample[curSample];
|
{
|
||||||
e->song.sample[curSample]=s;
|
if (curSample>=0 && curSample<(int)e->song.sample.size())
|
||||||
delete oldSample;
|
{
|
||||||
e->renderSamples();
|
DivSample* s = samples[0];
|
||||||
MARK_MODIFIED;
|
e->lockEngine([this, s]()
|
||||||
});
|
{
|
||||||
updateSampleTex=true;
|
// if it crashes here please tell me...
|
||||||
} else {
|
DivSample* oldSample=e->song.sample[curSample];
|
||||||
showError(_("...but you haven't selected a sample!"));
|
e->song.sample[curSample]= s;
|
||||||
delete s;
|
delete oldSample;
|
||||||
|
e->renderSamples();
|
||||||
|
MARK_MODIFIED;
|
||||||
|
});
|
||||||
|
updateSampleTex=true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showError(_("...but you haven't selected a sample!"));
|
||||||
|
delete samples[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (DivSample* s: samples) { //ask which samples to load!
|
||||||
|
pendingSamples.push_back(std::make_pair(s,false));
|
||||||
|
}
|
||||||
|
displayPendingSamples=true;
|
||||||
|
replacePendingSample = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -5741,6 +5795,11 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::OpenPopup(_("Select Instrument"));
|
ImGui::OpenPopup(_("Select Instrument"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (displayPendingSamples) {
|
||||||
|
displayPendingSamples=false;
|
||||||
|
ImGui::OpenPopup(_("Select Sample"));
|
||||||
|
}
|
||||||
|
|
||||||
if (displayPendingRawSample) {
|
if (displayPendingRawSample) {
|
||||||
displayPendingRawSample=false;
|
displayPendingRawSample=false;
|
||||||
ImGui::OpenPopup(_("Import Raw Sample"));
|
ImGui::OpenPopup(_("Import Raw Sample"));
|
||||||
|
@ -6569,6 +6628,191 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix style
|
||||||
|
centerNextWindow(_("Select Sample"),canvasW,canvasH);
|
||||||
|
if (ImGui::BeginPopupModal(_("Select Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
bool quitPlease=false;
|
||||||
|
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::Text(_("this is a sample bank! select which ones to load:"));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(_("All"))) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(_("None"))) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool reissueSearch=false;
|
||||||
|
|
||||||
|
bool anySelected=false;
|
||||||
|
float sizeY=ImGui::GetFrameHeightWithSpacing()*pendingSamples.size();
|
||||||
|
if (sizeY>(canvasH-180.0*dpiScale))
|
||||||
|
{
|
||||||
|
sizeY=canvasH-180.0*dpiScale;
|
||||||
|
if (sizeY<60.0*dpiScale) sizeY=60.0*dpiScale;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTable("PendingSamplesList",1,ImGuiTableFlags_ScrollY,ImVec2(0.0f,sizeY)))
|
||||||
|
{
|
||||||
|
if (sampleBankSearchQuery.empty())
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<pendingSamples.size(); i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
String id=fmt::sprintf("%d: %s",(int)i,pendingSamples[i].first->name);
|
||||||
|
if (pendingInsSingle)
|
||||||
|
{
|
||||||
|
if (ImGui::Selectable(id.c_str()))
|
||||||
|
{
|
||||||
|
pendingSamples[i].second=true;
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO:fixstyle from hereonwards
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Checkbox(id.c_str(),&pendingSamples[i].second) && io.KeyShift)
|
||||||
|
{
|
||||||
|
for(int jj = (int)i - 1; jj >= 0; jj--)
|
||||||
|
{
|
||||||
|
if(pendingSamples[jj].second) //pressed shift and there's selected item above
|
||||||
|
{
|
||||||
|
for(int k = jj; k < (int)i; k++)
|
||||||
|
{
|
||||||
|
pendingSamples[k].second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pendingSamples[i].second) anySelected=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //display search results
|
||||||
|
{
|
||||||
|
if(reissueSearch)
|
||||||
|
{
|
||||||
|
String lowerCase=sampleBankSearchQuery;
|
||||||
|
|
||||||
|
for (char& ii: lowerCase)
|
||||||
|
{
|
||||||
|
if (ii>='A' && ii<='Z') ii+='a'-'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleBankSearchResults.clear();
|
||||||
|
for (int j=0; j < (int)pendingSamples.size(); j++)
|
||||||
|
{
|
||||||
|
String lowerCase1 = pendingSamples[j].first->name;
|
||||||
|
|
||||||
|
for (char& ii: lowerCase1)
|
||||||
|
{
|
||||||
|
if (ii>='A' && ii<='Z') ii+='a'-'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lowerCase1.find(lowerCase)!=String::npos)
|
||||||
|
{
|
||||||
|
sampleBankSearchResults.push_back(std::make_pair(pendingSamples[j].first, pendingSamples[j].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<sampleBankSearchResults.size(); i++)
|
||||||
|
{
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
String id=fmt::sprintf("%d: %s",(int)i,sampleBankSearchResults[i].first->name);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Checkbox(id.c_str(),&sampleBankSearchResults[i].second) && io.KeyShift)
|
||||||
|
{
|
||||||
|
for(int jj = (int)i - 1; jj >= 0; jj--)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults[jj].second) //pressed shift and there's selected item above
|
||||||
|
{
|
||||||
|
for(int k = jj; k < (int)i; k++)
|
||||||
|
{
|
||||||
|
sampleBankSearchResults[k].second = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sampleBankSearchResults[i].second) anySelected=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<pendingSamples.size(); i++)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults.size() > 0)
|
||||||
|
{
|
||||||
|
for (size_t j=0; j<sampleBankSearchResults.size(); j++)
|
||||||
|
{
|
||||||
|
if(sampleBankSearchResults[j].first == pendingSamples[i].first && sampleBankSearchResults[j].second && pendingSamples[i].first != NULL)
|
||||||
|
{
|
||||||
|
pendingSamples[i].second = true;
|
||||||
|
if (pendingSamples[i].second) anySelected=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(!anySelected);
|
||||||
|
if (ImGui::Button(_("OK"))) {
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(_("Cancel")) || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples) {
|
||||||
|
i.second=false;
|
||||||
|
}
|
||||||
|
quitPlease=true;
|
||||||
|
}
|
||||||
|
if (quitPlease)
|
||||||
|
{
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
int counter = 0;
|
||||||
|
for (std::pair<DivSample*,bool>& i: pendingSamples)
|
||||||
|
{
|
||||||
|
if (!i.second)
|
||||||
|
{
|
||||||
|
delete i.first;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(counter == 0 && replacePendingSample)
|
||||||
|
{
|
||||||
|
*e->song.sample[curSample]=*i.first;
|
||||||
|
replacePendingSample = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e->addSamplePtr(i.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
curSample = (int)e->song.sample.size() - 1;
|
||||||
|
pendingSamples.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
centerNextWindow(_("Import Raw Sample"),canvasW,canvasH);
|
centerNextWindow(_("Import Raw Sample"),canvasW,canvasH);
|
||||||
if (ImGui::BeginPopupModal(_("Import Raw Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal(_("Import Raw Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text(_("Data type:"));
|
ImGui::Text(_("Data type:"));
|
||||||
|
@ -7581,7 +7825,15 @@ bool FurnaceGUI::init() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
compatFormats+="*.dmc ";
|
compatFormats+="*.dmc ";
|
||||||
compatFormats+="*.brr";
|
compatFormats+="*.brr ";
|
||||||
|
|
||||||
|
compatFormats+="*.ppc ";
|
||||||
|
compatFormats+="*.pps ";
|
||||||
|
compatFormats+="*.pvi ";
|
||||||
|
compatFormats+="*.pdx ";
|
||||||
|
compatFormats+="*.pzi ";
|
||||||
|
compatFormats+="*.p86 ";
|
||||||
|
compatFormats+="*.p";
|
||||||
audioLoadFormats[1]=compatFormats;
|
audioLoadFormats[1]=compatFormats;
|
||||||
|
|
||||||
audioLoadFormats.push_back(_("NES DPCM data"));
|
audioLoadFormats.push_back(_("NES DPCM data"));
|
||||||
|
@ -7590,6 +7842,27 @@ bool FurnaceGUI::init() {
|
||||||
audioLoadFormats.push_back(_("SNES Bit Rate Reduction"));
|
audioLoadFormats.push_back(_("SNES Bit Rate Reduction"));
|
||||||
audioLoadFormats.push_back("*.brr");
|
audioLoadFormats.push_back("*.brr");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD YM2608 ADPCM-B sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.ppc");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PDR 4-bit AY-3-8910 sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pps");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("FMP YM2608 ADPCM-B sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pvi");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("MDX OKI ADPCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pdx");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("FMP 8-bit PCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.pzi");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD 8-bit PCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.p86");
|
||||||
|
|
||||||
|
audioLoadFormats.push_back(_("PMD OKI ADPCM sample bank"));
|
||||||
|
audioLoadFormats.push_back("*.p");
|
||||||
|
|
||||||
audioLoadFormats.push_back(_("all files"));
|
audioLoadFormats.push_back(_("all files"));
|
||||||
audioLoadFormats.push_back("*");
|
audioLoadFormats.push_back("*");
|
||||||
|
|
||||||
|
@ -8012,6 +8285,8 @@ FurnaceGUI::FurnaceGUI():
|
||||||
snesFilterHex(false),
|
snesFilterHex(false),
|
||||||
modTableHex(false),
|
modTableHex(false),
|
||||||
displayEditString(false),
|
displayEditString(false),
|
||||||
|
displayPendingSamples(false),
|
||||||
|
replacePendingSample(false),
|
||||||
displayExportingROM(false),
|
displayExportingROM(false),
|
||||||
changeCoarse(false),
|
changeCoarse(false),
|
||||||
mobileEdit(false),
|
mobileEdit(false),
|
||||||
|
|
|
@ -816,6 +816,7 @@ enum FurnaceGUIActions {
|
||||||
GUI_ACTION_PAT_LATCH,
|
GUI_ACTION_PAT_LATCH,
|
||||||
GUI_ACTION_PAT_SCROLL_MODE,
|
GUI_ACTION_PAT_SCROLL_MODE,
|
||||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||||
|
GUI_ACTION_PAT_ABSORB_INSTRUMENT,
|
||||||
GUI_ACTION_PAT_MAX,
|
GUI_ACTION_PAT_MAX,
|
||||||
|
|
||||||
GUI_ACTION_INS_LIST_MIN,
|
GUI_ACTION_INS_LIST_MIN,
|
||||||
|
@ -1591,7 +1592,7 @@ class FurnaceGUI {
|
||||||
int sampleTexW, sampleTexH;
|
int sampleTexW, sampleTexH;
|
||||||
bool updateSampleTex;
|
bool updateSampleTex;
|
||||||
|
|
||||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery;
|
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
|
||||||
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
||||||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
||||||
String workingDirFont, workingDirColors, workingDirKeybinds;
|
String workingDirFont, workingDirColors, workingDirKeybinds;
|
||||||
|
@ -1603,6 +1604,7 @@ class FurnaceGUI {
|
||||||
String folderString;
|
String folderString;
|
||||||
|
|
||||||
std::vector<DivSystem> sysSearchResults;
|
std::vector<DivSystem> sysSearchResults;
|
||||||
|
std::vector<std::pair<DivSample*,bool>> sampleBankSearchResults;
|
||||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||||
std::vector<int> paletteSearchResults;
|
std::vector<int> paletteSearchResults;
|
||||||
FixedQueue<String,32> recentFile;
|
FixedQueue<String,32> recentFile;
|
||||||
|
@ -1618,6 +1620,7 @@ class FurnaceGUI {
|
||||||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
||||||
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
||||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||||
|
bool displayPendingSamples, replacePendingSample;
|
||||||
bool displayExportingROM;
|
bool displayExportingROM;
|
||||||
bool changeCoarse;
|
bool changeCoarse;
|
||||||
bool mobileEdit;
|
bool mobileEdit;
|
||||||
|
@ -1959,6 +1962,7 @@ class FurnaceGUI {
|
||||||
unsigned int maxUndoSteps;
|
unsigned int maxUndoSteps;
|
||||||
float vibrationStrength;
|
float vibrationStrength;
|
||||||
int vibrationLength;
|
int vibrationLength;
|
||||||
|
int s3mOPL3;
|
||||||
String mainFontPath;
|
String mainFontPath;
|
||||||
String headFontPath;
|
String headFontPath;
|
||||||
String patFontPath;
|
String patFontPath;
|
||||||
|
@ -2217,6 +2221,7 @@ class FurnaceGUI {
|
||||||
maxUndoSteps(100),
|
maxUndoSteps(100),
|
||||||
vibrationStrength(0.5f),
|
vibrationStrength(0.5f),
|
||||||
vibrationLength(20),
|
vibrationLength(20),
|
||||||
|
s3mOPL3(1),
|
||||||
mainFontPath(""),
|
mainFontPath(""),
|
||||||
headFontPath(""),
|
headFontPath(""),
|
||||||
patFontPath(""),
|
patFontPath(""),
|
||||||
|
@ -2373,6 +2378,7 @@ class FurnaceGUI {
|
||||||
std::vector<DivCommand> cmdStream;
|
std::vector<DivCommand> cmdStream;
|
||||||
std::vector<Particle> particles;
|
std::vector<Particle> particles;
|
||||||
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
||||||
|
std::vector<std::pair<DivSample*,bool>> pendingSamples;
|
||||||
|
|
||||||
std::vector<FurnaceGUISysCategory> sysCategories;
|
std::vector<FurnaceGUISysCategory> sysCategories;
|
||||||
|
|
||||||
|
|
|
@ -687,6 +687,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
||||||
D("PAT_LATCH", _N("Set note input latch"), 0),
|
D("PAT_LATCH", _N("Set note input latch"), 0),
|
||||||
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
|
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
|
||||||
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
|
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
|
||||||
|
D("PAT_ABSORB_INSTRUMENT", _N("Set current instrument to channel's current instrument column"), 0),
|
||||||
D("PAT_MAX", "", NOT_AN_ACTION),
|
D("PAT_MAX", "", NOT_AN_ACTION),
|
||||||
|
|
||||||
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),
|
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
#include "../ta-utils.h"
|
||||||
|
|
||||||
struct FurnacePlotArrayGetterData
|
struct FurnacePlotArrayGetterData
|
||||||
{
|
{
|
||||||
const float* Values;
|
const float* Values;
|
||||||
|
@ -270,12 +272,13 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx),
|
||||||
float lineHeight=ImGui::GetTextLineHeight()/2.0;
|
float lineHeight=ImGui::GetTextLineHeight()/2.0;
|
||||||
for (int i=0; i<bits && overlay_text[i]; i++) {
|
for (int i=0; i<bits && overlay_text[i]; i++) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(0,0,0,1.0f));
|
ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(0,0,0,1.0f));
|
||||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight-1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
const char* text=_(overlay_text[i]);
|
||||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight+1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight-1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight-1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight+1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight+1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight-1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||||
|
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight+1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y-lineHeight), ImVec2(frame_bb.Max.x,frame_bb.Max.y+lineHeight), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y-lineHeight), ImVec2(frame_bb.Max.x,frame_bb.Max.y+lineHeight), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1215,28 +1215,63 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
SUB_ENTRY(
|
||||||
|
"Sega TeraDrive", {
|
||||||
|
CH(DIV_SYSTEM_YM2612, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
SUB_SUB_ENTRY(
|
||||||
|
"Sega TeraDrive (extended channel 3)", {
|
||||||
|
CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
SUB_SUB_ENTRY(
|
||||||
|
"Sega TeraDrive (CSM)", {
|
||||||
|
CH(DIV_SYSTEM_YM2612_CSM, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
SUB_SUB_ENTRY(
|
||||||
|
"Sega TeraDrive (DualPCM)", {
|
||||||
|
CH(DIV_SYSTEM_YM2612_DUALPCM, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
SUB_SUB_ENTRY(
|
||||||
|
"Sega TeraDrive (DualPCM, extended channel 3)", {
|
||||||
|
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 1.0f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||||
|
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||||
|
}
|
||||||
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"Sharp X1", {
|
"Sharp X1", {
|
||||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3")
|
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
SUB_ENTRY(
|
SUB_ENTRY(
|
||||||
"Sharp X1 + FM Addon", {
|
"Sharp X1 + FM Addon", {
|
||||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3"),
|
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3"),
|
||||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2")
|
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"Sharp X68000", {
|
"Sharp X68000", {
|
||||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"),
|
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"),
|
||||||
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "clockSel=2")
|
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "clockSel=2")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ENTRY(
|
ENTRY(
|
||||||
"FM-7", {
|
"FM-7", {
|
||||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=12"),
|
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=12"),
|
||||||
CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=5")
|
CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=5")
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
SUB_ENTRY(
|
SUB_ENTRY(
|
||||||
"FM-7 (extended channel 3)", {
|
"FM-7 (extended channel 3)", {
|
||||||
|
|
|
@ -1259,6 +1259,14 @@ void FurnaceGUI::drawSettings() {
|
||||||
}
|
}
|
||||||
popDestColor();
|
popDestColor();
|
||||||
|
|
||||||
|
// SUBSECTION IMPORT
|
||||||
|
CONFIG_SUBSECTION(_("Import"));
|
||||||
|
bool s3mOPL3B=settings.s3mOPL3;
|
||||||
|
if (ImGui::Checkbox(_("Use OPL3 instead of OPL2 for S3M import"),&s3mOPL3B)) {
|
||||||
|
settings.s3mOPL3=s3mOPL3B;
|
||||||
|
settingsChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
END_SECTION;
|
END_SECTION;
|
||||||
}
|
}
|
||||||
CONFIG_SECTION(_("Audio")) {
|
CONFIG_SECTION(_("Audio")) {
|
||||||
|
@ -2431,6 +2439,7 @@ void FurnaceGUI::drawSettings() {
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_EXPAND_SONG);
|
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_EXPAND_SONG);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
|
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
|
||||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
|
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
|
||||||
|
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT);
|
||||||
|
|
||||||
KEYBIND_CONFIG_END;
|
KEYBIND_CONFIG_END;
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
|
@ -4761,6 +4770,8 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
||||||
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
||||||
|
|
||||||
|
settings.s3mOPL3=conf.getInt("s3mOPL3",1);
|
||||||
|
|
||||||
settings.backupEnable=conf.getInt("backupEnable",1);
|
settings.backupEnable=conf.getInt("backupEnable",1);
|
||||||
settings.backupInterval=conf.getInt("backupInterval",30);
|
settings.backupInterval=conf.getInt("backupInterval",30);
|
||||||
settings.backupMaxCopies=conf.getInt("backupMaxCopies",5);
|
settings.backupMaxCopies=conf.getInt("backupMaxCopies",5);
|
||||||
|
@ -5277,6 +5288,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
clampSetting(settings.backupMaxCopies,1,100);
|
clampSetting(settings.backupMaxCopies,1,100);
|
||||||
clampSetting(settings.autoFillSave,0,1);
|
clampSetting(settings.autoFillSave,0,1);
|
||||||
clampSetting(settings.autoMacroStepSize,0,1);
|
clampSetting(settings.autoMacroStepSize,0,1);
|
||||||
|
clampSetting(settings.s3mOPL3,0,1);
|
||||||
|
|
||||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||||
|
@ -5350,6 +5362,8 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
conf.set("vibrationStrength",settings.vibrationStrength);
|
conf.set("vibrationStrength",settings.vibrationStrength);
|
||||||
conf.set("vibrationLength",settings.vibrationLength);
|
conf.set("vibrationLength",settings.vibrationLength);
|
||||||
|
|
||||||
|
conf.set("s3mOPL3",settings.s3mOPL3);
|
||||||
|
|
||||||
conf.set("backupEnable",settings.backupEnable);
|
conf.set("backupEnable",settings.backupEnable);
|
||||||
conf.set("backupInterval",settings.backupInterval);
|
conf.set("backupInterval",settings.backupInterval);
|
||||||
conf.set("backupMaxCopies",settings.backupMaxCopies);
|
conf.set("backupMaxCopies",settings.backupMaxCopies);
|
||||||
|
|
Loading…
Reference in a new issue