mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-22 20:45:11 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into ymf278b
This commit is contained in:
commit
7fb7d32bd5
41 changed files with 1972 additions and 387 deletions
|
@ -682,6 +682,14 @@ src/engine/fileOps/text.cpp
|
|||
src/engine/fileOps/tfm.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/brrUtils.c
|
||||
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)
|
||||
- [instrument/wavetable/sample list](asset-list.md)
|
||||
- [song information](song-info.md)
|
||||
- [effect list window](effect-list-window.md)
|
||||
- [pattern view](../3-pattern/README.md)
|
||||
- [effect list window](effect-list-window.md)
|
||||
- [instrument editor](../4-instrument/README.md)
|
||||
- [wavetable editor](../5-wave/README.md)
|
||||
- [sample editor](../6-sample/README.md)
|
||||
|
||||
## advanced topics
|
||||
|
||||
- [mixer](../8-advanced/mixer.md)
|
||||
- [grooves](../8-advanced/grooves.md)
|
||||
- [channels](../8-advanced/channels.md)
|
||||
- [pattern manager](../8-advanced/pat-manager.md)
|
||||
- [chip manager](../8-advanced/chip-manager.md)
|
||||
- [compatibility flags](../8-advanced/compat-flags.md)
|
||||
- [song comments](../8-advanced/comments.md)
|
||||
- [piano/input pad](../8-advanced/piano.md)
|
||||
- [channels](../8-advanced/channels.md)
|
||||
- [chip manager](../8-advanced/chip-manager.md)
|
||||
- [pattern manager](../8-advanced/pat-manager.md)
|
||||
- [mixer](../8-advanced/mixer.md)
|
||||
- [compatibility flags](../8-advanced/compat-flags.md)
|
||||
- [oscilloscope](../8-advanced/osc.md)
|
||||
- [oscilloscope (per channel)](../8-advanced/chanosc.md)
|
||||
- [oscilloscope (X-Y)](../8-advanced/xyosc.md)
|
||||
- [clock](../8-advanced/clock.md)
|
||||
- [register view](../8-advanced/regview.md)
|
||||
- [grooves](../8-advanced/grooves.md)
|
||||
- [log viewer](../8-advanced/log-viewer.md)
|
||||
- [register view](../8-advanced/regview.md)
|
||||
- [statistics](../8-advanced/stats.md)
|
||||
- [memory composition](../8-advanced/memory-composition.md)
|
||||
|
||||
## other topics
|
||||
|
||||
- [basic mode](basic-mode.md)
|
||||
- [piano/input pad](../8-advanced/piano.md)
|
||||
- [settings](settings.md)
|
||||
|
|
|
@ -2,25 +2,27 @@
|
|||
|
||||
Furnace allows you to export your song in several formats. this section deals with describing the available export options.
|
||||
|
||||
## export audio
|
||||
## audio
|
||||
|
||||
this option allows you to export your song in .wav format. I know I know, no .mp3 or .ogg export yet, but you can use a converter.
|
||||
|
||||
there are two parameters:
|
||||
|
||||
- **Export type**:
|
||||
- **one file**: exports your song to one .wav file.
|
||||
- **multiple files (one per chip)**: exports the output of each chip to .wav files.
|
||||
- **multiple files (one per channel)**: exports the output of each channel to .wav files.
|
||||
- useful for usage with a channel visualizer such as corrscope.
|
||||
- **Bit depth**: default is 16-bit integer.
|
||||
- **Sample rate**: affects the quality of the output file.
|
||||
- default is 44100, "CD quality".
|
||||
- lower sample rates lose fidelity as upper frequencies disappear.
|
||||
- higher sample rates gain frequencies that can't be heard at the cost of file size and rendering time.
|
||||
- **Channels in file**: default is 2 (stereo). Set to 1 for mono.
|
||||
- **Loops**: sets the number of times the song will loop.
|
||||
- does not have effect if the song ends with `FFxx` effect.
|
||||
- **Fade out (seconds)**: sets the fade out time when the song is over.
|
||||
- does not have effect if the song ends with `FFxx` effect.
|
||||
|
||||
and three export choices:
|
||||
|
||||
- **one file**: exports your song to one .wav file.
|
||||
- **multiple files (one per chip)**: exports the output of each chip to .wav files.
|
||||
- **multiple files (one per channel)**: exports the output of each channel to .wav files.
|
||||
- useful for usage with a channel visualizer such as corrscope.
|
||||
|
||||
## export VGM
|
||||
## VGM
|
||||
|
||||
this option allows exporting to a VGM (Video Game Music) file. these can be played back with VGMPlay (for example).
|
||||
|
||||
|
@ -40,9 +42,6 @@ the following settings exist:
|
|||
- **custom**: allows you to specify how many ticks to add.
|
||||
- `0` is effectively none, disabling loop trail completely.
|
||||
- this option will not appear if the loop modality isn't set to None as there wouldn't be a need to.
|
||||
- **chips to export**: select which chips are going to be exported.
|
||||
- due to VGM format limitations, you can only select up to two of each chip type.
|
||||
- some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version.
|
||||
- **add pattern change hints**: this option adds a "hint" when a pattern change occurs. only useful if you're a developer.
|
||||
- the format of the "hint" data block that gets written is: `67 66 FE ll ll ll ll 01 oo rr pp pp pp ...`
|
||||
- `ll`: length, a 32-bit little-endian number
|
||||
|
@ -51,14 +50,11 @@ the following settings exist:
|
|||
- `pp`: pattern index (one per channel)
|
||||
- **direct stream mode**: this option allows DualPCM to work. don't use this for other chips.
|
||||
- may or may not play well with hardware VGM players.
|
||||
- **chips to export**: select which chips are going to be exported.
|
||||
- due to VGM format limitations, you can only select up to two of each chip type.
|
||||
- some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version.
|
||||
|
||||
click on **click to export** to begin exporting.
|
||||
|
||||
## export text
|
||||
|
||||
this option allows you to export your song as a text file.
|
||||
|
||||
## export ZSM
|
||||
## ZSM
|
||||
|
||||
ZSM (ZSound Music) is a format designed for the Commander X16 to allow hardware playback.
|
||||
it may contain data for either YM2151 or VERA chips.
|
||||
|
@ -72,21 +68,21 @@ the following settings are available:
|
|||
- **loop**: enables loop. if disabled, the song won't loop.
|
||||
- **optimize size**: removes unnecessary commands to reduce size.
|
||||
|
||||
click on **Begin Export** to... you know.
|
||||
## text
|
||||
|
||||
## export command stream
|
||||
this option allows you to export your song as a text file.
|
||||
|
||||
this option exports a binary file which contains a dump of the internal command stream produced when playing the song.
|
||||
## command stream
|
||||
|
||||
it's not really useful, unless you're a developer and want to use a command stream dump for some reason (e.g. writing a hardware sound driver).
|
||||
this option exports a binary file in Furnace's own command stream format (FCS) which contains a dump of the internal command stream produced when playing the song.
|
||||
|
||||
- **export**: exports in Furnace's own command stream format (FCS). see `export-tech.md` in `papers/` for details.
|
||||
it's not really useful, unless you're a developer and want to use a command stream dump for some reason (e.g. writing a hardware sound driver). see `export-tech.md` in `papers/` for details.
|
||||
|
||||
## export DMF
|
||||
## DMF
|
||||
|
||||
this option allows you to save your song as a .dmf which can be opened in DefleMask.
|
||||
|
||||
the following systems are supported when saving as 1.0/legacy:
|
||||
the following systems are supported when saving as 1.0/legacy (0.12):
|
||||
- Sega Genesis/Mega Drive (YM2612 + SN76489)
|
||||
- Sega Genesis/Mega Drive (YM2612 + SN76489, extended channel 3)
|
||||
- Sega Master System
|
||||
|
@ -97,7 +93,7 @@ the following systems are supported when saving as 1.0/legacy:
|
|||
- Arcade (YM2151 + SegaPCM 5-channel compatibility)
|
||||
- Neo Geo CD (DefleMask 1.0+)
|
||||
|
||||
the following systems are supported when saving as 1.1.3+:
|
||||
the following systems are also supported when saving as 1.1.3+:
|
||||
- Sega Master System (with FM expansion)
|
||||
- NES + Konami VRC7
|
||||
- Famicom Disk System
|
||||
|
|
|
@ -39,7 +39,7 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
|||
| Panic | `F12` |
|
||||
| | |
|
||||
| **Window activation** | |
|
||||
| Find/Replace | Ctrl-F |
|
||||
| Find/Replace | `Ctrl-F` |
|
||||
| Settings | — |
|
||||
| Song Information | — |
|
||||
| Subsongs | — |
|
||||
|
@ -63,16 +63,23 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
|||
| Piano | — |
|
||||
| Oscilloscope (master) | — |
|
||||
| Oscilloscope (per-channel) | — |
|
||||
| Oscilloscope (X-Y) | — |
|
||||
| Volume Meter | — |
|
||||
| Clock | — |
|
||||
| Register View | — |
|
||||
| Log Viewer | — |
|
||||
| Statistics | — |
|
||||
| Memory Composition | — |
|
||||
| Effect List | — |
|
||||
| Debug Menu | `Ctrl-Shift-D` |
|
||||
| Command Stream Player | — |
|
||||
| About | — |
|
||||
| Collapse/expand current window | — |
|
||||
| Close current window | `Shift-Escape` |
|
||||
| Command Palette | `Ctrl-P` |
|
||||
| Recent files (Palette) | — |
|
||||
| Insstruments (Palette) | — |
|
||||
| Samples (Palette) | — |
|
||||
| | |
|
||||
| **Note input** | |
|
||||
| _see "note input" section after table_ | |
|
||||
|
@ -102,8 +109,8 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
|||
| Move cursor down by one (override Edit Step) | `Shift-End` |
|
||||
| Move cursor to previous channel | — |
|
||||
| Move cursor to next channel | — |
|
||||
| Move cursor to next channel (overflow) | — |
|
||||
| Move cursor to previous channel (overflow) | — |
|
||||
| Move cursor to next channel (overflow) | — |
|
||||
| Move cursor to beginning of pattern | `Home` |
|
||||
| Move cursor to end of pattern | `End` |
|
||||
| Move cursor up (coarse) | `PageUp` |
|
||||
|
@ -118,6 +125,10 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
|||
| Expand selection to end of pattern | — |
|
||||
| Expand selection upwards (coarse) | `Shift-PageUp` |
|
||||
| Expand selection downwards (coarse) | `Shift-PageDown` |
|
||||
| Move selection up by one | `Alt-Up` |
|
||||
| Move selection down by one | `Alt-Down` |
|
||||
| Move selection to previous channel | `Alt-Left` |
|
||||
| Move selection to next channel | `Alt-Right` |
|
||||
| Delete | `Delete` |
|
||||
| Pull delete | `Backspace` |
|
||||
| Insert | `Insert` |
|
||||
|
@ -143,102 +154,103 @@ the keys in the "Global hotkeys" section can be used in any window, although not
|
|||
| Clear note input latch | — |
|
||||
| | |
|
||||
| **Instrument list** | |
|
||||
| Add | `Insert` |
|
||||
| Duplicate | `Ctrl-D` |
|
||||
| Open | — |
|
||||
| Open (replace current) | — |
|
||||
| Save | — |
|
||||
| Save (.dmp) | — |
|
||||
| Move up | `Shift-Up` |
|
||||
| Move down | `Shift-Down` |
|
||||
| Delete | — |
|
||||
| Edit | `Shift-Return` |
|
||||
| Cursor up | `Up` |
|
||||
| Cursor down | `Down` |
|
||||
| Toggle folders/standard view | `Ctrl-V` |
|
||||
| Add instrument | `Insert` |
|
||||
| Duplicate instrument | `Ctrl-D` |
|
||||
| Open instrument | — |
|
||||
| Open instrument (replace current) | — |
|
||||
| Save instrument | — |
|
||||
| Save instrument (.dmp) | — |
|
||||
| Move instrument up in list | `Shift-Up` |
|
||||
| Move instrument down in list | `Shift-Down` |
|
||||
| Delete instrument | — |
|
||||
| Edit instrument | `Shift-Return` |
|
||||
| Instrument cursor up | `Up` |
|
||||
| Instrument cursor down | `Down` |
|
||||
| Instruments: toggle folders/standard view | `Ctrl-V` |
|
||||
| | |
|
||||
| **Wavetable list** | |
|
||||
| Add | `Insert` |
|
||||
| Duplicate | `Ctrl-D` |
|
||||
| Open | — |
|
||||
| Open (replace current) | — |
|
||||
| Save | — |
|
||||
| Save (.dmw) | — |
|
||||
| Save (raw) | — |
|
||||
| Move up | `Shift-Up` |
|
||||
| Move down | `Shift-Down` |
|
||||
| Delete | — |
|
||||
| Edit | `Shift-Return` |
|
||||
| Cursor up | `Up` |
|
||||
| Cursor down | `Down` |
|
||||
| Toggle folders/standard view | `Ctrl-V` |
|
||||
| Add wavetable | `Insert` |
|
||||
| Duplicate wavetable | `Ctrl-D` |
|
||||
| Open wavetable | — |
|
||||
| Open wavetable (replace current) | — |
|
||||
| Save wavetable | — |
|
||||
| Save wavetable (.dmw) | — |
|
||||
| Save wavetable (raw) | — |
|
||||
| Move wavetable up in list | `Shift-Up` |
|
||||
| Move wavetable down in list | `Shift-Down` |
|
||||
| Delete wavetable | — |
|
||||
| Edit wavetable | `Shift-Return` |
|
||||
| Wavetable cursor up | `Up` |
|
||||
| Wavetable cursor down | `Down` |
|
||||
| Wavetables: toggle folders/standard view | `Ctrl-V` |
|
||||
| | |
|
||||
| **Sample list** | |
|
||||
| Add | `Insert` |
|
||||
| Duplicate | `Ctrl-D` |
|
||||
| Create wavetable from selection | `Ctrl-W` |
|
||||
| Open | — |
|
||||
| Open (replace current) | — |
|
||||
| Import raw data | — |
|
||||
| Import raw data (replace current) | — |
|
||||
| Save | — |
|
||||
| Save (raw) | — |
|
||||
| Move up | `Shift-Up` |
|
||||
| Move down | `Shift-Down` |
|
||||
| Delete | — |
|
||||
| Edit | `Shift-Return` |
|
||||
| Cursor up | `Up` |
|
||||
| Cursor down | `Down` |
|
||||
| Preview | — |
|
||||
| Stop preview | — |
|
||||
| Toggle folders/standard view | `Ctrl-V` |
|
||||
| Add sample | `Insert` |
|
||||
| Duplicate sample | `Ctrl-D` |
|
||||
| Sample Editor: Create wavetable from selection | `Ctrl-W` |
|
||||
| Open sample | — |
|
||||
| Open sample (replace current) | — |
|
||||
| Import raw sample data | — |
|
||||
| Import raw sample data (replace current) | — |
|
||||
| Save sample | — |
|
||||
| Save sample (raw) | — |
|
||||
| Move sample up in list | `Shift-Up` |
|
||||
| Move sample down in list | `Shift-Down` |
|
||||
| Delete sample | — |
|
||||
| Edit sample | `Shift-Return` |
|
||||
| Sample cursor up | `Up` |
|
||||
| Sample cursor down | `Down` |
|
||||
| Sample Preview | — |
|
||||
| Stop sample preview | — |
|
||||
| Samples: Toggle folders/standard view | `Ctrl-V` |
|
||||
| Samples: Make me a drum kit | — |
|
||||
| | |
|
||||
| **Orders** | |
|
||||
| Previous order | `Up` |
|
||||
| Next order | `Down` |
|
||||
| Cursor left | `Left` |
|
||||
| Cursor right | `Right` |
|
||||
| Increase value | — |
|
||||
| Decrease value | — |
|
||||
| Switch edit mode | — |
|
||||
| Toggle alter entire row | `Ctrl-L` |
|
||||
| Add | `Insert` |
|
||||
| Duplicate | `Ctrl-D` |
|
||||
| Deep clone | `Ctrl-Shift-D` |
|
||||
| Duplicate to end of song | `Ctrl-E` |
|
||||
| Deep clone to end of song | `Ctrl-Shift-E` |
|
||||
| Remove | `Delete` |
|
||||
| Move up | `Shift-Up` |
|
||||
| Move down | `Shift-Down` |
|
||||
| Replay | — |
|
||||
| Order cursor left | `Left` |
|
||||
| Order cursor right | `Right` |
|
||||
| Increase order value | — |
|
||||
| Decrease order value | — |
|
||||
| Switch order edit mode | — |
|
||||
| Order: Toggle alter entire row | `Ctrl-L` |
|
||||
| Add order | `Insert` |
|
||||
| Duplicate order | `Ctrl-D` |
|
||||
| Deep clone order | `Ctrl-Shift-D` |
|
||||
| Copy current order to end of song | `Ctrl-E` |
|
||||
| Deep clone current order to end of song | `Ctrl-Shift-E` |
|
||||
| Remove order | `Delete` |
|
||||
| Move order up | `Shift-Up` |
|
||||
| Move order down | `Shift-Down` |
|
||||
| Replay order | — |
|
||||
| | |
|
||||
| **Sample editor** | |
|
||||
| Edit mode: Select | `Shift-I` |
|
||||
| Edit mode: Draw | `Shift-D` |
|
||||
| Cut | `Ctrl-X` |
|
||||
| Copy | `Ctrl-C` |
|
||||
| Paste | `Ctrl-V` |
|
||||
| Paste replace | `Ctrl-Shift-V` |
|
||||
| Paste mix | `Ctrl-Alt-V` |
|
||||
| Select all | `Ctrl-A` |
|
||||
| Resize | `Ctrl-R` |
|
||||
| Resample | `Ctrl-E` |
|
||||
| Amplify | `Ctrl-B` |
|
||||
| Normalize | `Ctrl-N` |
|
||||
| Fade in | `Ctrl-I` |
|
||||
| Fade out | `Ctrl-O` |
|
||||
| Insert silence | `Insert` |
|
||||
| Apply silence | `Shift-Delete` |
|
||||
| Delete | `Delete` |
|
||||
| Trim | `Ctrl-Delete` |
|
||||
| Reverse | `Ctrl-T` |
|
||||
| Invert | `Ctrl-Shift-T` |
|
||||
| Signed/unsigned exchange | `Ctrl-U` |
|
||||
| Apply filter | `Ctrl-F` |
|
||||
| Preview sample | — |
|
||||
| Stop sample preview | — |
|
||||
| Zoom in | `Ctrl-=` |
|
||||
| Zoom out | `Ctrl--` |
|
||||
| Toggle auto-zoom | `Ctrl-0` |
|
||||
| Create instrument from sample | — |
|
||||
| Set loop to selection | `Ctrl-L` |
|
||||
| Sample editor mode: Select | `Shift-I` |
|
||||
| Sample editor mode: Draw | `Shift-D` |
|
||||
| Sample editor: Cut | `Ctrl-X` |
|
||||
| Sample editor: Copy | `Ctrl-C` |
|
||||
| Sample editor: Paste | `Ctrl-V` |
|
||||
| Sample editor: Paste replace | `Ctrl-Shift-V` |
|
||||
| Sample editor: Paste mix | `Ctrl-Alt-V` |
|
||||
| Sample editor: Select all | `Ctrl-A` |
|
||||
| Sample editor: Resize | `Ctrl-R` |
|
||||
| Sample editor: Resample | `Ctrl-E` |
|
||||
| Sample editor: Amplify | `Ctrl-B` |
|
||||
| Sample editor: Normalize | `Ctrl-N` |
|
||||
| Sample editor: Fade in | `Ctrl-I` |
|
||||
| Sample editor: Fade out | `Ctrl-O` |
|
||||
| Sample editor: Insert silence | `Insert` |
|
||||
| Sample editor: Apply silence | `Shift-Delete` |
|
||||
| Sample editor: Delete | `Delete` |
|
||||
| Sample editor: Trim | `Ctrl-Delete` |
|
||||
| Sample editor: Reverse | `Ctrl-T` |
|
||||
| Sample editor: Invert | `Ctrl-Shift-T` |
|
||||
| Sample editor: Signed/unsigned exchange | `Ctrl-U` |
|
||||
| Sample editor: Apply filter | `Ctrl-F` |
|
||||
| Sample editor: Preview sample | — |
|
||||
| Sample editor: Stop sample preview | — |
|
||||
| Sample editor: Zoom in | `Ctrl-=` |
|
||||
| Sample editor: Zoom out | `Ctrl--` |
|
||||
| Sample editor: Toggle auto-zoom | `Ctrl-0` |
|
||||
| Sample editor: Create instrument from sample | — |
|
||||
| Sample editor: Set loop to selection | `Ctrl-L` |
|
||||
|
|
|
@ -2,24 +2,20 @@
|
|||
|
||||
the menu bar allows you to select from five menus: file, edit, settings, window and help.
|
||||
|
||||
items in _italic_ don't appear in basic mode and are only available in advanced mode.
|
||||
|
||||
## file
|
||||
|
||||
- **new...**: creates a new song.
|
||||
- **new...**: opens the new song dialog to choose a system.
|
||||
- click a system name to create a new song with it.
|
||||
- some systems have several variants, which are inside a group.
|
||||
- **open...**: opens the file picker, allowing you to select a song to open.
|
||||
- see [file formats](formats.md) for a list of formats Furnace is able to open.
|
||||
- **open recent**: contains a list of the songs you've opened before.
|
||||
- **clear history**: erases the file history.
|
||||
|
||||
- **save**: saves the current song.
|
||||
- opens the file picker if this is a new song, or a backup.
|
||||
- **save as...**: opens the file picker, allowing you to save the song under a different name.
|
||||
|
||||
- **export**: allows you to export your song into other formats, such as audio files, VGM and more. see the [export](export.md) page for more information.
|
||||
|
||||
- **export...**: allows you to export your song into other formats, such as audio files, VGM and more. see the [export](export.md) page for more information.
|
||||
- **manage chips**: opens the [Chip Manager](../8-advanced/chip-manager.md) dialog.
|
||||
|
||||
- **restore backup**: restores a previously saved backup.
|
||||
- Furnace keeps up to 5 backups of a song.
|
||||
- the backup directory is located in:
|
||||
|
@ -28,21 +24,18 @@ items in _italic_ don't appear in basic mode and are only available in advanced
|
|||
- Linux/other: `~/.config/furnace/backups`
|
||||
- this directory grows in size as you use Furnace. remember to delete old backups periodically to save space.
|
||||
- **do NOT rely on the backup system as auto-save!** you should save a restored backup because Furnace will not save backups of backups.
|
||||
|
||||
- **exit**: closes Furnace.
|
||||
|
||||
## edit
|
||||
|
||||
- **...**: does nothing except prevent accidental clicks on later menu items if the menu is too tall to fit on the program window.
|
||||
|
||||
- **undo**: reverts the last action.
|
||||
- **redo**: repeats what you undid previously.
|
||||
|
||||
- **cut**: moves the current selection in the pattern view to clipboard.
|
||||
- **copy**: copies the current selection in the pattern view to clipboard.
|
||||
- **paste**: inserts the clipboard's contents in the cursor position.
|
||||
- you may be able to paste from OpenMPT as well.
|
||||
- _**paste special...**:_ variants of the paste feature.
|
||||
- **paste special...**: variants of the paste feature.
|
||||
- **paste mix**: inserts the clipboard's contents in the cursor position, but does not erase the occupied region.
|
||||
- **paste mix (background)**: does the same thing as paste mix, but doesn't alter content which is already there.
|
||||
- **paste with ins (foreground)**: same thing as paste mix, but changes the instrument.
|
||||
|
@ -55,81 +48,84 @@ items in _italic_ don't appear in basic mode and are only available in advanced
|
|||
- if the selection is tall, it will select the entire column.
|
||||
- if a column is already selected, it will select the entire channel.
|
||||
- if a channel is already selected, it will select the entire pattern.
|
||||
|
||||
- _**operation mask**:_ toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md)
|
||||
- _**input latch**:_ determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md)
|
||||
|
||||
- **operation mask**: toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md)
|
||||
- **input latch**: determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md)
|
||||
- **note/octave up/down**: transposes notes in the current selection.
|
||||
|
||||
- **values up/down**: changes values in the current selection by ±1 or ±16.
|
||||
|
||||
- **transpose**: transpose notes or change values by a specific amount.
|
||||
|
||||
- **interpolate**: fills in gaps in the selection by interpolation between values.
|
||||
- **change instrument**: changes the instrument number in a selection.
|
||||
- **gradient/fade**: replace the selection with a "gradient" that goes from the beginning of the selection to the end.
|
||||
- **change instrument...**: changes the instrument number in a selection.
|
||||
- **gradient/fade...**: replace the selection with a "gradient" that goes from the beginning of the selection to the end.
|
||||
- does not affect the note column.
|
||||
- **Nibble mode**: when enabled, the fade will be per-nibble (0 to F) rather than per-value (00 to FF).
|
||||
- use for effects like `04xy` (vibrato).
|
||||
- **scale**: scales values in the selection by a specific amount.
|
||||
- **scale...**: scales values in the selection by a specific amount.
|
||||
- use to change volume in a selection for example.
|
||||
- **randomize**: replaces the selection with random values.
|
||||
- does not affect the note column.
|
||||
- **Nibble mode**: when enabled, the randomization will be per-nibble (0 to F) rather than per-value (00 to FF).
|
||||
- **invert values**: `00` becomes `FF`, `01` becomes `FE`, `02` becomes `FD` and so on.
|
||||
|
||||
- **flip selection**: flips the selection so it is backwards.
|
||||
- **collapse/expand amount**: allows you to specify how much to collapse/expand in the next two menu items.
|
||||
- **collapse**: shrinks the selected contents.
|
||||
- **expand**: expands the selected contents.
|
||||
|
||||
- **collapse pattern**: same as collapse, but affects the entire pattern.
|
||||
- **expand pattern**: same as expand, but affects the entire pattern.
|
||||
|
||||
- **collapse song**: same as collapse, but affects the entire song.
|
||||
- it also changes speeds and pattern length to compensate.
|
||||
- **expand song**: same as expand, but affects the entire song.
|
||||
- it also changes speeds and pattern length to compensate.
|
||||
|
||||
- _**find/replace**:_ shows [the Find/Replace window](../8-advanced/find-replace.md).
|
||||
|
||||
- **clear**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like.
|
||||
- **find/replace**: shows [the Find/Replace window](../8-advanced/find-replace.md).
|
||||
- **clear...**: opens a window that allows you to mass-delete things like songs, unused instruments, and the like.
|
||||
|
||||
## settings
|
||||
|
||||
- **full screen**: expands the Furnace window so it covers your screen.
|
||||
- **lock layout**: prevents you from dragging/resizing docked windows, or docking more.
|
||||
- **basic mode**: toggles [Basic Mode](basic-mode.md).
|
||||
- **visualizer**: toggles pattern view particle effects when the song plays.
|
||||
- **pattern visualizer**: toggles pattern view particle effects when the song plays.
|
||||
- **reset layout**: resets the workspace to its defaults.
|
||||
- **settings...**: shows the Settings window. these are detailed in [settings.md].
|
||||
- **user systems...**: shows the User Systems window. this is detailed in [the User Systems documentation](../8-advanced/user-systems.md).
|
||||
- **settings...**: shows the Settings window. these are detailed in [the Settings documentation](settings.md).
|
||||
|
||||
## window
|
||||
|
||||
all these menu items show or hide their associated windows.
|
||||
|
||||
- [song information](song-info.md)
|
||||
- [subsongs](song-info.md)
|
||||
- [speed](song-info.md)
|
||||
- [instruments](../4-instrument/README.md)
|
||||
- [wavetables](../5-wave/README.md)
|
||||
- [samples](../6-sample/README.md)
|
||||
- [orders](order-list.md)
|
||||
- [pattern](../3-pattern/README.md)
|
||||
- _[mixer](../8-advanced/mixer.md)_
|
||||
- _[grooves](../8-advanced/grooves.md)_
|
||||
- _[channels](../8-advanced/channels.md)_
|
||||
- _[pattern manager](../8-advanced/pat-manager.md)_
|
||||
- _[chip manager](../8-advanced/chip-manager.md)_
|
||||
- _[compatibility flags](../8-advanced/compat-flags.md)_
|
||||
- [song comments](../8-advanced/comments.md)
|
||||
|
||||
- [piano](../8-advanced/piano.md)
|
||||
- [oscilloscope](../8-advanced/osc.md)
|
||||
- [oscilloscopes (per-channel)](../8-advanced/chanosc.md)
|
||||
- [clock](../8-advanced/clock.md)
|
||||
- [register view](../8-advanced/regview.md)
|
||||
- [log viewer](../8-advanced/log-viewer.md)
|
||||
- [stats](../8-advanced/stats.md)
|
||||
- song
|
||||
- **[song comments](../8-advanced/comments.md)**
|
||||
- **[song information](song-info.md)**
|
||||
- **[subsongs](song-info.md)**
|
||||
- **[channels](../8-advanced/channels.md)**
|
||||
- **[chip manager](../8-advanced/chip-manager.md)**
|
||||
- **[orders](order-list.md)**
|
||||
- **[pattern](../3-pattern/README.md)**
|
||||
- **[pattern manager](../8-advanced/pat-manager.md)**
|
||||
- **[mixer](../8-advanced/mixer.md)**
|
||||
- **[compatibility flags](../8-advanced/compat-flags.md)**
|
||||
- assets
|
||||
- **[instruments](../4-instrument/README.md)**
|
||||
- **[samples](../6-sample/README.md)**
|
||||
- **[wavetables](../5-wave/README.md)**
|
||||
- **[instrument editor](../4-instrument/README.md)**
|
||||
- **[sample editor](../6-sample/README.md)**
|
||||
- **[wavetable editor](../5-wave/README.md)**
|
||||
- visualizers
|
||||
- **[oscilloscope](../8-advanced/osc.md)**
|
||||
- **[oscilloscope (per-channel)](../8-advanced/chanosc.md)**
|
||||
- **[oscilloscope (X-Y)](../8-advanced/xyosc.md)**
|
||||
- volume meter
|
||||
- tempo
|
||||
- **[clock](../8-advanced/clock.md)**
|
||||
- **[grooves](../8-advanced/grooves.md)**
|
||||
- **[speed](song-info.md)**
|
||||
- debug
|
||||
- **[log viewer](../8-advanced/log-viewer.md)**
|
||||
- **[register view](../8-advanced/regview.md)**
|
||||
- **[statistics](../8-advanced/stats.md)**
|
||||
- **[memory composition](../8-advanced/memory-composition.md)**
|
||||
- **[effect list](../3-pattern/effects.md)**
|
||||
- **[play/edit controls](play-edit-controls.md)**
|
||||
- **[piano/input pad](../8-advanced/piano.md)**
|
||||
|
||||
## help
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ the "Play/Edit Controls" are used to control playback and change parameters of t
|
|||
- **Poly**: turns on polyphony for previewing notes. toggles to **Mono** for monophony (one note at a time only).
|
||||
- **Octave**: sets current input octave.
|
||||
- **Step**: sets edit step. if this is 1, entering a note or effect will move to the next row. if this is a larger number, rows will be skipped. if this is 0, the cursor will stay in place.
|
||||
- if clicked, Step becomes **Coarse**, which sets the number of rows moved with `PgUp`, `PgDn`, and related movement shortcuts. clicking again will revert it to Step.
|
||||
- **Follow orders**: if on, the selected order in the orders window will follow the song during playback.
|
||||
- **Follow pattern**: if on, the cursor will follow playback and the song will scroll by as it plays.
|
||||
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
# settings
|
||||
|
||||
the Settings window allows you to change Furnace setting.
|
||||
the Settings window allows you to change Furnace settings.
|
||||
|
||||
settings are saved when clicking the **OK** or **Apply** buttons at the bottom of the window, and when closing the program. several backups are kept in the Furnace settings directory.
|
||||
|
||||
settings are saved when clicking the **OK** or **Apply** buttons at the bottom of the window.
|
||||
|
||||
## General
|
||||
|
||||
### Program
|
||||
|
||||
- **Language**: select the language used for the interface. some languages are incomplete, and are listed with their approximate completion percentage.
|
||||
- **Render backend**: changing this may help with performace or compatibility issues. the available render backends are:
|
||||
- SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS.
|
||||
- it is slower than the other backends.
|
||||
- DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows.
|
||||
- DirectX 9: use if your hardware is incompatible with DirectX 11.
|
||||
- OpenGL 3.0: works with the majority of graphics chips/cards (from 2010 onwards). default on Linux.
|
||||
- OpenGL 2.0: use if you have a card without OpenGL 3.0 support.
|
||||
- OpenGL 1.1: use if your card doesn't even support OpenGL 2.0.
|
||||
- DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows.
|
||||
- SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS.
|
||||
- it is slower than the other backends.
|
||||
- Software: this is a last resort backend which renders the interface in software. very slow!
|
||||
- **Render driver**: this setting appears when using the SDL Renderer backend. it allows you to select an SDL render driver.
|
||||
- **Advanced render backend settings**: only applicable with some render backends.
|
||||
- **Render driver**: this setting only appears when using the SDL Renderer backend. it allows you to select an SDL render driver.
|
||||
- OpenGL settings: these only appear when using an OpenGL backend, and should only be adjusted if the display is incorrect.
|
||||
- **VSync**: synchronizes rendering to VBlank and eliminates tearing.
|
||||
- **Frame rate limit**: allows you to set a frame rate limit (in frames per second).
|
||||
- only has effect when VSync is off or not available (e.g. software rendering or force-disabled on driver settings).
|
||||
- **Display render time**: displays frame rate and frame render time at the right side of the menu bar.
|
||||
- **Late render clear**: this option is only useful when using old versions of Mesa drivers. it force-waits for VBlank by clearing after present, reducing latency.
|
||||
- **Power-saving mode**: saves power by lowering the frame rate to 2fps when idle.
|
||||
- may cause issues under Mesa drivers!
|
||||
|
@ -49,6 +55,9 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
|||
- **Remember last values**
|
||||
- **Store instrument name in .fui**: when enabled, saving an instrument will store its name. this may increase file size.
|
||||
- **Load instrument name from .fui**: when enabled, loading an instrument will use the stored name (if present). otherwise, it will use the file name.
|
||||
- **Auto-fill file name when saving**: pre-fill the file name field when saving or exporting.
|
||||
- when saving a module, the existing file name will be auto-filled.
|
||||
- when saving an instrument or sample, its name will be auto-filled.
|
||||
|
||||
### New Song
|
||||
|
||||
|
@ -79,6 +88,11 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
|||
|
||||
- **New instruments are blank**: when enabled, adding FM instruments will make them blank (rather than loading the default one).
|
||||
|
||||
### Configuration
|
||||
- **Import**: select an exported `.ini` config file to overwrite current settings.
|
||||
- **Export**: select an `.ini` file to save current settings.
|
||||
- **Factory Reset**: resets all settings to default and purges settings backups.
|
||||
|
||||
## Audio
|
||||
|
||||
### Output
|
||||
|
@ -100,9 +114,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
|||
- **Sample rate**: audio output rate.
|
||||
- a lower rate decreases quality and isn't really beneficial.
|
||||
- if using PortAudio backend, be careful about this value.
|
||||
- **Outputs**: number of audio outputs created, up to 16.
|
||||
- only appears when Backend is JACK.
|
||||
- **Channels**: mono, stereo or something.
|
||||
- **Outputs**: number of audio outputs created, up to 16. default is 2 (stereo).
|
||||
- **Buffer size**: size of buffer in both samples and milliseconds.
|
||||
- setting this to a low value may cause stuttering/glitches in playback (known as "underruns" or "xruns").
|
||||
- setting this to a high value increases latency.
|
||||
|
@ -135,6 +147,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
|
|||
### MIDI input
|
||||
|
||||
- **MIDI input**: input device.
|
||||
- **Rescan MIDI devices**: repopulates list with all currently connected MIDI devices. useful if a device is connected while Furnace is running.
|
||||
- **Note input**: enables note input. disable if you intend to use this device only for binding actions.
|
||||
- **Velocity input**: enables velocity input when entering notes in the pattern.
|
||||
- **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position.
|
||||
|
@ -291,6 +304,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **Push value when overwriting instead of clearing it**: in the order list and pattern editors, typing into an already-filled value will shift digits instead of starting fresh. for example:
|
||||
- if off: moving the cursor onto the value `A5` and typing a "B" results in `0B`.
|
||||
- if on: moving the cursor onto the value `A5` and typing a "B" results in `5B`.
|
||||
- **Keyboard note/value input repeat (hold key to input continuously)**
|
||||
- **Effect input behavior:**
|
||||
- **Move down**: after entering an effect (or effect value), the cursor moves down.
|
||||
- **Move to effect value (otherwise move down)**: after entering an effect, the cursor moves to its value. if entering a value, the cursor moves down.
|
||||
|
@ -359,11 +373,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **Pattern font** font for the pattern view, the order list, and related.
|
||||
- if "Custom...", a file path selector will appear.
|
||||
- **Size**: font size.
|
||||
- **Display Japanese characters**, **Display Chinese (Simplified) characters**, **Display Chinese (Traditional) characters** and **Display Korean characters**: only toggle these options if you have enough graphics memory.
|
||||
- these are a temporary solution until dynamic font atlas is implemented in Dear ImGui.
|
||||
|
||||
#### FreeType-specific settings
|
||||
|
||||
- **Anti-aliased fonts**: when enabled, fonts will be rendered smooth.
|
||||
- **Support bitmap fonts**: this option allows you to enable the loading of bitmap fonts.
|
||||
- be noted that this may force non-bitmap fonts to undesired sizes!
|
||||
|
@ -376,6 +386,13 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **Disable**: only rely upon font hinting data.
|
||||
- **Enable**: prefer font hinting data if present.
|
||||
- **Force**: ignore font hinting data.
|
||||
#### non-specific settings
|
||||
- **Oversample**: renders the font internally at higher resolution for visual quality.
|
||||
- higher settings use more video memory.
|
||||
- for pixel or bitmap fonts, set this to **1x**.
|
||||
- **Load fallback font**: load an extra font that contains nearly all characters that can be used, in case the selected fonts lack them. uses much video memory
|
||||
- **Display Japanese characters**, **Display Chinese (Simplified) characters**, **Display Chinese (Traditional) characters** and **Display Korean characters**: only toggle these options if you have enough graphics memory.
|
||||
- these are a temporary solution until dynamic font atlas is implemented in Dear ImGui.
|
||||
|
||||
### Program
|
||||
|
||||
|
@ -386,15 +403,16 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **/path/to/file.fur - Furnace**
|
||||
- **Display system name on title bar**
|
||||
- **Display chip names instead of "multi-system" in title bar**
|
||||
- **Export options layout:**
|
||||
- **Sub-menus in File menu**: export options appear in the File menu as sub-menus.
|
||||
- **Modal window with tabs**: a single "export..." option that opens a dialog with export options. this is the default.
|
||||
- **Modal windows with options in File menu**: like Sub-menus in File menu, but instead of being sub-menus, selecting one opens a dialog with export settings.
|
||||
- **Status bar:**
|
||||
- **Cursor details**
|
||||
- **File path**
|
||||
- **Cursor details or file path**
|
||||
- **Nothing**
|
||||
- **Display playback status when playing**: display playback time and current location in the menu bar.
|
||||
- **Export options layout:**
|
||||
- **Sub-menus in File menu**: export options appear in the File menu as sub-menus.
|
||||
- **Modal window with tabs**: a single "export..." option that opens a dialog with export options. this is the default.
|
||||
- **Modal windows with options in File menu**: like Sub-menus in File menu, but instead of being sub-menus, selecting one opens a dialog with export settings.
|
||||
- **Capitalize menu bar**
|
||||
- **Display add/configure/change/remove chip menus in File menu**: if enabled, the "manage chips" item in the file menu is split into the four listed items for quick access.
|
||||
|
||||
|
@ -532,10 +550,10 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **Rounded window corners**
|
||||
- **Rounded buttons**
|
||||
- **Rounded menu corners**
|
||||
- **Rounded tabs**
|
||||
- **Rounded scrollbars**
|
||||
- **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like.
|
||||
|
||||
|
||||
|
||||
## Color
|
||||
|
||||
### Color scheme
|
||||
|
@ -544,9 +562,28 @@ below all the binds, select a key from the dropdown list to add it. it will appe
|
|||
- **Export**
|
||||
- **Reset defaults**
|
||||
- **Guru mode**: exposes all color options (instead of accent colors).
|
||||
- **General**
|
||||
- **Interface**
|
||||
- **Frame shading**: applies a gradient effect to buttons and input boxes.
|
||||
- **Color scheme type:**
|
||||
- **Dark**
|
||||
- **Light**
|
||||
- **Frame shading**: applies a gradient effect to buttons and input boxes.
|
||||
- **Accent colors**: select main interface colors.
|
||||
- **Primary**
|
||||
- **Secondary**
|
||||
- several more categories...
|
||||
|
||||
## Backup
|
||||
|
||||
### Configuration
|
||||
|
||||
- **Enable backup system**: turn on automatic backups of the current open file.
|
||||
- **Interval (in seconds)**: time between automatic backups.
|
||||
- **Backups per file**: maximum number of backups to store for each file. oldest backups are deleted first.
|
||||
|
||||
### Backup Management
|
||||
|
||||
- **Purge before**:
|
||||
- **Go**: purge all backups from before the selected date.
|
||||
- total space used by all backups:
|
||||
- **Refresh**: recalculate space.
|
||||
- **Delete All**: purge all backups.
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
- **Name**: the track's title.
|
||||
- **Author**: the author(s) of this track.
|
||||
- **Album**: the associated album name (or the name of the game the song is from).
|
||||
- **System**: the game console or computer the track is designed for. this is automatically set when creating a new tune, but in advanced mode, it can be changed to anything one wants. the **Auto** button will provide a guess based on the chips in use.
|
||||
- **System**: the name of the game console or computer the track is designed for. this is automatically set when creating a new tune, but can be changed to anything. the **Auto** button will provide a guess based on the chips in use.
|
||||
|
||||
all of this metadata will be included in a VGM export. this isn't the case for an audio export, however.
|
||||
|
||||
- _**Tuning (A-4)**_: set tuning based on the note A-4, which should be 440 in most cases. opening an Amiga MOD will set it to 436 for hardware compatibility. available only in advanced mode.
|
||||
- **Tuning (A-4)**: set tuning based on the note A-4, which should be 440 in most cases. opening an Amiga MOD will set it to 436 for hardware compatibility.
|
||||
|
||||
## subsongs
|
||||
|
||||
|
@ -23,16 +23,17 @@ this window allows one to create **subsongs** - multiple individual songs within
|
|||
|
||||
there are multiple ways to set the tempo of a song.
|
||||
|
||||
items in _italic_ don't appear in basic mode and are only available in advanced mode.
|
||||
**Base Tempo**: tempo in beats per minute (BPM). this is affected by the Highlight settings below.
|
||||
- clicking the Base Tempo button switches to the more technical Tick Rate.
|
||||
|
||||
**Tick Rate**: the frequency of ticks per second, thus the rate at which notes and effects are processed.
|
||||
- all values are allowed for all chips, though most chips have hardware limitations that mean they should stay at either 60 (approximately NTSC) or 50 (exactly PAL).
|
||||
- clicking the Tick Rate button switches to a more traditional **Base Tempo** BPM setting.
|
||||
- clicking the Tick Rate button switches to the more traditional Base Tempo BPM setting.
|
||||
|
||||
**Speed**: the number of ticks per row.
|
||||
- clicking the "Speed" button changes to more complex modes covered in the [grooves](../8-advanced/grooves.md) page.
|
||||
|
||||
_**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rate. it does this by adding or skipping ticks to approximate the tempo. the two numbers represent a ratio applied to the actual tick rate. example:
|
||||
**Virtual Tempo**: simulates any arbitrary tempo without altering the tick rate. it does this by adding or skipping ticks to approximate the tempo. the two numbers represent a ratio applied to the actual tick rate. example:
|
||||
- set tick rate to 150 BPM (60 Hz) and speed to 6.
|
||||
- set the first virtual tempo number (numerator) to 200.
|
||||
- set the second virtual tempo number (denominator) to 150.
|
||||
|
@ -40,7 +41,8 @@ _**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rat
|
|||
- the ratio doesn't have to match BPM numbers. set the numerator to 4 and the denominator to 5, and the virtual BPM becomes 150 × 4/5 = 120.
|
||||
- another way to accomplish this with more control over the results is to use grooves. see the page on [grooves](../8-advanced/grooves.md) for details.
|
||||
|
||||
_**Divider**:_ Changes the effective tick rate. a tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each!
|
||||
**Divider**: changes the effective tick rate. a tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each!
|
||||
- to the right, the effective BPM is listed, taking all settings into account.
|
||||
|
||||
**Highlight**: sets the pattern row highlights:
|
||||
- the first value represents the number of rows per beat.
|
||||
|
@ -50,4 +52,4 @@ _**Divider**:_ Changes the effective tick rate. a tick rate of 60Hz and a diviso
|
|||
|
||||
**Pattern Length**: the length of each pattern in rows. this affects all patterns in the song, and every pattern must be the same length. (Individual patterns can be cut short by `0Bxx`, `0Dxx`, and `FFxx` commands.)
|
||||
|
||||
_**Song Length**:_ how many orders are in the order list. decreasing it will hide the orders at the bottom. increasing it will restore those orders; increasing it further will add new orders of all `00` patterns.
|
||||
**Song Length**: how many orders are in the order list. decreasing it will hide the orders at the bottom. increasing it will restore those orders; increasing it further will add new orders of all `00` patterns.
|
||||
|
|
|
@ -119,6 +119,10 @@ Shift-Up | expand selection upwards
|
|||
Shift-Down | expand selection downwards
|
||||
Shift-Left | expand selection to the left
|
||||
Shift-Right | expand selection to the right
|
||||
Alt-Up | move selection up by one
|
||||
Alt-Down | move selection down by one
|
||||
Alt-Left | move selection to previous channel
|
||||
Alt-Right | move selection to next channel
|
||||
Backspace | delete note at cursor and/or pull pattern upwards (configurable)
|
||||
Delete | delete selection
|
||||
Insert | create blank row at cursor position and push pattern
|
||||
|
|
|
@ -89,6 +89,7 @@ in there, you can modify certain data pertaining to your sample, such as the:
|
|||
- **Open**: replaces current sample.
|
||||
- right-clicking brings up a menu:
|
||||
- **import raw...**: brings up a file selector, then presents a dialog to choose the format of the selected file.
|
||||
- **import raw (replace)...**: same as above, but instead of adding it to the sample list, it replaces the currently selected sample.
|
||||
- **Save**: saves current sample to disk.
|
||||
- right-clicking brings up a menu:
|
||||
- **save raw...**: brings up a file selector, then saves the sample as raw data.
|
||||
|
|
|
@ -15,11 +15,11 @@ it features echo and up to 16 voices.
|
|||
|
||||
- `10xx`: **change wave.**
|
||||
- `11xy`: **configure echo.**
|
||||
- this effect is kinda odd. this is how it works:
|
||||
|
||||
> How do you echo on GBA
|
||||
>
|
||||
> Create an empty instrment and put a very high note of it in channel 1 then do 110x in effect column and set volume column to set feedback and do nothing else on it
|
||||
- this effect is kinda odd. here's how to use it:
|
||||
- create an empty instrument and put a very high note of it in channel 1.
|
||||
- put `110x` in the effect column.
|
||||
- set volume column to set feedback.
|
||||
- don't use the channel for anything else.
|
||||
|
||||
- `12xy`: **toggle invert.**
|
||||
- `x` left channel.
|
||||
|
|
|
@ -59,7 +59,7 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i
|
|||
- `00` to `7F` for 0 to 127.
|
||||
- `80` to `FF` for -128 to -1.
|
||||
- note: be sure the sum of all coefficients is between -128 and 127. sums outside that may result in overflow and therefore clicking.
|
||||
- see [SnesLab](https://sneslab.net/wiki/FIR_Filter) for a full explanation and examples.
|
||||
- see SnesLab for [echo filter explanations and examples](https://sneslab.net/wiki/FIR_Filter#Uses).
|
||||
|
||||
## info
|
||||
|
||||
|
@ -67,6 +67,8 @@ this chip uses the [SNES](../4-instrument/snes.md) instrument editor.
|
|||
|
||||
when two channels are joined for pitch modulation, the channel bar will show `mod` on a bracket tying them together.
|
||||
|
||||
when using sample offset commands, be sure to open each involved sample in the sample editor, look to the "Info" section at the top-left, and check the "no BRR filters" box. this prevents sound glitches, at the cost of lowering the sample quality to 4-bit.
|
||||
|
||||
## channel status
|
||||
|
||||
the following icons are displayed when channel status is enabled in the pattern view:
|
||||
|
@ -93,6 +95,8 @@ the following options are available in the Chip Manager window:
|
|||
- **Feedback**: sets how much of the echo output will be fed back into the buffer.
|
||||
- **Echo volume**: sets echo volume.
|
||||
- **Echo filter**: adjusts echo filter.
|
||||
- **Dec/Hex**: toggles decimal or hexadecimal mode for the filter settings text entry box to the right.
|
||||
- SnesLab provides [echo filter explanations and examples](https://sneslab.net/wiki/FIR_Filter#Uses). their example filter strings can be pasted directly into the filter settings text entry box if set to Hex mode.
|
||||
|
||||
## ADSR
|
||||
|
||||
|
|
|
@ -8,22 +8,27 @@ as listed in the "Edit" menu:
|
|||
|
||||
as listed in the "Window" menu:
|
||||
|
||||
- [mixer](mixer.md)
|
||||
- [grooves](grooves.md)
|
||||
- [channel manager](channels.md)
|
||||
- [pattern manager](pat-manager.md)
|
||||
- [chip manager](chip-manager.md)
|
||||
- [compatibility flags](compat-flags.md)
|
||||
- [song comments](comments.md)
|
||||
|
||||
- [piano](piano.md)
|
||||
- [oscilloscope](osc.md)
|
||||
- [oscilloscope (X-Y)](xyosc.md)
|
||||
- [oscilloscopes (per-channel)](chanosc.md)
|
||||
- [clock](clock.md)
|
||||
- [register view](regview.md)
|
||||
- [log viewer](log-viewer.md)
|
||||
- [stats](stats.md)
|
||||
- song
|
||||
- [song comments](../8-advanced/comments.md)
|
||||
- [channels](../8-advanced/channels.md)
|
||||
- [chip manager](../8-advanced/chip-manager.md)
|
||||
- [pattern manager](../8-advanced/pat-manager.md)
|
||||
- [mixer](../8-advanced/mixer.md)
|
||||
- [compatibility flags](../8-advanced/compat-flags.md)
|
||||
- visualizers
|
||||
- [oscilloscope](../8-advanced/osc.md)
|
||||
- [oscilloscope (per-channel)](../8-advanced/chanosc.md)
|
||||
- [oscilloscope (X-Y)](../8-advanced/xyosc.md)
|
||||
- volume meter
|
||||
- tempo
|
||||
- [clock](../8-advanced/clock.md)
|
||||
- [grooves](../8-advanced/grooves.md)
|
||||
- debug
|
||||
- [log viewer](../8-advanced/log-viewer.md)
|
||||
- [register view](../8-advanced/regview.md)
|
||||
- [statistics](../8-advanced/stats.md)
|
||||
- [memory composition](../8-advanced/memory-composition.md)
|
||||
- [piano/input pad](../8-advanced/piano.md)
|
||||
|
||||
other:
|
||||
|
||||
|
|
|
@ -5,8 +5,9 @@ the "Channels" dialog allows manipulation of the song's channels.
|
|||
![channels dialog](channels.png)
|
||||
|
||||
each channel has the following options:
|
||||
- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept.
|
||||
- crossed-arrows button: click and drag to rearrange pattern data throughout the song.
|
||||
- **Pat**: uncheck the box to hide the channel from the pattern view. pattern data will be kept.
|
||||
- **Osc**: uncheck the box to hide the channel from the per-channel oscilloscope view.
|
||||
- **Swap**: click and drag to rearrange pattern data throughout the song.
|
||||
- note: this does **not** move channels around! it only moves the channel's pattern data.
|
||||
- **Name**: the name displayed at the top of each channel in the pattern view.
|
||||
- the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed.
|
||||
|
|
|
@ -14,6 +14,7 @@ right-clicking the view will display the configuration view shown above:
|
|||
- **Mode 2**: bias slightly toward more columns.
|
||||
- **Mode 3**: always more columns than rows.
|
||||
- **Amplitude**: scales amplitude for all oscilloscope views.
|
||||
- **Line size**: controls line thickness.
|
||||
- **Gradient**: this allows you to use a gradient for determining the waveforms' colors instead of a single color. see the gradient section for more information.
|
||||
- if this option is off, a color selector will be displayed. right-click on it for some options:
|
||||
- select between the square selector and the color wheel selector.
|
||||
|
|
|
@ -8,6 +8,8 @@ the **chip manager** window allows you to manage chips, including adding, changi
|
|||
|
||||
**Clone channel data**: when cloning chips, also copy patterns, pattern names, channel names and other parameters to the clone.
|
||||
|
||||
**Clone at end**: instead of inserting the clone directly after the cloned chip, add it to the end.
|
||||
|
||||
to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to the left.
|
||||
|
||||
to duplicate a chip, click the **Clone** button.
|
||||
|
|
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.
|
||||
|
||||
![user systems window](user-systems.png)
|
||||
|
||||
the `+` button at the top of the **Systems** list will add a new system.
|
||||
|
||||
next to the **Name** field, the **Remove** button removes the current system from the list.
|
||||
|
||||
chip configuration is exactly as in the [chip manager](chip-manager.md) window.
|
||||
|
||||
the **Advanced** field stores additional settings that are set when a new song is started. these are listed in "option=value" format, one per line.
|
||||
- `tickRate`: sets tick rate.
|
||||
|
||||
**Save and Close**: as it says.
|
||||
|
||||
**Import**: opens a dialog to select a `.cfgu` file, then adds its systems to the list.
|
||||
|
||||
**Import (replace)**: opens a similar dialog, then clears the existing systems list and replaces it with the imported one.
|
||||
|
||||
**Export**: stores the current list of systems in a selected `.cfgu` file.
|
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 |
|
@ -619,6 +619,17 @@ class DivEngine {
|
|||
void loadFF(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);
|
||||
|
||||
//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);
|
||||
|
||||
|
@ -1034,7 +1045,8 @@ class DivEngine {
|
|||
int addSamplePtr(DivSample* which);
|
||||
|
||||
// get sample from file
|
||||
DivSample* sampleFromFile(const char* path);
|
||||
//DivSample* sampleFromFile(const char* path);
|
||||
std::vector<DivSample*> sampleFromFile(const char* path);
|
||||
|
||||
// get raw sample
|
||||
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_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...");
|
||||
}
|
||||
|
||||
logV("reading sample data (%d)",s->samples);
|
||||
|
||||
if (flags&8) { // compressed sample
|
||||
unsigned int ret=0;
|
||||
logV("decompression begin... (%d)",s->samples);
|
||||
|
@ -672,62 +674,66 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
|
|||
}
|
||||
logV("got: %d",ret);
|
||||
} else {
|
||||
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
if (flags&4) { // downmix stereo
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
short l;
|
||||
if (convert&2) {
|
||||
l=reader.readS_BE();
|
||||
} else {
|
||||
l=reader.readS();
|
||||
try {
|
||||
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
if (flags&4) { // downmix stereo
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
short l;
|
||||
if (convert&2) {
|
||||
l=reader.readS_BE();
|
||||
} else {
|
||||
l=reader.readS();
|
||||
}
|
||||
if (!(convert&1)) {
|
||||
l^=0x8000;
|
||||
}
|
||||
s->data16[i]=l;
|
||||
}
|
||||
if (!(convert&1)) {
|
||||
l^=0x8000;
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
short r;
|
||||
if (convert&2) {
|
||||
r=reader.readS_BE();
|
||||
} else {
|
||||
r=reader.readS();
|
||||
}
|
||||
if (!(convert&1)) {
|
||||
r^=0x8000;
|
||||
}
|
||||
s->data16[i]=(s->data16[i]+r)>>1;
|
||||
}
|
||||
s->data16[i]=l;
|
||||
}
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
short r;
|
||||
if (convert&2) {
|
||||
r=reader.readS_BE();
|
||||
} else {
|
||||
r=reader.readS();
|
||||
} else {
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
if (convert&2) {
|
||||
s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000);
|
||||
} else {
|
||||
s->data16[i]=reader.readS()^((convert&1)?0:0x8000);
|
||||
}
|
||||
}
|
||||
if (!(convert&1)) {
|
||||
r^=0x8000;
|
||||
}
|
||||
s->data16[i]=(s->data16[i]+r)>>1;
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i=0; i<s->samples; i++) {
|
||||
if (convert&2) {
|
||||
s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000);
|
||||
} else {
|
||||
s->data16[i]=reader.readS()^((convert&1)?0:0x8000);
|
||||
if (flags&4) { // downmix stereo
|
||||
for (unsigned int i=0; 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (flags&4) { // downmix stereo
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch (EndOfFileException& e) {
|
||||
logW("premature end of file...");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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"
|
||||
|
||||
// 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) {
|
||||
sbi.Mcharacteristics = 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) {
|
||||
struct InvalidHeaderException {};
|
||||
bool success=false;
|
||||
bool opl2=!getConfInt("s3mOPL3",0);
|
||||
char magic[4]={0,0,0,0};
|
||||
SafeReader reader=SafeReader(file,len);
|
||||
warnings="";
|
||||
|
@ -273,11 +259,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
|||
bool hasPCM=false;
|
||||
bool hasFM=false;
|
||||
int numChans=0;
|
||||
int realNumChans=0;
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
if (chanSettings[i]==255) continue;
|
||||
if ((chanSettings[i]&127)>=32) continue;
|
||||
if ((chanSettings[i]&127)>=16) {
|
||||
for (int ch=0; ch<32; ch++) {
|
||||
if (chanSettings[ch]!=255) realNumChans++;
|
||||
}
|
||||
|
||||
for (int ch=0; ch<32; ch++) {
|
||||
if (chanSettings[ch]==255) continue;
|
||||
if ((chanSettings[ch]&127)>=32) continue;
|
||||
if ((chanSettings[ch]&127)>=16) {
|
||||
hasFM=true;
|
||||
} else {
|
||||
hasPCM=true;
|
||||
|
@ -287,34 +278,69 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
|||
if (hasFM && hasPCM) break;
|
||||
}
|
||||
|
||||
int pcmChan=hasFM?9:0;
|
||||
int pcmChan=hasFM?(opl2 ? 9 : 18):0;
|
||||
int fmChan=hasPCM?32:0;
|
||||
int invalidChan=40;
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
if (chanSettings[i]==255) {
|
||||
chanMap[i]=invalidChan++;
|
||||
for (int ch=0; ch<32; ch++) {
|
||||
if (chanSettings[ch]==255) {
|
||||
chanMap[ch]=invalidChan++;
|
||||
continue;
|
||||
}
|
||||
if ((chanSettings[i]&127)>=32) {
|
||||
chanMap[i]=invalidChan++;
|
||||
if ((chanSettings[ch]&127)>=32) {
|
||||
chanMap[ch]=invalidChan++;
|
||||
continue;
|
||||
}
|
||||
if ((chanSettings[i]&127)>=16) {
|
||||
chanMap[i]=fmChan++;
|
||||
if ((chanSettings[ch]&127)>=16) {
|
||||
chanMap[ch]=fmChan++;
|
||||
} else {
|
||||
chanMap[i]=pcmChan++;
|
||||
chanMap[ch]=pcmChan++;
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[40];
|
||||
int chanIndex = 1;
|
||||
|
||||
if (hasPCM) {
|
||||
for (int i=pcmChan; i<32; i++) {
|
||||
ds.subsong[0]->chanShow[i]=false;
|
||||
ds.subsong[0]->chanShowChanOsc[i]=false;
|
||||
for(int ch = 0; ch < pcmChan - (realNumChans - (hasFM ? 9 : 0)); ch++)
|
||||
{
|
||||
ds.subsong[0]->chanShow[ch]=false;
|
||||
ds.subsong[0]->chanShowChanOsc[ch]=false;
|
||||
}
|
||||
|
||||
for (int ch=pcmChan; ch<32; ch++) {
|
||||
ds.subsong[0]->chanShow[ch]=false;
|
||||
ds.subsong[0]->chanShowChanOsc[ch]=false;
|
||||
}
|
||||
|
||||
for(int ch = 0; ch < 32; ch++)
|
||||
{
|
||||
if(ds.subsong[0]->chanShow[ch])
|
||||
{
|
||||
snprintf(buffer, 40, _("Channel %d"), chanIndex);
|
||||
ds.subsong[0]->chanName[ch] = buffer;
|
||||
chanIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFM && !opl2) {
|
||||
for (int ch=(hasPCM?32:0) + 9; ch<(hasPCM?32:0) + 18; ch++) {
|
||||
ds.subsong[0]->chanShow[ch]=false;
|
||||
ds.subsong[0]->chanShowChanOsc[ch]=false;
|
||||
}
|
||||
|
||||
chanIndex = 1;
|
||||
|
||||
for (int ch=(hasPCM?32:0); ch<(hasPCM?32:0) + 9; ch++) {
|
||||
snprintf(buffer, 40, _("FM %d"), chanIndex);
|
||||
ds.subsong[0]->chanName[ch] = buffer;
|
||||
chanIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
logV("numChans: %d",numChans);
|
||||
logV("realNumChans: %d",realNumChans);
|
||||
|
||||
ds.systemName="PC";
|
||||
if (hasPCM) {
|
||||
|
@ -327,7 +353,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
|
|||
ds.systemLen++;
|
||||
}
|
||||
if (hasFM) {
|
||||
ds.system[ds.systemLen]=DIV_SYSTEM_OPL2;
|
||||
ds.system[ds.systemLen]=opl2 ? DIV_SYSTEM_OPL2 : DIV_SYSTEM_OPL3;
|
||||
ds.systemVol[ds.systemLen]=1.0f;
|
||||
ds.systemPan[ds.systemLen]=0;
|
||||
ds.systemLen++;
|
||||
|
|
|
@ -24,10 +24,12 @@
|
|||
#include "sfWrapper.h"
|
||||
#endif
|
||||
|
||||
DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||
std::vector<DivSample*> DivEngine::sampleFromFile(const char* path) {
|
||||
std::vector<DivSample*> ret;
|
||||
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
@ -58,6 +60,110 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
}
|
||||
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
|
||||
size_t len=0;
|
||||
DivSample* sample=new DivSample;
|
||||
|
@ -68,7 +174,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
|
@ -76,7 +182,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
|
@ -86,7 +192,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError="file is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
|
@ -94,7 +200,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
|
@ -102,7 +208,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (extS==".dmc") {
|
||||
|
@ -120,7 +226,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError="wait... is that right? no I don't think so...";
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned char* dataBuf=sample->dataDPCM;
|
||||
|
@ -147,14 +253,14 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError="BRR sample is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
} else if ((len%9)!=0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="possibly corrupt BRR sample!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,16 +269,17 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
BUSY_END;
|
||||
return sample;
|
||||
ret.push_back(sample);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_SNDFILE
|
||||
lastError="Furnace was not compiled with libsndfile!";
|
||||
return NULL;
|
||||
return ret;
|
||||
#else
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
|
@ -186,13 +293,13 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
} 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));
|
||||
}
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
if (si.frames>16777215) {
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
void* buf=NULL;
|
||||
sf_count_t sampleLen=sizeof(short);
|
||||
|
@ -298,7 +405,8 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return sample;
|
||||
ret.push_back(sample);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
if (adpcmBMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0xffffff];
|
||||
return adpcmBMem[address&0x3ffff];
|
||||
case ymfm::ACCESS_PCM:
|
||||
if (pcmMem==NULL) {
|
||||
return 0;
|
||||
|
|
|
@ -1663,26 +1663,34 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
chan[i].panPos+=chan[i].panRate;
|
||||
chan[i].panPos&=255;
|
||||
|
||||
// calculate...
|
||||
// calculate inverted...
|
||||
switch (chan[i].panPos&0xc0) {
|
||||
case 0: // center -> right
|
||||
chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2);
|
||||
chan[i].panR=0xff;
|
||||
chan[i].panL=((chan[i].panPos&0x3f)<<2);
|
||||
chan[i].panR=0;
|
||||
break;
|
||||
case 0x40: // right -> center
|
||||
chan[i].panL=(chan[i].panPos&0x3f)<<2;
|
||||
chan[i].panR=0xff;
|
||||
chan[i].panL=0xff-((chan[i].panPos&0x3f)<<2);
|
||||
chan[i].panR=0;
|
||||
break;
|
||||
case 0x80: // center -> left
|
||||
chan[i].panL=0xff;
|
||||
chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2);
|
||||
chan[i].panL=0;
|
||||
chan[i].panR=((chan[i].panPos&0x3f)<<2);
|
||||
break;
|
||||
case 0xc0: // left -> center
|
||||
chan[i].panL=0xff;
|
||||
chan[i].panR=(chan[i].panPos&0x3f)<<2;
|
||||
chan[i].panL=0;
|
||||
chan[i].panR=0xff-((chan[i].panPos&0x3f)<<2);
|
||||
break;
|
||||
}
|
||||
|
||||
// multiply by depth
|
||||
chan[i].panL=(chan[i].panL*chan[i].panDepth)/15;
|
||||
chan[i].panR=(chan[i].panR*chan[i].panDepth)/15;
|
||||
|
||||
// then invert it to get final panning
|
||||
chan[i].panL^=0xff;
|
||||
chan[i].panR^=0xff;
|
||||
|
||||
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ static float oscDebugMax=1.0;
|
|||
static float oscDebugPower=1.0;
|
||||
static int oscDebugRepeat=1;
|
||||
static int numApples=1;
|
||||
static int getGainChan=0;
|
||||
static int getGainVol=0;
|
||||
|
||||
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
||||
if (cmd!=NULL) {
|
||||
|
@ -721,6 +723,13 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::TreePop();
|
||||
}
|
||||
#endif
|
||||
if (ImGui::TreeNode("Get Gain Test")) {
|
||||
float realVol=e->getGain(getGainChan,getGainVol);
|
||||
ImGui::InputInt("Chan",&getGainChan);
|
||||
ImGui::InputInt("Vol",&getGainVol);
|
||||
ImGui::Text("result: %.0f%%",realVol*100.0f);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("User Interface")) {
|
||||
if (ImGui::Button("Inspect")) {
|
||||
inspectorOpen=!inspectorOpen;
|
||||
|
|
|
@ -676,6 +676,17 @@ void FurnaceGUI::doAction(int what) {
|
|||
latchTarget=0;
|
||||
latchNibble=false;
|
||||
break;
|
||||
case GUI_ACTION_PAT_ABSORB_INSTRUMENT: {
|
||||
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
|
||||
if (!pat) break;
|
||||
for (int i=cursor.y; i>=0; i--) {
|
||||
if (pat->data[i][2] >= 0) {
|
||||
curIns=pat->data[i][2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GUI_ACTION_INS_LIST_ADD:
|
||||
if (settings.insTypeMenu) {
|
||||
|
|
341
src/gui/gui.cpp
341
src/gui/gui.cpp
|
@ -3828,8 +3828,9 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
|
||||
std::vector<DivSample*> samples = e->sampleFromFile(ev.drop.file);
|
||||
DivWavetable* droppedWave=NULL;
|
||||
DivSample* droppedSample=NULL;
|
||||
//DivSample* droppedSample=NULL;
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
|
@ -3854,10 +3855,24 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
nextWindow=GUI_WINDOW_WAVE_LIST;
|
||||
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;
|
||||
sampleCount=e->addSamplePtr(droppedSample);
|
||||
if (sampleCount>=0 && settings.selectAssetOnLoad) {
|
||||
for (DivSample* s: samples)
|
||||
{
|
||||
sampleCount=e->addSamplePtr(s);
|
||||
}
|
||||
//sampleCount=e->addSamplePtr(droppedSample);
|
||||
if (sampleCount>=0 && settings.selectAssetOnLoad)
|
||||
{
|
||||
curSample=sampleCount;
|
||||
updateSampleTex=true;
|
||||
}
|
||||
|
@ -5319,24 +5334,43 @@ bool FurnaceGUI::loop() {
|
|||
String errs=_("there were some errors while loading samples:\n");
|
||||
bool warn=false;
|
||||
for (String i: fileDialog->getFileName()) {
|
||||
DivSample* s=e->sampleFromFile(i.c_str());
|
||||
if (s==NULL) {
|
||||
std::vector<DivSample*> samples=e->sampleFromFile(i.c_str());
|
||||
if (samples.empty()) {
|
||||
if (fileDialog->getFileName().size()>1) {
|
||||
warn=true;
|
||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||
} else {;
|
||||
showError(e->getLastError());
|
||||
}
|
||||
} else {
|
||||
if (e->addSamplePtr(s)==-1) {
|
||||
if (fileDialog->getFileName().size()>1) {
|
||||
warn=true;
|
||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||
} else {
|
||||
showError(e->getLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
if((int)samples.size() == 1)
|
||||
{
|
||||
if (e->addSamplePtr(samples[0]) == -1)
|
||||
{
|
||||
if (fileDialog->getFileName().size()>1)
|
||||
{
|
||||
warn=true;
|
||||
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
showError(e->getLastError());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
case GUI_FILE_SAMPLE_OPEN_REPLACE: {
|
||||
DivSample* s=e->sampleFromFile(copyOfName.c_str());
|
||||
if (s==NULL) {
|
||||
case GUI_FILE_SAMPLE_OPEN_REPLACE:
|
||||
{
|
||||
std::vector<DivSample*> samples=e->sampleFromFile(copyOfName.c_str());
|
||||
if (samples.empty())
|
||||
{
|
||||
showError(e->getLastError());
|
||||
} else {
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
e->lockEngine([this,s]() {
|
||||
// if it crashes here please tell me...
|
||||
DivSample* oldSample=e->song.sample[curSample];
|
||||
e->song.sample[curSample]=s;
|
||||
delete oldSample;
|
||||
e->renderSamples();
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
updateSampleTex=true;
|
||||
} else {
|
||||
showError(_("...but you haven't selected a sample!"));
|
||||
delete s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((int)samples.size() == 1)
|
||||
{
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size())
|
||||
{
|
||||
DivSample* s = samples[0];
|
||||
e->lockEngine([this, s]()
|
||||
{
|
||||
// if it crashes here please tell me...
|
||||
DivSample* oldSample=e->song.sample[curSample];
|
||||
e->song.sample[curSample]= 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;
|
||||
|
@ -5741,6 +5795,11 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::OpenPopup(_("Select Instrument"));
|
||||
}
|
||||
|
||||
if (displayPendingSamples) {
|
||||
displayPendingSamples=false;
|
||||
ImGui::OpenPopup(_("Select Sample"));
|
||||
}
|
||||
|
||||
if (displayPendingRawSample) {
|
||||
displayPendingRawSample=false;
|
||||
ImGui::OpenPopup(_("Import Raw Sample"));
|
||||
|
@ -6569,6 +6628,191 @@ bool FurnaceGUI::loop() {
|
|||
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);
|
||||
if (ImGui::BeginPopupModal(_("Import Raw Sample"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text(_("Data type:"));
|
||||
|
@ -7581,7 +7825,15 @@ bool FurnaceGUI::init() {
|
|||
#endif
|
||||
|
||||
compatFormats+="*.dmc ";
|
||||
compatFormats+="*.brr";
|
||||
compatFormats+="*.brr ";
|
||||
|
||||
compatFormats+="*.ppc ";
|
||||
compatFormats+="*.pps ";
|
||||
compatFormats+="*.pvi ";
|
||||
compatFormats+="*.pdx ";
|
||||
compatFormats+="*.pzi ";
|
||||
compatFormats+="*.p86 ";
|
||||
compatFormats+="*.p";
|
||||
audioLoadFormats[1]=compatFormats;
|
||||
|
||||
audioLoadFormats.push_back(_("NES DPCM data"));
|
||||
|
@ -7590,6 +7842,27 @@ bool FurnaceGUI::init() {
|
|||
audioLoadFormats.push_back(_("SNES Bit Rate Reduction"));
|
||||
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("*");
|
||||
|
||||
|
@ -8012,6 +8285,8 @@ FurnaceGUI::FurnaceGUI():
|
|||
snesFilterHex(false),
|
||||
modTableHex(false),
|
||||
displayEditString(false),
|
||||
displayPendingSamples(false),
|
||||
replacePendingSample(false),
|
||||
displayExportingROM(false),
|
||||
changeCoarse(false),
|
||||
mobileEdit(false),
|
||||
|
|
|
@ -816,6 +816,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_PAT_LATCH,
|
||||
GUI_ACTION_PAT_SCROLL_MODE,
|
||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||
GUI_ACTION_PAT_ABSORB_INSTRUMENT,
|
||||
GUI_ACTION_PAT_MAX,
|
||||
|
||||
GUI_ACTION_INS_LIST_MIN,
|
||||
|
@ -1591,7 +1592,7 @@ class FurnaceGUI {
|
|||
int sampleTexW, sampleTexH;
|
||||
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 workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
||||
String workingDirFont, workingDirColors, workingDirKeybinds;
|
||||
|
@ -1603,6 +1604,7 @@ class FurnaceGUI {
|
|||
String folderString;
|
||||
|
||||
std::vector<DivSystem> sysSearchResults;
|
||||
std::vector<std::pair<DivSample*,bool>> sampleBankSearchResults;
|
||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||
std::vector<int> paletteSearchResults;
|
||||
FixedQueue<String,32> recentFile;
|
||||
|
@ -1618,6 +1620,7 @@ class FurnaceGUI {
|
|||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
||||
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
bool displayPendingSamples, replacePendingSample;
|
||||
bool displayExportingROM;
|
||||
bool changeCoarse;
|
||||
bool mobileEdit;
|
||||
|
@ -1959,6 +1962,7 @@ class FurnaceGUI {
|
|||
unsigned int maxUndoSteps;
|
||||
float vibrationStrength;
|
||||
int vibrationLength;
|
||||
int s3mOPL3;
|
||||
String mainFontPath;
|
||||
String headFontPath;
|
||||
String patFontPath;
|
||||
|
@ -2217,6 +2221,7 @@ class FurnaceGUI {
|
|||
maxUndoSteps(100),
|
||||
vibrationStrength(0.5f),
|
||||
vibrationLength(20),
|
||||
s3mOPL3(1),
|
||||
mainFontPath(""),
|
||||
headFontPath(""),
|
||||
patFontPath(""),
|
||||
|
@ -2373,6 +2378,7 @@ class FurnaceGUI {
|
|||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<Particle> particles;
|
||||
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
||||
std::vector<std::pair<DivSample*,bool>> pendingSamples;
|
||||
|
||||
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_SCROLL_MODE", _N("Change mobile scroll mode"), 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("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#include "../ta-utils.h"
|
||||
|
||||
struct FurnacePlotArrayGetterData
|
||||
{
|
||||
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;
|
||||
for (int i=0; i<bits && overlay_text[i]; i++) {
|
||||
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)));
|
||||
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), 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), 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), 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::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::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, "")
|
||||
}
|
||||
);
|
||||
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(
|
||||
"Sharp X1", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3")
|
||||
}
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"Sharp X1 + FM Addon", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3"),
|
||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2")
|
||||
}
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sharp X68000", {
|
||||
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"),
|
||||
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "clockSel=2")
|
||||
}
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"FM-7", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=12"),
|
||||
CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=5")
|
||||
}
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"FM-7 (extended channel 3)", {
|
||||
|
|
|
@ -1259,6 +1259,14 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
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;
|
||||
}
|
||||
CONFIG_SECTION(_("Audio")) {
|
||||
|
@ -2431,6 +2439,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_EXPAND_SONG);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
@ -4761,6 +4770,8 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
||||
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
||||
|
||||
settings.s3mOPL3=conf.getInt("s3mOPL3",1);
|
||||
|
||||
settings.backupEnable=conf.getInt("backupEnable",1);
|
||||
settings.backupInterval=conf.getInt("backupInterval",30);
|
||||
settings.backupMaxCopies=conf.getInt("backupMaxCopies",5);
|
||||
|
@ -5277,6 +5288,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.backupMaxCopies,1,100);
|
||||
clampSetting(settings.autoFillSave,0,1);
|
||||
clampSetting(settings.autoMacroStepSize,0,1);
|
||||
clampSetting(settings.s3mOPL3,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=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("vibrationLength",settings.vibrationLength);
|
||||
|
||||
conf.set("s3mOPL3",settings.s3mOPL3);
|
||||
|
||||
conf.set("backupEnable",settings.backupEnable);
|
||||
conf.set("backupInterval",settings.backupInterval);
|
||||
conf.set("backupMaxCopies",settings.backupMaxCopies);
|
||||
|
|
Loading…
Reference in a new issue