Merge branch 'master' of https://github.com/tildearrow/furnace into command-palette
|
@ -283,6 +283,13 @@ if (USE_SDL2)
|
|||
# If you link SDL statically, you also need to define HAVE_LIBC so it builds with the C runtime that your application uses.
|
||||
# This should probably go in a FAQ.
|
||||
set(SDL_LIBC ON CACHE BOOL "Tell SDL that we want it to use our C runtime (required for proper static linking)" FORCE)
|
||||
|
||||
# https://github.com/tildearrow/furnace/issues/1237
|
||||
# enabling this will result in SDL finding the Direct3D headers, forcing _WIN32_WINNT to an undesirable value (which makes the Wine headers define GetTickCount64)
|
||||
if (SUPPORT_XP)
|
||||
set(SDL_RENDER_D3D OFF CACHE BOOL "Enable the Direct3D render driver" FORCE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(extern/SDL EXCLUDE_FROM_ALL)
|
||||
list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2)
|
||||
list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include)
|
||||
|
@ -498,6 +505,8 @@ src/engine/platform/sound/sm8521.c
|
|||
|
||||
src/engine/platform/sound/d65modified.c
|
||||
|
||||
src/engine/platform/sound/ted-sound.c
|
||||
|
||||
src/engine/platform/oplAInterface.cpp
|
||||
src/engine/platform/ym2608Interface.cpp
|
||||
src/engine/platform/ym2610Interface.cpp
|
||||
|
@ -588,6 +597,8 @@ src/engine/platform/k007232.cpp
|
|||
src/engine/platform/ga20.cpp
|
||||
src/engine/platform/sm8521.cpp
|
||||
src/engine/platform/pv1000.cpp
|
||||
src/engine/platform/k053260.cpp
|
||||
src/engine/platform/ted.cpp
|
||||
src/engine/platform/pcmdac.cpp
|
||||
src/engine/platform/dummy.cpp
|
||||
|
||||
|
@ -696,6 +707,7 @@ src/gui/subSongs.cpp
|
|||
src/gui/sysConf.cpp
|
||||
src/gui/sysEx.cpp
|
||||
src/gui/sysManager.cpp
|
||||
src/gui/sysPartNumber.cpp
|
||||
src/gui/sysPicker.cpp
|
||||
src/gui/tutorial.cpp
|
||||
src/gui/util.cpp
|
||||
|
|
|
@ -81,6 +81,10 @@ additional guidelines:
|
|||
- on a switch block, **always** put `default` last and not in any other position.
|
||||
- I have fear of some C/C++ compilers ignoring the rest of cases upon hitting default.
|
||||
|
||||
## Do NOT Force-Push after submitting Pull Request
|
||||
|
||||
if you do so, your pull request will be closed.
|
||||
|
||||
## Demo Songs
|
||||
|
||||
just put your demo song in `demos/`! be noted there are some guidelines:
|
||||
|
|
|
@ -136,6 +136,7 @@ some people have provided packages for Unix/Unix-like distributions. here's a li
|
|||
- **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!).
|
||||
- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
|
||||
- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
|
||||
- **Void Linux**: [furnace](https://github.com/void-linux/void-packages/tree/master/srcpkgs/furnace) is available in the official repository.
|
||||
|
||||
---
|
||||
# developer info
|
||||
|
|
BIN
demos/arcade/Point_of_No_Return_SegaPCM.fur
Normal file
BIN
demos/ay8910/vibe_zone.fur
Normal file
BIN
demos/misc/FiveTwoThreeTwo_MSM5232.fur
Normal file
BIN
demos/msx/21492413.fur
Normal file
BIN
demos/x16/Shades of Blue.fur
Normal file
|
@ -19,3 +19,5 @@ due to its nature of being feature-packed, it may be technical and somewhat diff
|
|||
it also has a flexible windowing system which you may move around and organize.
|
||||
|
||||
see [2-interface](../2-interface/README.md) and [3-pattern](../3-pattern/README.md) for more information.
|
||||
|
||||
once familiar with the tracker, look to [9-guides](../9-guides/README.md) for useful techniques.
|
|
@ -95,3 +95,24 @@ now for decimal number `69420`:
|
|||
|
||||
= 10F2C
|
||||
```
|
||||
|
||||
# hex-decimal table
|
||||
|
||||
hex | `0` | `1` | `2` | `3` | `4` | `5` | `6` | `7` | `8` | `9` | `A` | `B` | `C` | `D` | `E` | `F`
|
||||
-----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:
|
||||
`00` | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
|
||||
`10` | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31
|
||||
`20` | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
|
||||
`30` | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63
|
||||
`40` | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79
|
||||
`50` | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
|
||||
`60` | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111
|
||||
`70` | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127
|
||||
`80` | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143
|
||||
`90` | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159
|
||||
`A0` | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175
|
||||
`B0` | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191
|
||||
`C0` | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207
|
||||
`D0` | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223
|
||||
`E0` | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239
|
||||
`F0` | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255
|
||||
|
|
|
@ -9,6 +9,7 @@ the default layout of Furnace is depicted below.
|
|||
primary topics:
|
||||
|
||||
- [menu bar](menu-bar.md)
|
||||
- [order list](order-list.md)
|
||||
- [play/edit controls](play-edit-controls.md)
|
||||
- [instrument/wavetable/sample list](asset-list.md)
|
||||
- [song information](song-info.md)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# menu bar
|
||||
|
||||
the menu bar allows you to select five menus: file, edit, settings, window and help.
|
||||
the menu bar allows you to select from five menus: file, edit, settings, window and help.
|
||||
|
||||
items in _italics_ don't appear in basic mode and are only available in advanced mode.
|
||||
|
||||
# file
|
||||
|
||||
|
@ -38,12 +40,12 @@ the menu bar allows you to select five menus: file, edit, settings, window and h
|
|||
- **export command stream...**: export song data to a command stream file. see next section for more details.
|
||||
- this option is for developers.
|
||||
|
||||
- **add chip...**: add a chip to the current song.
|
||||
- **configure chip...**: set a chip's parameters.
|
||||
- _**add chip...**:_ add a chip to the current song.
|
||||
- _**configure chip...**:_ set a chip's parameters.
|
||||
- for a list of parameters, see [7-systems](../7-systems/README.md).
|
||||
- **change chip...**: change a chip to another.
|
||||
- _**change chip...**:_ change a chip to another.
|
||||
- **Preserve channel positions**: enable this option to make sure Furnace does not auto-arrange/delete channels to compensate for differing channel counts. this can be useful for doing ports, e.g. from Genesis to PC-98.
|
||||
- **remove chip...**: remove a chip.
|
||||
- _**remove chip...**_: remove a chip.
|
||||
- **Preserve channel positions**: same thing as above.
|
||||
|
||||
- **restore backup**: restore a previously saved backup.
|
||||
|
@ -99,10 +101,10 @@ the following settings exist:
|
|||
- some chips will not be available, either because VGM doesn't support these yet, or because you selected an old format version.
|
||||
- **add pattern change hints**: this option adds a "hint" when a pattern change occurs. only useful if you're a developer.
|
||||
- the format of the "hint" data block that gets written is: `67 66 FE ll ll ll ll 01 oo rr pp pp pp ...`
|
||||
- ll: length, a 32-bit little-endian number
|
||||
- oo: order
|
||||
- rr: initial row (a 0Dxx effect is able to select a different row)
|
||||
- pp: pattern index (one per channel)
|
||||
- `ll`: length, a 32-bit little-endian number
|
||||
- `oo`: order
|
||||
- `rr`: initial row (a 0Dxx effect is able to select a different row)
|
||||
- `pp`: pattern index (one per channel)
|
||||
- **direct stream mode**: this option allows DualPCM to work. don't use this for other chips.
|
||||
- may or may not play well with hardware VGM players.
|
||||
|
||||
|
@ -140,7 +142,7 @@ it's not really useful, unless you're a developer and want to use a command stre
|
|||
- **cut**: moves the current selection in the pattern view to clipboard.
|
||||
- **copy**: copies the current selection in the pattern view to clipboard.
|
||||
- **paste**: inserts the clipboard's contents in the cursor position.
|
||||
- **paste special...**: variants of the paste feature.
|
||||
- _**paste special...**:_ variants of the paste feature.
|
||||
- **paste mix**: inserts the clipboard's contents in the cursor position, but does not erase the occupied region.
|
||||
- **paste mix (background)**: does the same thing as paste mix, but doesn't alter content which is already there.
|
||||
- **paste with ins (foreground)**: same thing as paste mix, but changes the instrument.
|
||||
|
@ -154,8 +156,8 @@ it's not really useful, unless you're a developer and want to use a command stre
|
|||
- if a column is already selected, it will select the entire channel.
|
||||
- if a channel is already selected, it will select the entire pattern.
|
||||
|
||||
- **operation mask**: this is an advanced feature. see [this page](../3-pattern/opmask.md) for more information.
|
||||
- **input latch**: this is an advanced feature. see [this page](../3-pattern/inputlatch.md) for more information.
|
||||
- _**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.
|
||||
|
||||
|
@ -176,7 +178,7 @@ it's not really useful, unless you're a developer and want to use a command stre
|
|||
- **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 options.
|
||||
- **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.
|
||||
|
||||
|
@ -188,7 +190,7 @@ it's not really useful, unless you're a developer and want to use a command stre
|
|||
- **expand song**: same as expand, but affects the entire song.
|
||||
- it also changes speeds and pattern length to compensate.
|
||||
|
||||
- **find/replace**: opens the Find/Replace window. see [this page](../3-pattern/find-replace.md) for more information.
|
||||
- _**find/replace**:_ shows [the Find/Replace window](../8-advanced/find-replace.md).
|
||||
|
||||
- **clear**: allows you to mass-delete things like songs, instruments and the like.
|
||||
|
||||
|
@ -199,46 +201,52 @@ it's not really useful, unless you're a developer and want to use a command stre
|
|||
- **basic mode**: toggles [Basic Mode](basic-mode.md).
|
||||
- **visualizer**: toggles pattern view particle effects when the song plays.
|
||||
- **reset layout**: resets the workspace to its defaults.
|
||||
- **settings...**: opens the Settings window. these are detailed in [settings.md].
|
||||
- **settings...**: shows the Settings window. these are detailed in [settings.md].
|
||||
|
||||
# window
|
||||
|
||||
- **song information**: shows/hides the Song Information window.
|
||||
- **subsongs**: shows/hides the Subsongs window.
|
||||
- **speed**: shows/hides the Speed window.
|
||||
- **instruments**: shows/hides the instrument list.
|
||||
- **wavetables**: shows/hides the wavetable list.
|
||||
- **samples**: shows/hides the sample list.
|
||||
- **orders**: shows/hides the Orders window.
|
||||
- **pattern**: shows/hides the pattern view.
|
||||
- **mixer**: shows/hides the Mixer window.
|
||||
- **grooves**: shows/hides the Grooves window.
|
||||
- **channels**: shows/hides the Channels window.
|
||||
- **pattern manager**: shows/hides the Pattern Manager window.
|
||||
- **chip manager**: shows/hides the Chip Manager window.
|
||||
- **compatibility flags**: shows/hides the Compatibility Flags window.
|
||||
- **song comments**: shows/hides the Song Comments window.
|
||||
all these menu items show or hide their associated windows.
|
||||
|
||||
- **instrument editor**: shows/hides the Instrument Editor
|
||||
- **wavetable editor**: shows/hides the Wavetable Editor.
|
||||
- **sample editor**: shows/hides the Sample Editor.
|
||||
- [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](mixer.md)_
|
||||
- _[grooves](grooves.md)_
|
||||
- _[channels](channels.md)_
|
||||
- _[pattern manager](pat-manager.md)_
|
||||
- _[chip manager](chip-manager.md)_
|
||||
- _[compatibility flags](compat-flags.md)_
|
||||
- [song comments](comments.md)
|
||||
|
||||
- **play/edit controls**: shows/hides the Play/Edit Controls.
|
||||
- **piano/input pad**: shows/hides the Piano/Input Pad window.
|
||||
- **oscilloscope (master)**: shows/hides the oscilloscope.
|
||||
- **oscilloscope (per-channel)**: shows/hides the per-channel oscilloscope.
|
||||
- **volume meter**: shows/hides the volume meter.
|
||||
- **clock**: shows/hides the clock.
|
||||
- **register view**: shows/hides the Register View window.
|
||||
- **log viewer**: shows/hides the log Viewer.
|
||||
- **statistics**: shows/hides the Statistics window.
|
||||
- [piano](piano.md)
|
||||
- [oscilloscope](osc.md)
|
||||
- [oscilloscopes (per-channel)](chanosc.md)
|
||||
- [clock](clock.md)
|
||||
- [register view](regview.md)
|
||||
- [log viewer](log-viewer.md)
|
||||
- [stats](stats.md)
|
||||
|
||||
# help
|
||||
|
||||
- **effect list**: displays the effect list.
|
||||
- **debug menu**: this menu contains various debug utilities.
|
||||
- unless you are working with the Furnace codebase, it's not useful.
|
||||
- **inspector**: this options opens the Dear ImGui Metrics/Debugger window.
|
||||
- **inspector**: this option shows the Dear ImGui Metrics/Debugger window.
|
||||
- unless you are working with the Furnace codebase, it's not useful.
|
||||
- **panic**: this resets all chips while the song is playing, effectively silencing everything.
|
||||
- **about...**: displays the About screen.
|
||||
|
||||
at the end of the menu bar, more information may be shown:
|
||||
- during editing, information about the data under the cursor will be shown here:
|
||||
- note or note modifier.
|
||||
- instrument number and name.
|
||||
- volume in decimal, hex, and percentage.
|
||||
- effect type and description.
|
||||
- during playback, the current values of the following will be listed:\
|
||||
> speed/groove @ tick rate (BPM) | order | row | elapsed time.
|
||||
- if any changes or edits have been made but not yet saved, "modified" will appear.
|
||||
|
|
29
doc/2-interface/order-list.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# order list
|
||||
|
||||
the order list is a playlist for patterns.
|
||||
|
||||
![order list](order-list.png)
|
||||
|
||||
along the top are the available channels. their abbreviations can be set in the [channels window](../8-advanced/channels.md). the highlighted channel follows the channel the pattern view cursor is in.
|
||||
|
||||
along the left are the order numbers. these are referenced with the `0Bxx` command. the highlighted row follows the order the pattern view cursor is in.
|
||||
|
||||
each entry in the table is the pattern that will play during that order. these can be changed according to the order edit mode.
|
||||
|
||||
hovering over a pattern number will pop up a tooltip showing the name of that pattern, if it has one.
|
||||
|
||||
The buttons are as follows:
|
||||
- **Add new order**.
|
||||
- **Remove order**.
|
||||
- **Duplicate order**: adds a new order with patterns matching the selected one directly below it. right-click to "deep clone"; this copies all patterns involved to new ones.
|
||||
- **Move order up**: swaps the selected order with the one above it.
|
||||
- **Move order down**: swaps the selected order with the one below it.
|
||||
- **Duplicate order at end of song**: same as "Duplicate order" except the new order is added at the bottom of the list.
|
||||
- **Order change mode**: selects how much of the order will change with an edit. only applies if "Order edit mode" is set to "Click to change".
|
||||
- **one**: only current channel's pattern will change.
|
||||
- **entire row**: all patterns in the order will change.
|
||||
- **Order edit mode**: selects the method of changing orders.
|
||||
- **Click to change**: a click will add one to the pattern number. a right-click will subtract one.
|
||||
- **Select and type (don't scroll)**: select a pattern and type.
|
||||
- **Select and type (scroll horizontally)**: as above, but after entering two digits, the cursor moves to the next channel.
|
||||
- **Select and type (scroll vertically)**: as above, but after entering two digits, the cursor moves to the next order.
|
BIN
doc/2-interface/order-list.png
Normal file
After Width: | Height: | Size: 72 KiB |
|
@ -181,37 +181,19 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
|
|||
- **NTSC non-drop (30fps)**
|
||||
|
||||
# Emulation
|
||||
- **Arcade/YM2151 core**
|
||||
- **ymfm**
|
||||
- **Nuked-OPM**
|
||||
- **Genesis/YM2612 core**
|
||||
- **Nuked-OPN2**
|
||||
- **ymfm**
|
||||
- **SN76489 core**
|
||||
- **MAME**
|
||||
- **Nuked-PSG Mod**
|
||||
- **NES core**
|
||||
- **puNES**
|
||||
- **NSFplay**
|
||||
- **FDS core**
|
||||
- **puNES**
|
||||
- **NSFplay**
|
||||
- **SID core**
|
||||
- **reSID**
|
||||
- **reSIDfp**
|
||||
- **POKEY core**
|
||||
- **Atari800 (mzpokeysnd)**
|
||||
- **ASAP (C++ port)**
|
||||
- **OPN/OPNA/OPNB cores**
|
||||
- **ymfm only**
|
||||
- **Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)**
|
||||
|
||||
- **PC Speaker strategy:**
|
||||
- **evdev SND_TONE**
|
||||
- **KIOCSOUND on /dev/tty1**
|
||||
- **/dev/port**
|
||||
- **KIOCSOUND on standard output**
|
||||
- **outb()**
|
||||
|
||||
|
||||
- **Arcade/YM2151 core**\
|
||||
**Genesis/YM2612 core**\
|
||||
**SN76489 core**\
|
||||
**NES core**\
|
||||
**FDS core**\
|
||||
**SID core**\
|
||||
**POKEY core**\
|
||||
**OPN/OPNA/OPNB cores**: all of these are covered in the [guide to choosing emulation cores](../9-guides/emulation-cores.md).
|
||||
|
||||
- **PC Speaker strategy**: this is covered in the [PC speaker system doc](../7-systems/pcspkr.md).
|
||||
|
||||
- **Sample ROMs:**
|
||||
- **OPL4 YRW801 path**
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# song info
|
||||
|
||||
- **Name**: The track's title.
|
||||
- **Author**: List of contributors to a song. If the song is a cover of someone else's track, it's customary to list their name first, followed by `[cv. YourName]`.
|
||||
- **Author**: List of contributors to a song. If the song is a cover of someone else's work, it's customary to list their name first, followed by `[cv. YourName]`.
|
||||
- **Album**: The associated album name, the name of the game the song is from, or whatever.
|
||||
- **System**: The game console or computer the track is designed for. This is automatically set when creating a new tune, but it can be changed to anything one wants. The **Auto** button will provide a guess based on the chips in use.
|
||||
- **System**: The game console or computer the track is designed for. This is automatically set when creating a new tune, but in advanced mode, it can be changed to anything one wants. The **Auto** button will provide a guess based on the chips in use.
|
||||
|
||||
All of this metadata will be included in a VGM export. This isn't the case for a WAV export, however.
|
||||
|
||||
**Tuning (A-4)**: Set tuning based on the note A-4, which should be 440 in most cases. Opening an Amiga MOD will set it to 436 for hardware compatibility.
|
||||
- _**Tuning (A-4)**:_ Set tuning based on the note A-4, which should be 440 in most cases. Opening an Amiga MOD will set it to 436 for hardware compatibility. Available only in advanced mode.
|
||||
|
||||
# subsongs
|
||||
|
||||
|
@ -23,6 +23,8 @@ This window allows one to create **subsongs** - multiple individual songs within
|
|||
|
||||
There are multiple ways to set the tempo of a song.
|
||||
|
||||
Items in _italics_ don't appear in basic mode and are only available in advanced mode.
|
||||
|
||||
**Tick Rate**: The frequency of ticks per second, thus the rate at which notes and effects are processed.
|
||||
- All values are allowed for all chips, though most chips have hardware limitations that mean they should stay at either 60 (approximately NTSC) or 50 (exactly PAL).
|
||||
- Clicking the Tick Rate button switches to a more traditional **Base Tempo** BPM setting.
|
||||
|
@ -30,14 +32,14 @@ There are multiple ways to set the tempo of a song.
|
|||
**Speed**: The number of ticks per row.
|
||||
- Clicking the "Speed" button changes to more complex modes covered in the [grooves] page.
|
||||
|
||||
**Virtual Tempo**: Simulates any arbitrary tempo without altering the tick rate. It does this by adding or skipping ticks to approximate the tempo. The two numbers represent a ratio applied to the actual tick rate. Example:
|
||||
_**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rate. It does this by adding or skipping ticks to approximate the tempo. The two numbers represent a ratio applied to the actual tick rate. Example:
|
||||
- Set tick rate to 150 BPM (60 Hz) and speed to 6.
|
||||
- Set the first virtual tempo number (numerator) to 200.
|
||||
- Set the second virtual tempo number (denominator) to 150.
|
||||
- The track will play at 200 BPM.
|
||||
- The ratio doesn't have to match BPM numbers. Set the numerator to 4 and the denominator to 5, and the virtual BPM becomes 150 × 4/5 = 120.
|
||||
|
||||
**Divider**: Changes the effective tick rate. A tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each!
|
||||
_**Divider**:_ Changes the effective tick rate. A tick rate of 60Hz and a divisor of 6 will result in ticks lasting a tenth of a second each!
|
||||
|
||||
**Highlight**: Sets the pattern row highlights:
|
||||
- The first value represents the number of rows per beat.
|
||||
|
@ -46,4 +48,4 @@ There are multiple ways to set the tempo of a song.
|
|||
|
||||
**Pattern Length**: The length of each pattern in rows. This affects all patterns in the song, and every pattern must be the same length. (Individual patterns can be cut short by `0Bxx`, `0Dxx`, and `FFxx` commands.)
|
||||
|
||||
**Song Length**: How many orders are in the order list. Decreasing it will hide the orders at the bottom. Increasing it will restore those orders; increasing it further will add new orders of all `00` patterns.
|
||||
_**Song Length**:_ How many orders are in the order list. Decreasing it will hide the orders at the bottom. Increasing it will restore those orders; increasing it further will add new orders of all `00` patterns.
|
||||
|
|
|
@ -74,7 +74,7 @@ Space | toggle note input (edit)
|
|||
![keyboard](keyboard.png)
|
||||
|
||||
- pressing any of the respective keys will insert a note at the cursor's location, and then advance it by the Edit Step.
|
||||
- note off turns off the last played note in that channel (key off on FM; note cut otherwise).
|
||||
- note off turns off the last played note in that channel (key off for FM; note cut otherwise).
|
||||
- note release triggers macro release (and in FM channels it also triggers key off).
|
||||
- macro release does the same as above, but does not trigger key off in FM channels.
|
||||
|
||||
|
@ -91,4 +91,8 @@ if the effect value is not present, it is treated as `00`.
|
|||
|
||||
most effects run until canceled using an effect of the same type with effect value `00`, with some exceptions.
|
||||
|
||||
for a list of effects [click here](effects.md).
|
||||
here's [a list of effects](effects.md).
|
||||
|
||||
# pop-up menu
|
||||
|
||||
right-clicking within the pattern view brings up a pop-up menu with everything in the [edit menu](../2-interface/menu-bar.md) that makes sense for entering data or altering a selected area.
|
||||
|
|
|
@ -84,7 +84,7 @@ not all chips support these effects.
|
|||
|
||||
## other
|
||||
|
||||
- `9xxx`: **Set sample position.** jumps current sample to position `xxx \* 0x100`.
|
||||
- `9xxx`: **Set sample position.** jumps current sample to position `xxx * 0x100`.
|
||||
- Not all chips support this effect.
|
||||
- `EBxx`: **Set sample bank.**
|
||||
- Does not apply on Amiga.
|
||||
|
@ -97,52 +97,52 @@ additionally, [each chip has its own effects](../7-systems/README.md).
|
|||
|
||||
## macro table
|
||||
|
||||
ID | macro
|
||||
---|-----------------------------
|
||||
00 | volume
|
||||
01 | arpeggio
|
||||
02 | duty/noise
|
||||
03 | waveform
|
||||
04 | pitch
|
||||
05 | extra 1
|
||||
06 | extra 2
|
||||
07 | extra 3
|
||||
08 | extra A (ALG)
|
||||
09 | extra B (FM)
|
||||
0A | extra C (FMS)
|
||||
0B | extra D (AMS)
|
||||
0C | panning left
|
||||
0D | panning right
|
||||
0E | phase reset
|
||||
0F | extra 4
|
||||
10 | extra 5
|
||||
11 | extra 6
|
||||
12 | extra 7
|
||||
13 | extra 8
|
||||
ID | macro
|
||||
-----|-----------------------------
|
||||
`00` | volume
|
||||
`01` | arpeggio
|
||||
`02` | duty/noise
|
||||
`03` | waveform
|
||||
`04` | pitch
|
||||
`05` | extra 1
|
||||
`06` | extra 2
|
||||
`07` | extra 3
|
||||
`08` | extra A (ALG)
|
||||
`09` | extra B (FM)
|
||||
`0A` | extra C (FMS)
|
||||
`0B` | extra D (AMS)
|
||||
`0C` | panning left
|
||||
`0D` | panning right
|
||||
`0E` | phase reset
|
||||
`0F` | extra 4
|
||||
`10` | extra 5
|
||||
`11` | extra 6
|
||||
`12` | extra 7
|
||||
`13` | extra 8
|
||||
| | **operator 1 macros**
|
||||
20 | AM
|
||||
21 | AR
|
||||
22 | DR
|
||||
23 | MULT
|
||||
24 | RR
|
||||
25 | SL
|
||||
26 | TL
|
||||
27 | DT2
|
||||
28 | RS
|
||||
29 | DT
|
||||
2A | D2R
|
||||
2B | SSG-EG
|
||||
2C | DAM
|
||||
2D | DVB
|
||||
2E | EGT
|
||||
2F | KSL
|
||||
30 | SUS
|
||||
31 | VIB
|
||||
32 | WS
|
||||
33 | KSR
|
||||
40 | **operator 2 macros**
|
||||
60 | **operator 3 macros**
|
||||
80 | **operator 4 macros**
|
||||
`20` | AM
|
||||
`21` | AR
|
||||
`22` | DR
|
||||
`23` | MULT
|
||||
`24` | RR
|
||||
`25` | SL
|
||||
`26` | TL
|
||||
`27` | DT2
|
||||
`28` | RS
|
||||
`29` | DT
|
||||
`2A` | D2R
|
||||
`2B` | SSG-EG
|
||||
`2C` | DAM
|
||||
`2D` | DVB
|
||||
`2E` | EGT
|
||||
`2F` | KSL
|
||||
`30` | SUS
|
||||
`31` | VIB
|
||||
`32` | WS
|
||||
`33` | KSR
|
||||
`40` | **operator 2 macros**
|
||||
`60` | **operator 3 macros**
|
||||
`80` | **operator 4 macros**
|
||||
|
||||
the interpretation of duty, wave and extra macros depends on chip/instrument type:
|
||||
|
||||
|
|
BIN
doc/4-instrument/FM-ADSRchart.png
Normal file
After Width: | Height: | Size: 184 KiB |
|
@ -13,7 +13,8 @@ every instrument can be renamed and have its type changed.
|
|||
depending on the instrument type, there are many different types of instrument editor:
|
||||
|
||||
- [FM synthesis](fm.md) - for use with YM2612, YM2151 and FM block portion of YM2610.
|
||||
- [Standard](standard.md) - for use with NES and Sega Master System's PSG sound source and its derivatives.
|
||||
- [PSG](psg.md) - for use with TI SN76489 and derivatives like Sega Master System's PSG.
|
||||
- [NES](nes.md) - for use with NES.
|
||||
- [Game Boy](game-boy.md) - for use with Game Boy APU.
|
||||
- [PC Engine / TurboGrafx-16](pce.md) - for use with PC Engine's wavetable synthesizer.
|
||||
- [WonderSwan](wonderswan.md) - for use with WonderSwan's wavetable synthesizer.
|
||||
|
|
|
@ -1,47 +1,77 @@
|
|||
# FM synthesis instrument editor
|
||||
|
||||
FM editor is divided into 7 tabs:
|
||||
The FM editor is divided into 7 tabs:
|
||||
|
||||
- **FM**: for controlling the basic parameters of FM sound source.
|
||||
- **Macros (FM)**: for macros controlling algorithm, feedback and LFO
|
||||
- **Macros (OP1)**: for macros controlling FM paramets of operator 1
|
||||
- **Macros (OP2)**: for macros controlling FM paramets of operator 2
|
||||
- **Macros (OP3)**: for macros controlling FM paramets of operator 3
|
||||
- **Macros (OP4)**: for macros controlling FM paramets of operator 4
|
||||
- **Macros**: for miscellaneous macros controlling volume, argeggio and YM2151 noise generator.
|
||||
- **Macros (OP1)**: for macros controlling FM parameters of operator 1
|
||||
- **Macros (OP2)**: for macros controlling FM parameters of operator 2
|
||||
- **Macros (OP3)**: for macros controlling FM parameters of operator 3
|
||||
- **Macros (OP4)**: for macros controlling FM parameters of operator 4
|
||||
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and YM2151 noise generator.
|
||||
|
||||
## FM
|
||||
|
||||
FM synthesizers Furnace supports are four-operator, meaning it takes four oscillators to produce a single sound. Each operator is controlled by a dozen sliders:
|
||||
The FM synthesizers Furnace supports are four-operator, meaning it takes four oscillators to produce a single sound.
|
||||
|
||||
These apply to the instrument as a whole:
|
||||
- **Feedback (FB)**: Determines how many times operator 1 returns its output to itself. (0-7 range)
|
||||
- **Algorithm (AL)**: Determines how operators are connected to each other. (0-7 range)
|
||||
- Left-click pops up a small "operators changes with volume?" dialog where each operator can be toggled to scale with volume level.
|
||||
- Right-click to switch to a preview display of the waveform generated on a new note:
|
||||
- Left-click restarts the preview.
|
||||
- Middle-click pauses and unpauses the preview.
|
||||
- Right-click returns to algorithm view.
|
||||
|
||||
- **LFO Frequency Sensitivity**: Determines the amount of LFO frequency changes. (0-7 range)
|
||||
- **LFO Amplitude Sensitivity (AM)**: Determines the amount of LFO amplitude changes. (0-3 range)
|
||||
|
||||
These apply to each operator:
|
||||
- The crossed-arrows button can be dragged to rearrange operators.
|
||||
- The **OP1**, **OP2**, **OP3**, and **OP4** buttons enable or disable those operators.
|
||||
- **Amplitude Modulation (AM)**: Makes the operator affected by LFO.
|
||||
- **Hardware Envelope Generator (SSG-EG)**: Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. YM2610/YM2612 sound source only.
|
||||
- **Attack Rate (AR)**: determines the rising time for the sound. The bigger the value, the faster the attack. (0-31 range)
|
||||
- **Decay Rate (DR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. It's the initial amplitude decay rate. (0-31 range)
|
||||
- **Secondary Decay Rate (DR2)/Sustain Rate (SR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. This is the long "tail" of the sound that continues as long as the key is depressed. (0-31 range)
|
||||
- **Sustain Level (SL)**: Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range)
|
||||
- **Secondary Decay Rate (DR2) / Sustain Rate (SR)**: Determines the diminishing time for the sound. The higher the value, the shorter the decay. This is the long "tail" of the sound that continues as long as the key is depressed. (0-31 range)
|
||||
- **Release Rate (RR)**: Determines the rate at which the sound disappears after KEY-OFF. The higher the value, the shorter the release. (0-15 range)
|
||||
- **Sustain Level(SL)**: Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range)
|
||||
- **Total Level (TL)**: Represents the envelope’s highest amplitude, with 0 being the largest and 127 (decimal) the smallest. A change of one unit is about 0.75 dB.
|
||||
- **Envelope Scale (KSR)**: A parameter that determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range)
|
||||
|
||||
![FM ADSR chart](FM-ADSRchart.png)
|
||||
|
||||
- **Envelope Scale (KSR)**: Also known as "Key Scale". Determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range)
|
||||
- **Frequency Multiplier (MULT)**: Determines the operator frequency in relation to the pitch. (0-15 range)
|
||||
- **Fine Detune (DT)**: Shifts the pitch a little (0-7 range)
|
||||
- **Coarse Detune (DT2)**: Shifts the pitch by tens of cents (0-3 range) WARNING: this parameter affects only YM2151 sound source!!!
|
||||
- **Hardware Envelope Generator (SSG-EG)**: Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. WARNING: this parameter affects only YM2610/YM2612 sound source!!!
|
||||
- **Algorithm (AL)**: Determines how operators are connected to each other. (0-7 range)
|
||||
- **Feedback (FB)**: Determines the amount of signal whick operator 1 returns to itself. (0-7 range)
|
||||
- **Amplitude Modulation (AM)**: Makes the operator affected by LFO.
|
||||
- **LFO Frequency Sensitivity**: Determines the amount of LFO frequency changes. (0-7 range)
|
||||
- **LFO Amplitude Sensitivity (AM)**: Determines the amount of LFO frequency changes. (0-3 range)
|
||||
- **Fine Detune (DT)**: Shifts the pitch a little. (0-7 range)
|
||||
- **Coarse Detune (DT2)**: Shifts the pitch by tens of cents. (0-3 range) YM2151 sound source only.
|
||||
|
||||
|
||||
## macros
|
||||
|
||||
Macros define the sequence of values passed to the given parameter. Via macro, along with the previously mentioned parameters, the following can be controlled:
|
||||
|
||||
## FM Macros
|
||||
|
||||
- **AM Depth**: amplitude modulation depth. YM2151 sound source only.
|
||||
- **PM Depth**: pitch modulation depth. YM2151 sound source only.
|
||||
- **LFO Speed**: LFO frequency.
|
||||
- **LFO Shape**: LFO shape. Choose between saw, square, triangle, and random.
|
||||
- **OpMask**: toggles each operator.
|
||||
|
||||
## OP1-OP4 Macros
|
||||
|
||||
All parameters are listed above.
|
||||
|
||||
## Macros
|
||||
|
||||
Macros define the sequence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled:
|
||||
|
||||
- **LFO Frequency**
|
||||
- **LFO Waveform**: _WARNING:_ this parameter affects only YM2151 sound source!
|
||||
- **Amplitude Modulation Depth**: _WARNING:_ this parameter affects only YM2151 sound source!
|
||||
- **Frequency Modulation Depth**: _WARNING:_ this parameter affects only YM2151 sound source!
|
||||
- **Arpeggio Macro**: Pitch change sequence in semitones. Two modes are available:
|
||||
- **Absolute** (default): Executes the pitch with absolute change based on the pitch of the actual note.
|
||||
- **Fixed**: Executes at the pitch specified in the sequence regardless of the note pitch.
|
||||
- **Arpeggio**: Pitch change sequence in semitones.
|
||||
- **Noise Frequency**: specifies the noise frequency in noise mode of YM2151's Channel 8 Operator 4 special mode.
|
||||
- **Panning**: toggles output on left and right channels.
|
||||
- **Pitch**: fine pitch.
|
||||
- **Relative**: pitch changes are relative to the current pitch, not the note's base pitch.
|
||||
- **Phase Reset**: Restarts all operators and resets the waveform to its start. Effectively the same as a `0Cxx` retrigger.
|
||||
|
||||
Looping: You can loop the execution of part of a sequence. Left-click anywhere on the Loop line at the bottom of the editor to create a loop. You can move the start and end points of the loop by dragging both ends of the loop. Rigkt-click to remove the loop.
|
||||
|
||||
# links
|
||||
|
||||
[FM instrument tutorial](https://www.youtube.com/watch?v=wS8edjurjDw): A great starting point to learn how create and work with FM sounds. This was made for DefleMask, but all the same principles apply.
|
||||
|
|
|
@ -4,27 +4,23 @@ The Namco 163 instrument editor consists of two tabs: "Namco 163" for control of
|
|||
|
||||
## Namco 163
|
||||
|
||||
- **Waveform**: Determines the initial waveform for playing.
|
||||
- **Offset**: Determines the initial waveform position will be load to RAM.
|
||||
- **Length**: Determines the initial waveform length will be load to RAM.
|
||||
- **Load waveform before playback**: Determines the load initial waveform into RAM before playback.
|
||||
- **Update waveforms into RAM when every waveform changes**: Determines the update every different waveform changes in playback.
|
||||
|
||||
- **Load waveform**: if enabled, a waveform will be loaded when this instrument plays.
|
||||
- if it isn't then only the offset/length change.
|
||||
- **Waveform**: determines the waveform that will be loaded.
|
||||
- only appears when Load waveform is enabled.
|
||||
- **Per-channel wave offset/length**: when enabled, the offset/length settings are split per channel.
|
||||
- **Offset**: determines the waveform position in RAM.
|
||||
- **Length**: determines the waveform length in RAM.
|
||||
|
||||
## Macros
|
||||
|
||||
- **Volume**: volume levels sequence
|
||||
- **Arpeggio**: pitch sequence
|
||||
- **Wave Pos**: sets waveform seek position in RAM
|
||||
- **Waveform**: sets waveform source for playback immediately or update later
|
||||
- **Panning**: output for left and right channels
|
||||
- **Pitch**: fine pitch
|
||||
- **Phase Reset**: trigger restart of waveform
|
||||
- **Wave Length**: sets waveform length
|
||||
<!--
|
||||
- **Waveform pos.**: sets the waveform source address in RAM for playback (single nibble unit)
|
||||
- **Waveform len.**: sets the waveform source length for playback (4 nibble unit)
|
||||
- **Waveform update**: sets the waveform update trigger behavior for playback
|
||||
- **Waveform to load**: sets waveform source for load to RAM immediately or later
|
||||
- **Wave pos. to load**: sets address of waveform for load to RAM (single nibble unit)
|
||||
- **Wave len. to load**: sets length of waveform for load to RAM (4 nibble unit)
|
||||
- **Waveform load**: sets the waveform load trigger behavior
|
||||
- **Phase Reset**: trigger restart of waveform
|
||||
-->
|
18
doc/4-instrument/nes.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Standard instrument editor
|
||||
|
||||
The instrument editor for NES consists of these macros:
|
||||
|
||||
- **Volume**: volume.
|
||||
- **Arpeggio**: pitch in half-steps.
|
||||
- **Duty**: duty cycle and noise mode.
|
||||
- pulse duty cycles:
|
||||
- `0`: 12.5%
|
||||
- `1`: 25%
|
||||
- `2`: 50%
|
||||
- `3`: 75%
|
||||
- noise modes:
|
||||
- `0`: long noise.
|
||||
- `1`: short noise.
|
||||
- **Panning**: output for left and right channels.
|
||||
- **Pitch**: fine pitch.
|
||||
- **Phase Reset**: trigger restart of waveform.
|
14
doc/4-instrument/psg.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# PSG instrument editor
|
||||
|
||||
The instrument editor for PSG (SMS, MSX, and other TI SN76489 derivatives) consists of these macros:
|
||||
|
||||
- **Volume**: volume.
|
||||
- **Arpeggio**: pitch in half-steps.
|
||||
- **Duty**: noise mode.
|
||||
- `0`: short noise, preset frequencies.
|
||||
- `1`: long noise, preset frequencies.
|
||||
- `2`: short noise, use channel 3 for frequency.
|
||||
- `3`: long noise, use channel 3 for frequency.
|
||||
- **Panning**: output for left and right channels.
|
||||
- **Pitch**: fine pitch.
|
||||
- **Phase Reset**: trigger restart of waveform.
|
|
@ -4,5 +4,5 @@ The SCC/Bubble System WSG instrument editor consists of these macros:
|
|||
|
||||
- **Volume**: volume sequence
|
||||
- **Arpeggio**: pitch sequence
|
||||
- **Waveform**: spicifies wavetables sequence
|
||||
- **Waveform**: specifies wavetable sequence
|
||||
- **Pitch**: fine pitch
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# Standard instrument editor
|
||||
|
||||
The instrument editor for NES and PSG (SMS, MSX, and such) consists of these macros:
|
||||
|
||||
- **Volume**: volume
|
||||
- **Arpeggio**: pitch in half-steps
|
||||
- **Duty**: duty cycle and noise mode for NES channels. _Note:_ This has no effect on Sega Master System.
|
||||
- **Panning**: output for left and right channels
|
||||
- **Pitch**: fine pitch
|
||||
- **Phase Reset**: trigger restart of waveform
|
11
doc/4-instrument/ted.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# TED instrument editor
|
||||
|
||||
TED instrument editor consists of these macros:
|
||||
|
||||
- **Volume**: volume sequence. **global!**
|
||||
- **Arpeggio**: pitch sequence
|
||||
- **Square/Noise**: select whether square, noise or nothing will be output
|
||||
- noise only available on channel 2
|
||||
- if square and noise are enabled, square takes precedence.
|
||||
- **Pitch**: "fine" pitch
|
||||
- **Phase Reset**: trigger restart of waveform. **global!**
|
|
@ -1,23 +1,121 @@
|
|||
# wavetables
|
||||
|
||||
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape.
|
||||
|
||||
Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "synth?" column.
|
||||
|
||||
system | width | height | synth?
|
||||
--------------------|------:|:-------|:------
|
||||
Bubble System | 32 | 16 |
|
||||
Game Boy | 32 | 16 |
|
||||
SM8521 | 32 | 16 |
|
||||
Namco WSG | 32 | 16 | RAM only
|
||||
WonderSwan | 32 | 16 |
|
||||
Namco 163 | ≤128 | 16 |
|
||||
SNES | ≤256 | 16 |
|
||||
PC Engine | 32 | 32 |
|
||||
Virtual Boy | 32 | 64 | no
|
||||
Famicom Disk System | 64 | 64 |
|
||||
Konami SCC | 32 | 256 |
|
||||
Seta X1-010 | 128 | 256 |
|
||||
Amiga | ≤256 | 256 |
|
||||
|
||||
|
||||
# wavetable editor
|
||||
|
||||
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.6pre4, wavetable editor affects PC Engine, WonderSwan, Namco WSGs, Virtual Boy, Game.com, SCC, FDS, Seta X1-010, Konami Bubble System WSG, SNES, Amiga and channel 3 of Game Boy.
|
||||
![wavetable editor](wave-editor.png)
|
||||
|
||||
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan, Namco WSG, N163, Game.com, Virtual Boy and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System, SNES, Namco WSG and N163, 32-level height for PCE and 64-level height for Virtual Boy. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips.
|
||||
controls across the top line:
|
||||
- waveform number. the `-` and `+` buttons step through the list.
|
||||
- open.
|
||||
- save.
|
||||
- **Steps**: view waveform as discrete blocks.
|
||||
- **Lines**: view waveform as a continuous line.
|
||||
- **Width**: length of the waveform data. maximum is 256.
|
||||
- **Height**: depth of the waveform. maximum is 256.
|
||||
- `<` / `>`: toggle tabs (described below).
|
||||
|
||||
Furnace's wavetable editor features multiple ways of creating desired waveform shape:
|
||||
waveform display:
|
||||
- the waveform is directly editable with the mouse.
|
||||
- hovering will display a tooltip with the waveform position and value.
|
||||
|
||||
- **Shape** tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders:
|
||||
- **Duty**: Affects mainly pulse waves, determining its wisth, like on C64/VRC6
|
||||
- **Exponent**: Powers the waveform in the mathematical sense of the word (^2, ^3 and so on)
|
||||
- **XOR Point**: Determines the point where the waveform gets negated.
|
||||
- _TODO:_ amplitude/phase part
|
||||
- **FM** for creating the waveform with frequency modulation synthesis principles: One can set carrier/modulation levels, frquency multiplier, connection between operators and FM waveforms of these operators.
|
||||
- **WaveTools**: Allows user to fine-tune the waveform: scale said waveform in both X and Y axes, smoothen, amplify, normalize, convert to signed/unisgned, invert or even randomize the wavetable.
|
||||
controls across the bottom line:
|
||||
- **Dec**: view MML stream as decimal.
|
||||
- **Hex**: view MML stream as hexadecimal.
|
||||
- `+` / `±`: toggle MML stream as unsigned/signed. also adjusts waveform display.
|
||||
- MML stream: waveform data as an editable numeric sequence.
|
||||
|
||||
## wavetable synthesizer
|
||||
## tabs
|
||||
|
||||
Furnace contains a mode for wavetable instruments that allows you to modulate or combine 1 or 2 waves to create unique "animated" sounds. Think of it like a VST or a plugin, as it's basically an extension of regular wavetable soundchips that still allow it to run on real hardware.
|
||||
each tab provides different ways of creating or altering a waveform.
|
||||
|
||||
### Shapes
|
||||
|
||||
![wavetable shape tab](wave-editor-shapes.png)
|
||||
|
||||
this creates a waveform by adding together a few predefined basic wave shapes.
|
||||
- shape: select shape from sine, triangle, saw, and square.
|
||||
- **Duty**: only affects pulse waves, determining their width
|
||||
- **Exponent**: applies an exponent (power) to the waveform (^2, ^3 and so on).
|
||||
- **XOR Point**: determines the point where the waveform gets negated.
|
||||
- **Amplitude/Phase**: add together up to 16 instances of the shape.
|
||||
- **Amplitude**: height of the shape.
|
||||
- **Phase**: position along the shape. for example, 0.250 starts the shape a quarter of the way along.
|
||||
|
||||
### FM
|
||||
|
||||
![wavetable FM tab](wave-editor-FM.png)
|
||||
|
||||
this creates a waveform using frequency modulation synthesis with up to four operators.
|
||||
|
||||
one can set carrier/modulation levels, frequency multipliers, connections between operators and FM waveforms of these operators.
|
||||
|
||||
### WaveTools
|
||||
|
||||
![wavetable tools tab](wave-editor-tools.png)
|
||||
|
||||
these are useful editing tools to fine-tune the waveform:
|
||||
- **Scale X**: stretches the waveform to a new length.
|
||||
- interpolation method: filters the waveform when stretching. choose from none, linear, cosine, and cubic interpolation.
|
||||
- **Scale Y**: resizes the waveform to a new height. it will clip at the top and bottom.
|
||||
- **Offset X**: slides the the waveform forward or back. it will wrap around.
|
||||
- **Offset Y**: slides the waveform up or down. it will clip at the top and bottom.
|
||||
- **Smooth**. smooths waveform.
|
||||
- **Amplify**. changes the volume of the waveform. it will clip at the top and bottom.
|
||||
- **Normalize**: stretches waveform to maximum within the wavetable height.
|
||||
- **Invert**: flips waveform vertically.
|
||||
- **Half**: halves the waveform's frequency by stretching its first half to fill the waveform length.
|
||||
- **Double**: doubles the waveform's frequency by squashing it to half length then repeating it.
|
||||
- **Convert Signed/Unsigned**. worth trying if an imported wave sounds corrupted.
|
||||
- **Randomize**: generate a completely random waveform.
|
||||
|
||||
|
||||
|
||||
# wavetable synthesizer
|
||||
|
||||
Within the "Wavetable" tab of the instrument editor, Furnace allows you to modulate or combine 1 or 2 waves to create unique "animated" sounds. Think of it like a VST or a plugin, as it's basically an extension of regular wavetable soundchips that still allow it to run on real hardware.
|
||||
|
||||
This is accomplished by selecting a wave or two, a mode, and adjusting the settings as needed until you come up with a sound that you like, without taking up a load of space. This allows you to create unique sound effects or instruments, that, when used well, almost sound like they're Amiga samples.
|
||||
|
||||
Unfortunately, on chips like the HuC6280, you cannot use the wavetable synth to animate waveforms and have them sound smooth, as the chip resets the channel's phase when a waveform is changed while the channel is playing. On certain frequencies, this can be avoided, but not on most, unfortunately.
|
||||
Unfortunately, on some chips like the HuC6280, you cannot use the wavetable synth to animate waveforms and have them sound smooth, as the chip resets the channel's phase when a waveform is changed while the channel is playing. On certain frequencies, this can be avoided, but not on most, unfortunately.
|
||||
|
||||
![instrument wavetable tab](instrument-wavetable.png)
|
||||
|
||||
input waveforms should match the size of the wavetable or unexpected results may occur.
|
||||
|
||||
- **Enable synthesizer**: must be on for the rest of this to work.
|
||||
- synthesizer type: selects the synthesis algorithm.
|
||||
- waveform displays.
|
||||
- **Wave 1**: selects input waveform.
|
||||
- **Wave 2**: selects second input waveform. only appears when a dual-waveform synthesizer is selected.
|
||||
- **Pause preview**: toggles live waveform preview.
|
||||
- **Restart preview**: restarts preview from initial state.
|
||||
- **Copy to new wavetable**: copies the currently displayed output waveform into the wavetable as a new entry.
|
||||
- (width×height): size of wavetable.
|
||||
- **Update Rate**: time in ticks between waveform changes.
|
||||
- **Speed**: rate of change with each update.
|
||||
- **Amount**: strength of synthesizer function.
|
||||
- **Power**: only appears when synthesizer type is "Phase Modulation".
|
||||
- **Global**:
|
||||
- if disabled, each note resets the synthesizer to the start.
|
||||
- if enabled, synthesis continues unbroken from note to note.
|
||||
|
|
BIN
doc/5-wave/instrument-wavetable.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
doc/5-wave/wave-editor-FM.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
doc/5-wave/wave-editor-shapes.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
doc/5-wave/wave-editor-tools.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
doc/5-wave/wave-editor.png
Normal file
After Width: | Height: | Size: 44 KiB |
|
@ -47,7 +47,7 @@ use of this mode is discouraged in favor of Sample type instruments.
|
|||
|
||||
due to limitations in some of those sound chips, some restrictions exist:
|
||||
|
||||
- Amiga: sample lengths and loop will be set to an even number, and your sample can't be longer than 131070.
|
||||
- Amiga: maximum frequency is 31,469Hz, but anything over 28,867 will sound glitchy on hardware. sample lengths and loop will be set to an even number, and your sample can't be longer than 131070.
|
||||
- NES: if on DPCM mode, only a limited selection of frequencies is available, and loop position isn't supported (only entire sample).
|
||||
- SegaPCM: your sample can't be longer than 65535, and the maximum frequency is 31.25KHz.
|
||||
- QSound: your sample can't be longer than 65535, and the loop length shall not be greater than 32767.
|
||||
|
|
|
@ -14,6 +14,7 @@ this is a list of sound chips that Furnace supports, including effects.
|
|||
- [Famicom Disk System](fds.md)
|
||||
- [Game Boy](game-boy.md)
|
||||
- [Konami K007232](k007232.md)
|
||||
- [Konami K056320](k056320.md)
|
||||
- [Konami SCC](scc.md)
|
||||
- [Konami VRC6](vrc6.md)
|
||||
- [Atari Lynx](lynx.md)
|
||||
|
|
|
@ -15,3 +15,9 @@ imported MOD files use this chip, and will set A-4 tuning to 436.
|
|||
- does not work on the last channel.
|
||||
- `13xx`: **change wave.**
|
||||
- only works when "Mode" is set to "Wavetable" in the instrument.
|
||||
|
||||
# info
|
||||
|
||||
- the maximum rate for sample playback is technically 31469Hz but anything higher than 28867Hz will sound glitchy on hardware.
|
||||
- sample lengths and loop will be set to an even number.
|
||||
- samples can't be longer than 131070.
|
|
@ -6,6 +6,8 @@ it is a 3-channel square/noise/envelope sound generator. the chip's powerful sou
|
|||
|
||||
the AY-3-8914 variant was used in Intellivision, which is pretty much an AY with 4 level envelope volume per channel and different register format.
|
||||
|
||||
as of Furnace 0.6pre7, AY-3-8910 supports software sample playback, where all 3 channels can play 4-bit PCM samples (at the cost of a very high CPU usage)
|
||||
|
||||
# effects
|
||||
|
||||
- `20xx`: **set channel mode.**
|
||||
|
@ -44,3 +46,12 @@ the AY-3-8914 variant was used in Intellivision, which is pretty much an AY with
|
|||
- this changes the port's mode to "write". make sure you have connected something to it.
|
||||
- `2Fxx`: **write to I/O port B.**
|
||||
- this changes the port's mode to "write". make sure you have connected something to it.
|
||||
|
||||
# chip config
|
||||
## AY derivative modes
|
||||
|
||||
AY-3-810 was an absurdly popular chip that was blessed with many third-party clones, licensed or not.
|
||||
|
||||
- the AY-3-8914 variant was used in Intellivision, which is pretty much an 8910 with 4 level envelope volume per channel and different register format.
|
||||
- Yamaha YM2149 was an AY-3-8910 clone released in 1983. it's almost identical to AY with minor differences being: higher hardware envelope step resolution (16 vs 32), half-clock mode when voltage level is low, much stronger DC offset and cleaner, but softer output.
|
||||
- Sunsoft 5B is YM2149 clone with half-clock mode forced on.
|
||||
|
|
|
@ -7,6 +7,8 @@ it is best known for being used in the Covox Sound Master, which didn't sell wel
|
|||
|
||||
emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted.
|
||||
|
||||
as of Furnace 0.6pre7, AY8930 supports software PCM, where all 3 channels can play 5-bit PCM samples (at the cost of a very high CPU usage)
|
||||
|
||||
# effects
|
||||
|
||||
- `12xx`: **set channel duty cycle.**
|
||||
|
|
|
@ -54,7 +54,7 @@ CSM is beyond the scope of this documentation. for more information, see this [b
|
|||
|
||||
## DualPCM
|
||||
|
||||
DualPCM splits channel 6 into two individual PCM channels. using the console's Z80 processor, these are mixed together in software and streamed to channel 6 in PCM mode. because this generates a stream of data, exported VGM files will be very large.
|
||||
[info here.](ym2612.md)
|
||||
|
||||
## Sega CD
|
||||
|
||||
|
|
9
doc/7-systems/k056320.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Konami 056320
|
||||
|
||||
the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats.
|
||||
|
||||
|
||||
|
||||
# effects
|
||||
|
||||
- `DFxx`: Set sample playback direction. `0` is normal; `1` is reverse.
|
|
@ -1,7 +1,20 @@
|
|||
# OKI MSM6258
|
||||
|
||||
a single-channel ADPCM sound source developed by OKI. it allows max sample rate of 15.6 KHz... with no variable pitch. most prominent use of this chip was Sharp X68000 computer, where it was paired with Yamaha YM2151.
|
||||
Furnace's implementation is MSM6258V, a CPU driven variant that is unlimited by amount of sample data, being able to be fed from the system's RAM.
|
||||
|
||||
# effects
|
||||
|
||||
...
|
||||
# chip config
|
||||
|
||||
## chip clock rates
|
||||
|
||||
MSM6258 is an extremely basic ADPCM sound codec. it has no variable frequency rate; it depends on clock rate of a chip itself. Furnace supports following rates:
|
||||
|
||||
| clock rate | sampling rate |
|
||||
|--------------------|---------------|
|
||||
| 4 MHz | 7812 Hz |
|
||||
| 4.096 MHz | 8000 Hz |
|
||||
| 8 MHz | 15625 Hz |
|
||||
| 8.192 MHz | 16000 Hz |
|
||||
|
|
|
@ -1,7 +1,33 @@
|
|||
# OKI MSM6295
|
||||
|
||||
an upgrade from 6258 - it provides 4 ADPCM channels, at max 32 KHz (still no variable pitch though). between late '80s and late '90s, it was one of the most common, if not _the_ most common soundchip used in arcade machines (Capcom, Toaplan, Kaneko, Atari, Tecmo, the list can go on and on...)
|
||||
an upgrade from 6258 - it provides 4 ADPCM channels, at max 32 KHz (still no variable pitch though). between late '80s and late '90s, it was one of the most common, if not _the_ most common soundchip used in arcade machines (Capcom, Toaplan, Kaneko, Atari, Tecmo, the list can go on and on...). Without bankswitching, the chip supports 256kB of sample RAM and can hold up to 127 samples at once.
|
||||
|
||||
# effects
|
||||
|
||||
- `20xx`: **set chip output rate.**
|
||||
|
||||
# chip config
|
||||
## chip clock rates
|
||||
like MSM6258, MSM295 is an extremely basic ADPCM sound codec. it has no variable frequency rate, it depends on clock rate of a chip itself. Furnace supports following rates:
|
||||
|
||||
| clock rate | sampling rate |
|
||||
|--------------------|---------------|
|
||||
| 1 MHz | 7576 Hz |
|
||||
| 1.02 MHz | 7727 Hz |
|
||||
| 1.056 MHz | 8000 Hz |
|
||||
| 1.193 MHz | 9038 Hz |
|
||||
| 0.89 MHz | 6742 Hz |
|
||||
| 0.875 MHz | 6629 Hz |
|
||||
| 0.9375 MHz | 7102 Hz |
|
||||
| 1.5 MHz | 11364 Hz |
|
||||
| 1.79 MHz | 13561 Hz |
|
||||
| 2 MHz | 15152Hz |
|
||||
| 2.112 MHz | 16000 Hz |
|
||||
| 3 MHz | 22728 Hz |
|
||||
| 3.58 MHz | 27122 Hz |
|
||||
| 4 MHz | 30304 Hz |
|
||||
| 4.224 MHz | 32000 Hz |
|
||||
|
||||
## chip clock divisor
|
||||
|
||||
MSM6295 clock rate could be divided by 132 (resulting sample rates above), or by 165. To get a clock rate using divisor of 165, formula is clock rate (in Hz) / 165. Example: 1 MHz MSM6295 in 165 divisor mode results in output rate of 6060 Hz.
|
||||
|
|
|
@ -8,34 +8,33 @@ wavetables are variable in size and may be allocated anywhere in RAM. at least 1
|
|||
|
||||
Namco 163 uses time-division multiplexing for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated.
|
||||
|
||||
Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands.
|
||||
# waveform load position versus waveform position
|
||||
|
||||
you must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes.
|
||||
in Furnace, waveform **load** position/length is different from the waveform position/length.
|
||||
|
||||
both waveform playback and load command work independently per each channel columns.
|
||||
global commands don't care about the channel columns for work commands and its load behavior is independent with per-channel column load commands.
|
||||
when placing a note, the load pos/len and the pos/len are set to the values specified in the instrument.
|
||||
|
||||
waveforms are loaded in the region set by the **load** pos/len, which you can change using effects `15xx` and `16xx` as described below.
|
||||
|
||||
the region that will play is set by the waveform pos/len, which you can alter using effects `11xx` and `12xx`.
|
||||
|
||||
the waveform pos/len macros only change the pos/len, and not the **load** one.
|
||||
|
||||
if the waveform changes (e.g. ins change, wave macro or wave synth), or the **load** pos/len changes, the wave is written to memory.
|
||||
|
||||
# effects
|
||||
|
||||
- `10xx`: **set waveform for playback.**
|
||||
- `11xx`: **set waveform position in RAM for playback.** single nibble unit.
|
||||
- `12xx`: **set waveform length in RAM for playback.** `04` to `FC`, 4 nibble unit.
|
||||
- `130x`: **set playback waveform update behavior.**
|
||||
- `0`: off.
|
||||
- bit 0: update now.
|
||||
- bit 1: update when every waveform is changed.
|
||||
- `14xx`: **set waveform for load to RAM.**
|
||||
- `15xx`: **set waveform position for load to RAM.** single nibble unit.
|
||||
- `16xx`: **set waveform length for load to RAM.** `04` to `FC`, 4 nibble unit.
|
||||
- `170x`: **set waveform load behavior.**
|
||||
- `0`: off.
|
||||
- bit 0: load now.
|
||||
- bit 1: load when every waveform is changed.
|
||||
- `180x`: **set channel limit.** range is `0` to `7`; 1 is added to get results of 1 through 8.
|
||||
- `20xx`: **globally set waveform for load to RAM.**
|
||||
- `21xx`: **globally set waveform position for load to RAM.** single nibble unit.
|
||||
- `22xx`: **globally set waveform length for load to RAM.** `04` to `FC`, 4 nibble unit.
|
||||
- `230x`: **globally set waveform load behavior.**
|
||||
- `0`: off.
|
||||
- bit 0: load now.
|
||||
- bit 1: load when every waveform is changed.
|
||||
- `11xx`: **set waveform position in RAM for playback.**
|
||||
- `12xx`: **set waveform length in RAM for playback.**
|
||||
- `x` goes from `04` to `FC` in steps of 4.
|
||||
- `15xx`: **set waveform load position.**
|
||||
- `16xx`: **set waveform load length.**
|
||||
- `x` goes from `04` to `FC` in steps of 4.
|
||||
- `180x`: **set channel limit.**
|
||||
- range of `x` is `0` to `7`. 1 is added to get results of 1 through 8.
|
||||
- `20xx`: **load a waveform to RAM.**
|
||||
- `x` is the waveform.
|
||||
- the length is determined by the wave's width (it will be snapped to a multiple of 4 if it isn't).
|
||||
- make sure to use `21xx` first!
|
||||
- `21xx`: **set position for 20xx.**
|
||||
|
|
|
@ -83,45 +83,6 @@ reference: [NESdev](https://www.nesdev.org/wiki/APU_Noise)
|
|||
|
||||
## length counter table
|
||||
|
||||
<!-- organized by input value... of little use -->
|
||||
<!--
|
||||
value | raw | NTSC | PAL | Dendy | NTSC 5-step | PAL 5-step | Dendy 5-step
|
||||
-----:|----:|------:|------:|------:|------------:|-----------:|-------------:
|
||||
`00` | 10 | 83ms | 100ms | 84ms | 104ms | 125ms | 105ms
|
||||
`01` | 254 | 2.1s | 2.5s | 2.1s | 2.6s | 3.2s | 2.7s
|
||||
`02` | 20 | 166ms | 200ms | 168ms | 208ms | 250ms | 210ms
|
||||
`03` | 2 | 17ms | 20ms | 17ms | 21ms | 25ms | 21ms
|
||||
`04` | 40 | 333ms | 400ms | 336ms | 417ms | 500ms | 421ms
|
||||
`05` | 4 | 33ms | 40ms | 34ms | 42ms | 50ms | 42ms
|
||||
`06` | 80 | 667ms | 800ms | 673ms | 833ms | 1.0s | 841ms
|
||||
`07` | 6 | 50ms | 60ms | 50ms | 63ms | 75ms | 63ms
|
||||
`08` | 160 | 1.3s | 1.6s | 1.3s | 1.7s | 2.0s | 1.7s
|
||||
`09` | 8 | 67ms | 80ms | 67ms | 83ms | 100ms | 84ms
|
||||
`0A` | 60 | 500ms | 600ms | 505ms | 625ms | 750ms | 631ms
|
||||
`0B` | 10 | 83ms | 100ms | 84ms | 104ms | 125ms | 105ms
|
||||
`0C` | 14 | 117ms | 140ms | 118ms | 146ms | 175ms | 147ms
|
||||
`0D` | 12 | 100ms | 120ms | 101ms | 125ms | 150ms | 126ms
|
||||
`0E` | 26 | 217ms | 260ms | 219ms | 271ms | 325ms | 273ms
|
||||
`0F` | 14 | 117ms | 140ms | 118ms | 145ms | 175ms | 147ms
|
||||
`10` | 12 | 100ms | 120ms | 101ms | 125ms | 150ms | 126ms
|
||||
`11` | 16 | 133ms | 160ms | 135ms | 167ms | 200ms | 168ms
|
||||
`12` | 24 | 200ms | 240ms | 202ms | 250ms | 300ms | 252ms
|
||||
`13` | 18 | 150ms | 180ms | 151ms | 188ms | 225ms | 189ms
|
||||
`14` | 48 | 400ms | 480ms | 404ms | 500ms | 600ms | 505ms
|
||||
`15` | 20 | 167ms | 200ms | 168ms | 208ms | 250ms | 210ms
|
||||
`16` | 96 | 800ms | 960ms | 807ms | 1.0s | 1.2s | 1.0s
|
||||
`17` | 22 | 183ms | 220ms | 185ms | 229ms | 275ms | 231ms
|
||||
`18` | 192 | 1.6s | 1.9s | 1.6s | 2.0s | 2.4s | 2.0s
|
||||
`19` | 24 | 200ms | 240ms | 202ms | 250ms | 300ms | 252ms
|
||||
`1A` | 72 | 600ms | 720ms | 606ms | 750ms | 900ms | 757ms
|
||||
`1B` | 26 | 217ms | 260ms | 219ms | 271ms | 325ms | 273ms
|
||||
`1C` | 16 | 133ms | 160ms | 135ms | 167ms | 200ms | 168ms
|
||||
`1D` | 28 | 233ms | 280ms | 235ms | 292ms | 350ms | 294ms
|
||||
`1E` | 32 | 267ms | 320ms | 269ms | 333ms | 400ms | 336ms
|
||||
`1F` | 30 | 250ms | 300ms | 252ms | 313ms | 375ms | 315ms
|
||||
-->
|
||||
|
||||
<!-- organized by resulting times... more useful! -->
|
||||
value | raw | NTSC | PAL | Dendy | NTSC 5-step | PAL 5-step | Dendy 5-step
|
||||
-----:|----:|------:|------:|------:|------------:|-----------:|-------------:
|
||||
`03` | 2 | 17ms | 20ms | 17ms | 21ms | 25ms | 21ms
|
||||
|
@ -161,21 +122,25 @@ reference: [NESdev](https://www.nesdev.org/wiki/APU_Length_Counter)
|
|||
|
||||
## DPCM frequency table
|
||||
|
||||
value | NTSC | PAL
|
||||
------|----------:|----------:
|
||||
`00` | 4181.7Hz | 4177.4Hz
|
||||
`01` | 4709.9Hz | 4696.6Hz
|
||||
`02` | 5264.0Hz | 5261.4Hz
|
||||
`03` | 5593.0Hz | 5579.2Hz
|
||||
`04` | 6257.9Hz | 6023.9Hz
|
||||
`05` | 7046.3Hz | 7044.9Hz
|
||||
`06` | 7919.3Hz | 7917.2Hz
|
||||
`07` | 8363.4Hz | 8397.0Hz
|
||||
`08` | 9419.9Hz | 9446.6Hz
|
||||
`09` | 11186.1Hz | 11233.8Hz
|
||||
`0A` | 12604.0Hz | 12595.5Hz
|
||||
`0B` | 13982.6Hz | 14089.9Hz
|
||||
`0C` | 16884.6Hz | 16965.4Hz
|
||||
`0D` | 21306.8Hz | 21315.5Hz
|
||||
`0E` | 24858.0Hz | 25191.0Hz
|
||||
`0F` | 33143.9Hz | 33252.1Hz
|
||||
"value" is for DefleMask compatability.
|
||||
|
||||
value | tracker | NTSC freq | NTSC pitch | PAL freq | PAL pitch
|
||||
-----:|:-------:|----------:|:----------:|----------:|:----------:
|
||||
`00` | `C-3` | 4181.7Hz | C-8 - 2¢ | 4177.4Hz | C-8 - 4¢
|
||||
`01` | `D-3` | 4709.9Hz | D-8 + 4¢ | 4696.6Hz | D-8 - 1¢
|
||||
`02` | `E-3` | 5264.0Hz | E-8 - 3¢ | 5261.4Hz | E-8 - 4¢
|
||||
`03` | `F-3` | 5593.0Hz | F-8 + 2¢ | 5579.2Hz | F-8 - 3¢
|
||||
`04` | `G-3` | 6258.0Hz | G-8 - 4¢ | 6023.9Hz | G-8 - 70¢
|
||||
`05` | `A-3` | 7046.4Hz | A-8 + 2¢ | 7044.9Hz | A-8 + 1¢
|
||||
`06` | `B-3` | 7919.4Hz | B-8 + 4¢ | 7917.2Hz | B-8 + 3¢
|
||||
`07` | `C-4` | 8363.4Hz | C-9 - 2¢ | 8397.0Hz | C-9 + 5¢
|
||||
`08` | `D-4` | 9419.9Hz | D-9 + 4¢ | 9446.6Hz | D-9 + 9¢
|
||||
`09` | `F-4` | 11186.1Hz | F-9 + 2¢ | 11233.8Hz | F-9 + 9¢
|
||||
`0A` | `G-4` | 12604.0Hz | G-9 + 8¢ | 12595.5Hz | G-9 + 7¢
|
||||
`0B` | `A-4` | 13982.6Hz | A-9 - 12¢ | 14089.9Hz | A-9 + 1¢
|
||||
`0C` | `C-5` | 16884.6Hz | C-10 + 15¢ | 16965.4Hz | C-10 + 23¢
|
||||
`0D` | `E-5` | 21306.8Hz | E-10 + 17¢ | 21315.5Hz | E-10 + 18¢
|
||||
`0E` | `G-5` | 24858.0Hz | G-10 - 16¢ | 25191.0Hz | G-10 + 7¢
|
||||
`0F` | `C-6` | 33143.9Hz | C-11 - 18¢ | 33252.1Hz | C-11 - 12¢
|
||||
|
||||
reference: [NESdev](https://www.nesdev.org/wiki/APU_DMC#Pitch_table)
|
|
@ -13,3 +13,19 @@ the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624
|
|||
- `y` controls whether to select noise or thin pulse.
|
||||
- `0`: thin pulse.
|
||||
- `1`: noise.
|
||||
|
||||
# chip config
|
||||
## SN7 versions
|
||||
SN7 was extremely popular due to low cost. Therefore, it was cloned and copied to no end, often with minor differences between each other. Furnace supports several of these:
|
||||
- SN94624, can only produce tones as low as 100Hz, and is clocked at 447 KHz.
|
||||
- SN76494, which can play notes as low as 13.670 Hz (A -1). It has a different noise feedback and invert masks.
|
||||
- SN76489, identical to SN94624, just without a clock divider
|
||||
- SN76489A, identical to 76494, just with a /8 clock divider
|
||||
- SN76496, literally identical to former. Why is it even here?
|
||||
- SN76496 with a Atari-like short noise. The chip of many legend and rumours, might be a result of inaccurate emulation.
|
||||
- Sega Master System VDP version has a different, characteristic noise LFSR.
|
||||
- Game Gear SN7, identical to the above, but with stereo
|
||||
- NCR8496, different noise invert masks
|
||||
- PSSJ3, literally identical to the former, it just swaps "high" and "low" signals in the output, which results in no audible difference
|
||||
|
||||
TODO: all these checkboxes
|
||||
|
|
10
doc/7-systems/ted.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# MOS Technology TED
|
||||
|
||||
also called 7360/8360, TED stands for Text Editing Device. it's both a video and audio chip of Commodore budget computers, like Plus/4 and 16.
|
||||
|
||||
its audio portion is pretty barren - only 2 channels. one can output square wave and other may be either square or noise.
|
||||
pitch range is limited as well, akin to that of SN76489, and volume control is global.
|
||||
|
||||
# effects
|
||||
|
||||
none so far.
|
|
@ -99,3 +99,9 @@ several variants of this chip were released as well, with more features.
|
|||
- `5Dxx`: **set D2R/SR of operator 2.**
|
||||
- `5Exx`: **set D2R/SR of operator 3.**
|
||||
- `5Fxx`: **set D2R/SR of operator 4.**
|
||||
|
||||
# system modes
|
||||
## extended channel 3
|
||||
in ExtCh mode, channel 3 is split into one column for each of its four operators and feedback are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds.
|
||||
|
||||
all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
like YM2203, but with twice the FM channels, stereo, an ADPCM channel and built-in drums ("rhythm")!
|
||||
|
||||
it was one of the available sound chips for the NEC PC-88VA and PC-98 series of computers.
|
||||
it was one of the available sound chips for the NEC PC-88VA and later models of PC-98 series of computers.
|
||||
|
||||
the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-in drums have been replaced with 6 sample channels.
|
||||
|
||||
|
@ -99,3 +99,9 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-
|
|||
- `5Dxx`: **set D2R/SR of operator 2.**
|
||||
- `5Exx`: **set D2R/SR of operator 3.**
|
||||
- `5Fxx`: **set D2R/SR of operator 4.**
|
||||
|
||||
# system modes
|
||||
## extended channel 3
|
||||
in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds.
|
||||
|
||||
all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
originally an arcade board, but SNK shortly adapted it to a rather expensive video game console with the world's biggest cartridges because some people liked the system so much they wanted a home version of it.
|
||||
|
||||
its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different format ADPCM in a single package!
|
||||
its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and [2 different format ADPCM](https://wiki.neogeodev.org/index.php?title=ADPCM) in a single package!
|
||||
|
||||
# effects
|
||||
|
||||
|
@ -97,3 +97,9 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different
|
|||
- `5Dxx`: **set D2R/SR of operator 2.**
|
||||
- `5Exx`: **set D2R/SR of operator 3.**
|
||||
- `5Fxx`: **set D2R/SR of operator 4.**
|
||||
|
||||
# system modes
|
||||
## extended channel 2
|
||||
in ExtCh mode, channel 2 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds.
|
||||
|
||||
all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone.
|
||||
|
|
|
@ -96,3 +96,9 @@ it is backward compatible with the original chip.
|
|||
- `5Dxx`: **set D2R/SR of operator 2.**
|
||||
- `5Exx`: **set D2R/SR of operator 3.**
|
||||
- `5Fxx`: **set D2R/SR of operator 4.**
|
||||
|
||||
# system modes
|
||||
## extended channel 3
|
||||
in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds.
|
||||
|
||||
all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Yamaha YM2612
|
||||
|
||||
one of two chips that powered the Sega Genesis. it is a six-channel, four-operator FM synthesizer. channel #6 can be turned into 8-bit PCM player, that via software mixing, thanks to Z80 sound CPU, can play more than one channel of straight-shot samples at once. as of Furnace 0.6pre5, Furnace offers DualPCM, which allows 2 channels of software-mixed 8-bit PCM samples at 13750 Hz.
|
||||
one of two chips that powered the Sega Genesis. it is a six-channel, four-operator FM synthesizer. channel #6 can be turned into 8-bit PCM player, that via software mixing, thanks to Z80 sound CPU, can play more than one channel of straight-shot samples at once.
|
||||
as of Furnace 0.6pre5, Furnace offers DualPCM, a Z80 driver that splits channel 6 into two individual PCM channels with variable pitch. using the console's Z80 processor, these are mixed together in software and streamed to channel 6 in PCM mode with a mix rate of 13750 Hz. VGM export requires the "direct stream mode" option to be enabled, and resulting files will be very large.
|
||||
|
||||
# effects
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
rather than having a dedicated sound synthesizer, early ZX Spectrum models had one piezo beeper, controlled by Z80 CPU and ULA chip. its capabilities should be on par with an IBM PC speaker... right?
|
||||
|
||||
not really - very soon talented programmers found out ways to output much more than one square wave channel. a lot of ZX beeper routines do exist, but as of 0.6 Furnace supports only a Follin/SFX-like engine with 6 channels of narrow pulse wave and click drums.
|
||||
not really - very soon talented programmers found out ways to output much more than one square wave channel. a lot of ZX beeper routines do exist, but as of 0.6 Furnace supports two engines:
|
||||
|
||||
- a Follin/SFX-like engine with 6 channels of narrow pulse wave and click drums.
|
||||
- QuadTone: PWM-driven engine with 4ch of pulse wave with freely variable duty cycles and 1-bit PCM drums.
|
||||
|
||||
# effects
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
# advanced
|
||||
|
||||
advanced Furnace features, as listed in the `Window` menu.
|
||||
advanced Furnace features.
|
||||
|
||||
as listed in the "Edit" menu:
|
||||
|
||||
- [find/replace](find-replace.md)
|
||||
|
||||
as listed in the "Window" menu:
|
||||
|
||||
- [mixer](mixer.md)
|
||||
- [grooves](grooves.md)
|
||||
|
@ -10,8 +16,6 @@ advanced Furnace features, as listed in the `Window` menu.
|
|||
- [compatibility flags](compat-flags.md)
|
||||
- [song comments](comments.md)
|
||||
|
||||
<hr>
|
||||
|
||||
- [piano](piano.md)
|
||||
- [oscilloscope](osc.md)
|
||||
- [oscilloscopes (per-channel)](chanosc.md)
|
||||
|
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 161 KiB |
|
@ -1,6 +1,6 @@
|
|||
# oscilloscope (per channel)
|
||||
# oscilloscope (per-channel)
|
||||
|
||||
the "Oscilloscope (per channel)" dialog shows an individual oscilloscope for each channel during playback.
|
||||
the "Oscilloscope (per-channel)" dialog shows an individual oscilloscope for each channel during playback.
|
||||
|
||||
![oscilloscope per-channel configuration view](chanosc.png)
|
||||
|
||||
|
@ -8,8 +8,9 @@ right-clicking within the view will change it to the configuration view shown ab
|
|||
- **Columns**: arranges oscilloscopes into this many columns.
|
||||
- **Size (ms)**: sets what length of audio is visible in each oscilloscope.
|
||||
- **Center waveform**: does its best to latch the waveform to the channel's note frequency and centers the display.
|
||||
- **Amplitude**: scales amplitude for all oscilloscopes.
|
||||
- **Gradient**: see below.
|
||||
- the color selector sets the waveform color. right-clicking on it pops up an option dialog:
|
||||
- the color selector sets the color for all waveforms. right-clicking on it pops up an option dialog:
|
||||
- select between the square selector and the color wheel selector.
|
||||
- **Alpha bar**: adds a transparency selector.
|
||||
- the boxes below that are for selecting colors numerically by red-green-blue-alpha, hue-saturation-value-alpha, and HTML-style RGBA in hex.
|
||||
|
@ -22,6 +23,7 @@ right-clicking within the view will change it to the configuration view shown ab
|
|||
- `%I`: instrument number (decimal)
|
||||
- `%x`: instrument number (hex)
|
||||
- `%s`: chip name
|
||||
- `%p`: chip part number
|
||||
- `%S`: chip ID
|
||||
- `%v`: volume (decimal)
|
||||
- `%V`: volume (percentage)
|
||||
|
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
BIN
doc/8-advanced/find-find.png
Normal file
After Width: | Height: | Size: 73 KiB |
60
doc/8-advanced/find-replace.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# find/replace
|
||||
|
||||
Furnace has a powerful find-and-replace function that can take the repetitive work out of mass editing.
|
||||
|
||||
# find
|
||||
|
||||
![find dialog](find-find.png)
|
||||
|
||||
all data that can be found within a pattern can be searched for here.
|
||||
|
||||
- a find term contains:
|
||||
- **Note**: note.\
|
||||
**Ins**: instrument.\
|
||||
**Volume**: volume.\
|
||||
**Effect**: effect type.\
|
||||
**Value**: effect value. all of these have the following choices for what data will be found:
|
||||
- **ignore**: ignore this.
|
||||
- **equals**: match the given value exactly.
|
||||
- **not equal**: match everything but the given value.
|
||||
- **between**: match anything between and including the given values.
|
||||
- **not between**: match anything outside the given range of values.
|
||||
- **any**: match all values.
|
||||
- **none**: match blanks only.
|
||||
- **-**: remove find term. if only one find term exists, it is cleared.
|
||||
- **Add effect**: adds another Effect and Value to the term, each set representing additional effects columns.
|
||||
- **Remove effect**: removes last Effect and Value from the term.
|
||||
- **+**: adds another find term.
|
||||
- **Search range**: restricts the find to the whole **Song**, the current **Selection**, or the currently viewed **Pattern**.
|
||||
- **Confine to channels**: restricts the find to the selected channels and the channels between them.
|
||||
- **Match effect position**: chooses how the order of effect types and effect values will matter when finding them.
|
||||
- **No**: no attention is paid to what order the effects appear in.
|
||||
- **Lax**: matches effects if they appear in the same order as selected above.
|
||||
- **Strict**: effects may only match in their correponding effects columns.
|
||||
- **Find**: finds everything that matches the terms and displays it in a list.
|
||||
- the **order**, **row**, and **channel** columns are as they say.
|
||||
- the **go** column of buttons will snap the pattern cursor to the location of the find.
|
||||
|
||||
# replace
|
||||
|
||||
![replace dialog](find-replace.png)
|
||||
|
||||
- the replacement term contains:
|
||||
- **Note**: note.\
|
||||
**Ins**: instrument.\
|
||||
**Volume**: volume.\
|
||||
**Effect**: effect type.\
|
||||
**Value**: effect value. all of these have the following choices for how they alter the found data:
|
||||
- **set**: changes found data to this value.
|
||||
- **add**: adds this value to the found data. it may be negative for subtraction. notes are calculated in semitones.
|
||||
- **add (overflow)**: as "add" above, but values will wrap around; for example, adding 13 to `FF` will result in `0C`.
|
||||
- **scale**: multiply value to this percentage; for example, scaling `1A` by `150` results in `27`. not available for "note".
|
||||
- **clear**: erases data.
|
||||
- **Add effect**: adds another Effect and Value to be replaced according to how they were found.
|
||||
- **Remove effect**: removes last Effect and Value.
|
||||
- **Effect replace mode**:
|
||||
- **Replace matches only**: replaces only the effect columns that match.
|
||||
- **Replace matches, then free spaces**: replaces matched effects; if there are effect columns without data, those will be filled in with the additional effect replacements.
|
||||
- **Clear effects**: overwrites effect data with replacement effects.
|
||||
- **Insert in free spaces**: replaces nothing; replacement effects are inserted in free effects columns when available.
|
||||
- **Replace**: finds everything from the "Find" tab and replaces it as directed.
|
BIN
doc/8-advanced/find-replace.png
Normal file
After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 55 KiB |
11
doc/8-advanced/inputlatch.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# input latch
|
||||
|
||||
![input latch menu item](inputlatch.png)
|
||||
|
||||
input latch determines which data are placed along with a note. as in the pattern view, the columns are note (not changeable), instrument, volume, effect type, and effect value.
|
||||
- `&&` fills in the currently selected instrument.
|
||||
- `..` ignores the column.
|
||||
- all columns (except note) can be reset with a right-click.
|
||||
- **Set**: sets latch according to the data found at the cursor.
|
||||
- **Reset**: resets all columns to default (selected instrument, ignore others).
|
||||
- only the first effect type and effect value may be latched.
|
BIN
doc/8-advanced/inputlatch.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
7
doc/8-advanced/opmask.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# operation mask
|
||||
|
||||
![operation mask popup](opmask.png)
|
||||
|
||||
the operation mask toggles which columns will be affected by the listed operations. as in the pattern view, the columns are note, instrument, volume, effect types, and effect values. the effect toggles apply to all effect columns.
|
||||
|
||||
click any area to toggle it. a `---` or `--` means the operation will ignore any data in that column.
|
BIN
doc/8-advanced/opmask.png
Normal file
After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
6
doc/9-guides/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# guides
|
||||
|
||||
here is a small collection of useful tricks and techniques to really make Furnace sing.
|
||||
|
||||
- [using samples with limited playback rates](limited-samples.md)
|
||||
- [choosing emulation cores](emulation-cores.md)
|
36
doc/9-guides/emulation-cores.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# choosing emulation cores
|
||||
|
||||
Furnace achieves the authentic sound of videogame hardware by emulating sound chips accurately as possible, using **emulator cores**. in some cases there are multiple cores to choose from, each with different strengths and weaknesses. here are the major differences between them all.
|
||||
|
||||
- **Arcade/YM2151 core**:
|
||||
- **ymfm**: default. much less CPU usage than Nuked-OPM, but less accurate. recommended for users with last-gen or earlier hardware.
|
||||
- **Nuked-OPM**: much more accurate than ymfm, due to the emulator being based on an image of the die map taken from a real YM2151. very CPU heavy, only recommended for users with recent hardware.
|
||||
|
||||
- **Genesis/YM2612 core**:
|
||||
- **Nuked-OPN2**: default. a little lighter on the CPU than Nuked-OPM.
|
||||
- **ymfm**: same as ymfm above.
|
||||
|
||||
- **SN76489 core**:
|
||||
- **MAME**: default. less accurate than Nuked, but with lower CPU usage. comes from the MAME emulator project.
|
||||
- **Nuked-PSG Mod**: more accurate, but not by that much. this originally started as an emulator for the YM7101 PSG sound generator, but was modified to emulate the SN7 as the MAME core was deemed unsatisfactory by some.
|
||||
|
||||
- **NES core**:
|
||||
- **puNES**: default. it comes from a dedicated NES emulator.
|
||||
- **NSFplay**: higher CPU usage than puNES.
|
||||
|
||||
- **FDS core**:
|
||||
- **puNES**: default. lower CPU usage and far less accurate.
|
||||
- **NSFplay**: higher CPU usage and much more accurate.
|
||||
|
||||
- **SID core**:
|
||||
- **reSID**: default. a high quality emulation core. somewhat CPU heavy.
|
||||
- **reSIDfp**: improved version of reSID. the most accurate choice. _extremely_ CPU heavy.
|
||||
- **dSID**: a lightweight open-source core used in DefleMask. not so accurate but it's very CPU light.
|
||||
|
||||
- **POKEY core**:
|
||||
- **Atari800 (mzpokeysnd)**: does not emulate two-tone mode.
|
||||
- **ASAP (C++ port)**: default. the sound core used in the ASAP player. most accurate option.
|
||||
|
||||
- **OPN/OPNA/OPNB cores**:
|
||||
- **ymfm only**: lower CPU usage, less accurate FM.
|
||||
- **Nuked-OPN2 (FM) + ymfm (SSG/ADPCM)**: default. more accurate FM at the cost of more CPU load.
|
24
doc/9-guides/limited-samples.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# using samples with limited playback rates
|
||||
|
||||
some sample-based chips have a limited number of available sample playback rates. when working with these chips, notes entered in the pattern editor will play back at the closest available rate... which might be perfect, or might be several semitones off. the solution is to prepare samples to work around this.
|
||||
|
||||
for example: using the NES, a `C-4` note in the PCM channel means the associated sample will play back at a rate of 8363Hz. let's say we want to use a slap bass sample recorded at a rate of 22050Hz in which the audible pitch is A-2. let's also say that when we put a `C-4` note in the tracker, we want to hear the bass play at what sounds like C-3, transposed three semitones higher than the recorded pitch.
|
||||
|
||||
here's how to make this example work:
|
||||
|
||||
- load up the sample and open it in the sample editor.
|
||||
- the Note selector will show "F-6"; add the three semitones mentioned above to make it "G#6". the Hz will change to 26217.
|
||||
- use the Resample button. in the pop-up dialog, type in `8363`, then click Resample.
|
||||
- select the instrument from the instrument list, and in the pattern editor, enter a `C-4` in the PCM channel. it should sound like a slap bass playing a C-3 note.
|
||||
|
||||
the NES PCM frequency table shows the sixteen notes can be played. if a `D-4` is entered, the slap bass will be heard at D-3 as desired. what if we want to hear a C#3, though?
|
||||
|
||||
- load up the original sample in a new slot and open it in the sample editor.
|
||||
- the Note selector will show "F-6"; this time we add four semitones to make it "A-6". the Hz will change to 27776.
|
||||
- just like before, use the Resample button. in the little pop-up, type in `8363` (yes, the NES's C-4 rate), then click Resample.
|
||||
- select the instrument from the instrument list and open it in the instrument editor. turn on "Use sample map".
|
||||
- in the leftmost column, find C#5. click in the next column and enter the number of the second sample. in the next column to the right, click the "C#4" and hit the key for C-4 to change it.
|
||||
- in the pattern editor, enter a `C#4`; it should sound like a C#3!
|
||||
- try adding another entry in the sample map so the note D#4 plays the second sample at D-4.
|
||||
|
||||
|
|
@ -10,6 +10,7 @@ this documentation is a work in progress! expect several sections to be incomple
|
|||
6. [samples](6-sample/README.md)
|
||||
7. [list of sound chips](7-systems/README.md)
|
||||
8. [advanced topics](8-advanced/README.md)
|
||||
9. [guides](9-guides/README.md)
|
||||
|
||||
# attribution
|
||||
|
||||
|
|
1
extern/imgui_patched/imgui.h
vendored
|
@ -1236,6 +1236,7 @@ enum ImGuiTableFlags_
|
|||
ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
|
||||
// tildearrow
|
||||
ImGuiTableFlags_NoBordersInFrozenArea = 1 << 28, // Disable vertical borders in frozen area.
|
||||
ImGuiTableFlags_NoScrollWithMouse = 1 << 29, // Disable user scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set.
|
||||
|
||||
// [Internal] Combinations and masks
|
||||
ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,
|
||||
|
|
3
extern/imgui_patched/imgui_tables.cpp
vendored
|
@ -401,6 +401,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
|||
|
||||
// Create scrolling region (without border and zero window padding)
|
||||
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
if (flags & ImGuiTableFlags_NoScrollWithMouse) {
|
||||
child_flags |= ImGuiWindowFlags_NoScrollWithMouse;
|
||||
}
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
|
|
|
@ -8,18 +8,19 @@
|
|||
|
||||
#include "k053260.hpp"
|
||||
|
||||
void k053260_core::tick()
|
||||
void k053260_core::tick(u32 cycle)
|
||||
{
|
||||
m_out[0] = m_out[1] = 0;
|
||||
if (m_ctrl.sound_en())
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
m_voice[i].tick();
|
||||
m_voice[i].tick(cycle);
|
||||
m_out[0] += m_voice[i].out(0);
|
||||
m_out[1] += m_voice[i].out(1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
// dac clock (YM3012 format)
|
||||
u8 dac_clock = m_dac.clock();
|
||||
if (bitfield(++dac_clock, 0, 4) == 0)
|
||||
|
@ -34,62 +35,58 @@ void k053260_core::tick()
|
|||
m_dac.set_state(bitfield(dac_state, 0, 2));
|
||||
}
|
||||
m_dac.set_clock(bitfield(dac_clock, 0, 4));
|
||||
*/
|
||||
}
|
||||
|
||||
void k053260_core::voice_t::tick()
|
||||
void k053260_core::voice_t::tick(u32 cycle)
|
||||
{
|
||||
if (m_enable && m_busy)
|
||||
{
|
||||
bool update = false;
|
||||
// update counter
|
||||
if (bitfield(++m_counter, 0, 12) == 0)
|
||||
m_counter += cycle;
|
||||
if (m_counter >= 0x1000)
|
||||
{
|
||||
if (m_bitpos < 8)
|
||||
{
|
||||
m_bitpos += 8;
|
||||
m_addr = bitfield(m_addr + 1, 0, 21);
|
||||
m_addr = m_reverse ? bitfield(m_addr - 1, 0, 21) : bitfield(m_addr + 1, 0, 21);
|
||||
m_remain--;
|
||||
}
|
||||
if (m_adpcm)
|
||||
{
|
||||
m_bitpos -= 4;
|
||||
update = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bitpos -= 8;
|
||||
}
|
||||
m_counter = bitfield(m_pitch, 0, 12);
|
||||
}
|
||||
m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
|
||||
if (update)
|
||||
{
|
||||
const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
|
||||
if (nibble)
|
||||
{
|
||||
m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3))
|
||||
: (1 << bitfield(nibble - 1, 0, 3));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_remain < 0) // check end flag
|
||||
{
|
||||
if (m_loop)
|
||||
{
|
||||
m_addr = m_start;
|
||||
m_remain = m_length;
|
||||
m_adpcm_buf = 0;
|
||||
m_output = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
|
||||
if (m_adpcm)
|
||||
{
|
||||
m_bitpos -= 4;
|
||||
const u8 nibble = bitfield(m_data, m_reverse ? (~m_bitpos & 4) : (m_bitpos & 4), 4); // get nibble from ROM
|
||||
if (nibble)
|
||||
{
|
||||
m_output += m_host.adpcm_lut(nibble);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bitpos -= 8;
|
||||
}
|
||||
m_counter = (m_counter - 0x1000) + bitfield(m_pitch, 0, 12);
|
||||
}
|
||||
|
||||
// calculate output
|
||||
s32 output = m_adpcm ? m_adpcm_buf : sign_ext<s32>(m_data, 8) * s32(m_volume);
|
||||
s32 output = (m_adpcm ? m_output : sign_ext<s32>(m_data, 8)) * s32(m_volume);
|
||||
// use math for now; actually fomula unknown
|
||||
m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0;
|
||||
m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0;
|
||||
m_out[0] = (output * m_host.pan_lut(m_pan, 0)) >> 7;
|
||||
m_out[1] = (output * m_host.pan_lut(m_pan, 1)) >> 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -172,6 +169,7 @@ void k053260_core::write(u8 address, u8 data)
|
|||
case 0x28: // keyon/off toggle
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
m_voice[i].set_reverse(bitfield(data, 4 + i));
|
||||
if (bitfield(data, i) && (!m_voice[i].enable()))
|
||||
{ // rising edge (keyon)
|
||||
m_voice[i].keyon();
|
||||
|
@ -244,8 +242,9 @@ void k053260_core::voice_t::keyon()
|
|||
m_addr = m_start;
|
||||
m_remain = m_length;
|
||||
m_bitpos = 4;
|
||||
m_adpcm_buf = 0;
|
||||
std::fill(m_out.begin(), m_out.end(), 0);
|
||||
m_data = 0;
|
||||
m_output = 0;
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// key off trigger
|
||||
|
@ -259,15 +258,15 @@ void k053260_core::reset()
|
|||
elem.reset();
|
||||
}
|
||||
|
||||
m_intf.write_int(0);
|
||||
//m_intf.write_int(0);
|
||||
|
||||
std::fill(m_host2snd.begin(), m_host2snd.end(), 0);
|
||||
std::fill(m_snd2host.begin(), m_snd2host.end(), 0);
|
||||
std::fill_n(m_host2snd, 2, 0);
|
||||
std::fill_n(m_snd2host, 2, 0);
|
||||
m_ctrl.reset();
|
||||
m_dac.reset();
|
||||
//m_dac.reset();
|
||||
|
||||
std::fill(m_reg.begin(), m_reg.end(), 0);
|
||||
std::fill(m_out.begin(), m_out.end(), 0);
|
||||
std::fill_n(m_reg, 64, 0);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// reset voice
|
||||
|
@ -278,15 +277,16 @@ void k053260_core::voice_t::reset()
|
|||
m_loop = 0;
|
||||
m_adpcm = 0;
|
||||
m_pitch = 0;
|
||||
m_reverse = 0;
|
||||
m_start = 0;
|
||||
m_length = 0;
|
||||
m_volume = 0;
|
||||
m_pan = -1;
|
||||
m_pan = 4;
|
||||
m_counter = 0;
|
||||
m_addr = 0;
|
||||
m_remain = 0;
|
||||
m_bitpos = 4;
|
||||
m_data = 0;
|
||||
m_adpcm_buf = 0;
|
||||
m_output = 0;
|
||||
m_out[0] = m_out[1] = 0;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class k053260_intf : public vgsound_emu_core
|
|||
|
||||
virtual u8 read_sample(u32 address) { return 0; } // sample fetch
|
||||
|
||||
virtual void write_int(u8 out) {} // timer interrupt
|
||||
//virtual void write_int(u8 out) {} // timer interrupt
|
||||
};
|
||||
|
||||
class k053260_core : public vgsound_emu_core
|
||||
|
@ -33,7 +33,19 @@ class k053260_core : public vgsound_emu_core
|
|||
friend class k053260_intf; // k053260 specific interface
|
||||
|
||||
private:
|
||||
const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction
|
||||
const s32 m_pan_lut[8][2] = {
|
||||
{0x00, 0x00},
|
||||
{0x7f, 0x00},
|
||||
{0x74, 0x34},
|
||||
{0x68, 0x49},
|
||||
{0x5a, 0x5a},
|
||||
{0x49, 0x68},
|
||||
{0x34, 0x74},
|
||||
{0x00, 0x7f}
|
||||
}; // pan LUT
|
||||
|
||||
const s8 m_adpcm_lut[16] =
|
||||
{0, 1, 2, 4, 8, 16, 32, 64, -128, -64, -32, -16, -8, -4, -2, -1}; // ADPCM LUT
|
||||
|
||||
class voice_t : public vgsound_emu_core
|
||||
{
|
||||
|
@ -47,23 +59,24 @@ class k053260_core : public vgsound_emu_core
|
|||
, m_loop(0)
|
||||
, m_adpcm(0)
|
||||
, m_pitch(0)
|
||||
, m_reverse(0)
|
||||
, m_start(0)
|
||||
, m_length(0)
|
||||
, m_volume(0)
|
||||
, m_pan(-1)
|
||||
, m_pan(4)
|
||||
, m_counter(0)
|
||||
, m_addr(0)
|
||||
, m_remain(0)
|
||||
, m_bitpos(4)
|
||||
, m_data(0)
|
||||
, m_adpcm_buf(0)
|
||||
, m_output(0)
|
||||
{
|
||||
m_out.fill(0);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(u32 cycle);
|
||||
|
||||
// accessors
|
||||
void write(u8 address, u8 data);
|
||||
|
@ -79,9 +92,14 @@ class k053260_core : public vgsound_emu_core
|
|||
|
||||
inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; }
|
||||
|
||||
inline void set_reverse(const bool reverse)
|
||||
{
|
||||
m_reverse = reverse ? 1 : 0;
|
||||
}
|
||||
|
||||
inline void length_inc() { m_length = (m_length + 1) & 0xffff; }
|
||||
|
||||
inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; }
|
||||
inline void set_pan(u8 pan) { m_pan = pan & 7; }
|
||||
|
||||
// getters
|
||||
inline bool enable() { return m_enable; }
|
||||
|
@ -102,17 +120,18 @@ class k053260_core : public vgsound_emu_core
|
|||
u16 m_loop : 1; // loop flag
|
||||
u16 m_adpcm : 1; // ADPCM flag
|
||||
u16 m_pitch : 12; // pitch
|
||||
u8 m_reverse : 1; // reverse playback
|
||||
u32 m_start = 0; // start position
|
||||
u16 m_length = 0; // source length
|
||||
u8 m_volume = 0; // master volume
|
||||
int m_pan = -1; // master pan
|
||||
s32 m_pan = 4; // master pan
|
||||
u16 m_counter = 0; // frequency counter
|
||||
u32 m_addr = 0; // current address
|
||||
s32 m_remain = 0; // remain for end sample
|
||||
u8 m_bitpos = 4; // bit position for ADPCM decoding
|
||||
u8 m_data = 0; // current data
|
||||
s8 m_adpcm_buf = 0; // ADPCM buffer
|
||||
std::array<s32, 2> m_out; // current output
|
||||
s8 m_output = 0; // ADPCM buffer
|
||||
s32 m_out[2]; // current output
|
||||
};
|
||||
|
||||
class ctrl_t
|
||||
|
@ -152,6 +171,7 @@ class k053260_core : public vgsound_emu_core
|
|||
u8 m_input_en : 2; // Input enable
|
||||
};
|
||||
|
||||
/*
|
||||
class ym3012_t
|
||||
{
|
||||
public:
|
||||
|
@ -177,7 +197,9 @@ class k053260_core : public vgsound_emu_core
|
|||
std::array<s32, 2> m_in;
|
||||
std::array<s32, 2> m_out;
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
class dac_t
|
||||
{
|
||||
public:
|
||||
|
@ -205,6 +227,7 @@ class k053260_core : public vgsound_emu_core
|
|||
u8 m_clock : 4; // DAC clock (16 clock)
|
||||
u8 m_state : 2; // DAC state (4 state - SAM1, SAM2)
|
||||
};
|
||||
*/
|
||||
|
||||
public:
|
||||
// constructor
|
||||
|
@ -213,13 +236,13 @@ class k053260_core : public vgsound_emu_core
|
|||
, m_voice{*this, *this, *this, *this}
|
||||
, m_intf(intf)
|
||||
, m_ctrl(ctrl_t())
|
||||
, m_ym3012(ym3012_t())
|
||||
, m_dac(dac_t())
|
||||
//, m_ym3012(ym3012_t())
|
||||
//, m_dac(dac_t())
|
||||
{
|
||||
m_host2snd.fill(0);
|
||||
m_snd2host.fill(0);
|
||||
m_reg.fill(0);
|
||||
m_out.fill(0);
|
||||
std::fill_n(m_host2snd, 2, 0);
|
||||
std::fill_n(m_snd2host, 2, 0);
|
||||
std::fill_n(m_reg, 64, 0);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// communications
|
||||
|
@ -233,7 +256,7 @@ class k053260_core : public vgsound_emu_core
|
|||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(u32 cycle);
|
||||
|
||||
// getters for debug, trackers, etc
|
||||
inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels
|
||||
|
@ -245,20 +268,25 @@ class k053260_core : public vgsound_emu_core
|
|||
return (voice < 4) ? m_voice[voice].out(ch & 1) : 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline s32 pan_lut(const u8 pan, const u8 out) { return m_pan_lut[pan][out]; }
|
||||
|
||||
inline s32 adpcm_lut(const u8 nibble) { return m_adpcm_lut[nibble]; }
|
||||
|
||||
private:
|
||||
std::array<voice_t, 4> m_voice;
|
||||
voice_t m_voice[4];
|
||||
k053260_intf &m_intf; // common memory interface
|
||||
|
||||
std::array<u8, 2> m_host2snd;
|
||||
std::array<u8, 2> m_snd2host;
|
||||
u8 m_host2snd[2];
|
||||
u8 m_snd2host[2];
|
||||
|
||||
ctrl_t m_ctrl; // chip control
|
||||
|
||||
ym3012_t m_ym3012; // YM3012 output
|
||||
dac_t m_dac; // YM3012 interface
|
||||
//ym3012_t m_ym3012; // YM3012 output
|
||||
//dac_t m_dac; // YM3012 interface
|
||||
|
||||
std::array<u8, 64> m_reg; // register pool
|
||||
std::array<s32, 2> m_out; // stereo output
|
||||
u8 m_reg[64]; // register pool
|
||||
s32 m_out[2]; // stereo output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,110 +34,15 @@ the format versions are:
|
|||
|
||||
- 162: Furnace 0.6pre7
|
||||
- 161: Furnace 0.6pre6
|
||||
- 160: Furnace dev160
|
||||
- 159: Furnace dev159
|
||||
- 158: Furnace 0.6pre5
|
||||
- 157: Furnace dev157
|
||||
- 156: Furnace dev156
|
||||
- 155: Furnace dev155
|
||||
- 154: Furnace dev154
|
||||
- 153: Furnace dev153
|
||||
- 152: Furnace dev152
|
||||
- 151: Furnace dev151
|
||||
- 150: Furnace dev150
|
||||
- 149: Furnace dev149
|
||||
- 148: Furnace dev148
|
||||
- 147: Furnace dev147
|
||||
- 146: Furnace Pro (joke version)
|
||||
- 145: Furnace dev145
|
||||
- 144: Furnace dev144
|
||||
- 143: Furnace 0.6pre4
|
||||
- 142: Furnace dev142
|
||||
- 141: Furnace Tournament Edition (for intro tune contest)
|
||||
- 140: Furnace dev140
|
||||
- 139: Furnace dev139
|
||||
- 138: Furnace dev138
|
||||
- 137: Furnace dev137
|
||||
- 136: Furnace dev136
|
||||
- 135: Furnace dev135
|
||||
- 134: Furnace dev134
|
||||
- 133: Furnace 0.6pre3
|
||||
- 132: Furnace 0.6pre2
|
||||
- 131: Furnace dev131
|
||||
- 130: Furnace dev130
|
||||
- 129: Furnace dev129
|
||||
- 128: Furnace dev128
|
||||
- 127: Furnace dev127
|
||||
- 126: Furnace dev126
|
||||
- 125: Furnace dev125
|
||||
- 124: Furnace dev124
|
||||
- 123: Furnace dev123
|
||||
- 122: Furnace dev122
|
||||
- 121: Furnace dev121
|
||||
- 120: Furnace dev120
|
||||
- 119: Furnace dev119
|
||||
- 118: Furnace dev118
|
||||
- 117: Furnace dev117
|
||||
- 116: Furnace 0.6pre1.5
|
||||
- 115: Furnace dev115
|
||||
- 114: Furnace dev114
|
||||
- 113: Furnace dev113
|
||||
- 112: Furnace dev112
|
||||
- 111: Furnace dev111
|
||||
- 110: Furnace dev110
|
||||
- 109: Furnace dev109
|
||||
- 108: Furnace dev108
|
||||
- 107: Furnace dev107
|
||||
- 106: Furnace dev106
|
||||
- 105: Furnace dev105
|
||||
- 104: Furnace dev104
|
||||
- 103: Furnace dev103
|
||||
- 102: Furnace 0.6pre1 (dev102)
|
||||
- 101: Furnace 0.6pre1 (dev101)
|
||||
- 100: Furnace 0.6pre1
|
||||
- 99: Furnace dev99
|
||||
- 98: Furnace dev98
|
||||
- 97: Furnace dev97
|
||||
- 96: Furnace dev96
|
||||
- 95: Furnace dev95
|
||||
- 94: Furnace dev94
|
||||
- 93: Furnace dev93
|
||||
- 92: Furnace dev92
|
||||
- 91: Furnace dev91
|
||||
- 90: Furnace dev90
|
||||
- 89: Furnace dev89
|
||||
- 88: Furnace dev88
|
||||
- 87: Furnace dev87
|
||||
- 86: Furnace dev86
|
||||
- 85: Furnace dev85
|
||||
- 84: Furnace dev84
|
||||
- 83: Furnace dev83
|
||||
- 82: Furnace dev82
|
||||
- 81: Furnace dev81
|
||||
- 80: Furnace dev80
|
||||
- 79: Furnace dev79
|
||||
- 78: Furnace dev78
|
||||
- 77: Furnace dev77
|
||||
- 76: Furnace dev76
|
||||
- 75: Furnace dev75/April Fools' 0.6pre0
|
||||
- 74: Furnace dev74
|
||||
- 73: Furnace dev73
|
||||
- 72: Furnace dev72
|
||||
- 71: Furnace dev71
|
||||
- 70: Furnace dev70
|
||||
- 69: Furnace dev69
|
||||
- 68: Furnace dev68
|
||||
- 67: Furnace dev67
|
||||
- 66: Furnace dev66
|
||||
- 65: Furnace dev65
|
||||
- 64: Furnace dev64
|
||||
- 63: Furnace dev63
|
||||
- 62: Furnace dev62
|
||||
- 61: Furnace dev61
|
||||
- 60: Furnace dev60
|
||||
- 59: Furnace dev59
|
||||
- 58: Furnace dev58
|
||||
- 57: Furnace dev57
|
||||
|
||||
- 54: Furnace 0.5.8
|
||||
- 53: Furnace 0.5.7
|
||||
|
@ -176,6 +81,8 @@ the format versions are:
|
|||
- 13: Furnace 0.2.1
|
||||
- 12: Furnace 0.2
|
||||
|
||||
versions that do not appear in this list are `dev???` ones.
|
||||
|
||||
# header
|
||||
|
||||
the header is 32 bytes long.
|
||||
|
@ -311,6 +218,7 @@ size | description
|
|||
| - 0xca: ZX Spectrum (beeper, QuadTone engine) - 5 channels
|
||||
| - 0xcb: Casio PV-1000 - 3 channels
|
||||
| - 0xcc: K053260 - 4 channels
|
||||
| - 0xcd: TED - 2 channels
|
||||
| - 0xde: YM2610B extended - 19 channels
|
||||
| - 0xe0: QSound - 19 channels
|
||||
| - 0xfc: Pong - 1 channel
|
||||
|
|
|
@ -117,6 +117,8 @@ the following instrument types are available:
|
|||
- 47: Pokémon Mini/QuadTone
|
||||
- 48: SM8521
|
||||
- 49: PV-1000
|
||||
- 50: K053260
|
||||
- 52: TED
|
||||
|
||||
the following feature codes are recognized:
|
||||
|
||||
|
@ -468,6 +470,12 @@ size | description
|
|||
1 | wave pos
|
||||
1 | wave len
|
||||
1 | wave mode
|
||||
| **extra info** (>=164)
|
||||
1 | enable per channel wave pos/len
|
||||
8 | per channel wave pos
|
||||
| - only read if enabled.
|
||||
8 | per channel wave len
|
||||
| - only read if enabled.
|
||||
```
|
||||
|
||||
# FDS/Virtual Boy data (FD)
|
||||
|
|
|
@ -237,56 +237,60 @@ bool DivConfig::loadFromBase64(const char* buf) {
|
|||
}
|
||||
|
||||
bool DivConfig::getBool(String key, bool fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
if (val=="true") {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
if (val->second=="true") {
|
||||
return true;
|
||||
} else if (val=="false") {
|
||||
} else if (val->second=="false") {
|
||||
return false;
|
||||
}
|
||||
} catch (std::out_of_range& e) {
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
int DivConfig::getInt(String key, int fallback) const {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
int ret=std::stoi(val);
|
||||
int ret=std::stoi(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
float DivConfig::getFloat(String key, float fallback) const {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
float ret=std::stof(val);
|
||||
float ret=std::stof(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
double DivConfig::getDouble(String key, double fallback) const {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
double ret=std::stod(val);
|
||||
double ret=std::stod(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
String DivConfig::getString(String key, String fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
return val;
|
||||
} catch (std::out_of_range& e) {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
return val->second;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
@ -294,10 +298,10 @@ String DivConfig::getString(String key, String fallback) const {
|
|||
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
|
||||
String next;
|
||||
std::vector<int> ret;
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
|
||||
for (char i: val) {
|
||||
for (char i: val->second) {
|
||||
if (i==',') {
|
||||
int num=std::stoi(next);
|
||||
ret.push_back(num);
|
||||
|
@ -315,16 +319,13 @@ std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fa
|
|||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool DivConfig::has(String key) const {
|
||||
try {
|
||||
String test=conf.at(key);
|
||||
} catch (std::out_of_range& e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
auto val=conf.find(key);
|
||||
return (val!=conf.cend());
|
||||
}
|
||||
|
||||
void DivConfig::set(String key, bool value) {
|
||||
|
|
|
@ -173,16 +173,16 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_N163_WAVE_POSITION,
|
||||
DIV_CMD_N163_WAVE_LENGTH,
|
||||
DIV_CMD_N163_WAVE_MODE,
|
||||
DIV_CMD_N163_WAVE_LOAD,
|
||||
DIV_CMD_N163_WAVE_UNUSED1,
|
||||
DIV_CMD_N163_WAVE_UNUSED2,
|
||||
DIV_CMD_N163_WAVE_LOADPOS,
|
||||
DIV_CMD_N163_WAVE_LOADLEN,
|
||||
DIV_CMD_N163_WAVE_LOADMODE,
|
||||
DIV_CMD_N163_WAVE_UNUSED3,
|
||||
DIV_CMD_N163_CHANNEL_LIMIT,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOAD,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,
|
||||
DIV_CMD_N163_UNUSED4,
|
||||
DIV_CMD_N163_UNUSED5,
|
||||
|
||||
DIV_CMD_SU_SWEEP_PERIOD_LOW, // (which, val)
|
||||
DIV_CMD_SU_SWEEP_PERIOD_HIGH, // (which, val)
|
||||
|
@ -295,6 +295,8 @@ struct DivRegWrite {
|
|||
*/
|
||||
unsigned int addr;
|
||||
unsigned int val;
|
||||
DivRegWrite():
|
||||
addr(0), val(0) {}
|
||||
DivRegWrite(unsigned int a, unsigned int v):
|
||||
addr(a), val(v) {}
|
||||
};
|
||||
|
|
|
@ -78,6 +78,8 @@
|
|||
#include "platform/ga20.h"
|
||||
#include "platform/sm8521.h"
|
||||
#include "platform/pv1000.h"
|
||||
#include "platform/k053260.h"
|
||||
#include "platform/ted.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
|
@ -503,6 +505,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_PV1000:
|
||||
dispatch=new DivPlatformPV1000;
|
||||
break;
|
||||
case DIV_SYSTEM_K053260:
|
||||
dispatch=new DivPlatformK053260;
|
||||
break;
|
||||
case DIV_SYSTEM_TED:
|
||||
dispatch=new DivPlatformTED;
|
||||
break;
|
||||
case DIV_SYSTEM_PCM_DAC:
|
||||
dispatch=new DivPlatformPCMDAC;
|
||||
break;
|
||||
|
|
|
@ -2598,6 +2598,8 @@ void DivEngine::stepOne(int row) {
|
|||
}
|
||||
stepPlay=2;
|
||||
ticks=1;
|
||||
prevOrder=curOrder;
|
||||
prevRow=curRow;
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
|
@ -4320,9 +4322,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
|||
|
||||
void DivEngine::autoNoteOff(int ch, int note, int vol) {
|
||||
if (!playing) {
|
||||
reset();
|
||||
freelance=true;
|
||||
playing=true;
|
||||
return;
|
||||
}
|
||||
//if (ch<0 || ch>=chans) return;
|
||||
for (int i=0; i<chans; i++) {
|
||||
|
@ -4335,9 +4335,7 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) {
|
|||
|
||||
void DivEngine::autoNoteOffAll() {
|
||||
if (!playing) {
|
||||
reset();
|
||||
freelance=true;
|
||||
playing=true;
|
||||
return;
|
||||
}
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (chan[i].midiNote!=-1) {
|
||||
|
|
|
@ -56,17 +56,14 @@
|
|||
|
||||
#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev163"
|
||||
#define DIV_ENGINE_VERSION 163
|
||||
#define DIV_VERSION "dev165"
|
||||
#define DIV_ENGINE_VERSION 165
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
#define DIV_VERSION_S3M 0xff03
|
||||
#define DIV_VERSION_FTM 0xff04
|
||||
|
||||
// "Namco C163"
|
||||
#define DIV_C163_DEFAULT_NAME "Namco 163"
|
||||
|
||||
enum DivStatusView {
|
||||
DIV_STATUS_NOTHING=0,
|
||||
DIV_STATUS_PATTERN,
|
||||
|
@ -465,7 +462,6 @@ class DivEngine {
|
|||
int reversePitchTable[4096];
|
||||
int pitchTable[4096];
|
||||
short effectSlotMap[4096];
|
||||
char c163NameCS[1024];
|
||||
int midiBaseChan;
|
||||
bool midiPoly;
|
||||
size_t midiAgeCounter;
|
||||
|
|
|
@ -2941,6 +2941,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// Namco 163 pitch compensation compat
|
||||
if (ds.version<165) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_N163) {
|
||||
ds.systemFlags[i].set("lenCompensate",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
140
src/engine/fixedQueue.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _FIXED_QUEUE_H
|
||||
#define _FIXED_QUEUE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../ta-log.h"
|
||||
|
||||
template<typename T, size_t items> struct FixedQueue {
|
||||
size_t readPos, writePos;
|
||||
T data[items];
|
||||
|
||||
T& front();
|
||||
T& back();
|
||||
bool pop();
|
||||
bool push(const T& item);
|
||||
|
||||
bool pop_front();
|
||||
bool pop_back();
|
||||
bool push_front(const T& item);
|
||||
bool push_back(const T& item);
|
||||
void clear();
|
||||
bool empty();
|
||||
size_t size();
|
||||
FixedQueue():
|
||||
readPos(0),
|
||||
writePos(0) {}
|
||||
};
|
||||
|
||||
template <typename T, size_t items> T& FixedQueue<T,items>::front() {
|
||||
return data[readPos];
|
||||
}
|
||||
|
||||
template <typename T, size_t items> T& FixedQueue<T,items>::back() {
|
||||
if (writePos==0) return data[items-1];
|
||||
return data[writePos-1];
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::pop() {
|
||||
if (readPos==writePos) return false;
|
||||
if (++readPos>=items) readPos=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::push(const T& item) {
|
||||
if (writePos==(readPos-1)) {
|
||||
logW("queue overflow!");
|
||||
return false;
|
||||
}
|
||||
if (writePos==items-1 && readPos==0) {
|
||||
logW("queue overflow!");
|
||||
return false;
|
||||
}
|
||||
data[writePos]=item;
|
||||
if (++writePos>=items) writePos=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::pop_front() {
|
||||
if (readPos==writePos) return false;
|
||||
if (++readPos>=items) readPos=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::push_back(const T& item) {
|
||||
if (writePos==(readPos-1)) {
|
||||
logW("queue overflow!");
|
||||
return false;
|
||||
}
|
||||
if (writePos==items-1 && readPos==0) {
|
||||
logW("queue overflow!");
|
||||
return false;
|
||||
}
|
||||
data[writePos]=item;
|
||||
if (++writePos>=items) writePos=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::pop_back() {
|
||||
if (readPos==writePos) return false;
|
||||
if (writePos>0) {
|
||||
writePos--;
|
||||
} else {
|
||||
writePos=items-1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::push_front(const T& item) {
|
||||
if (readPos==(writePos+1)) {
|
||||
logW("stack overflow!");
|
||||
return false;
|
||||
}
|
||||
if (readPos==0 && writePos==items-1) {
|
||||
logW("stack overflow!");
|
||||
return false;
|
||||
}
|
||||
if (readPos>0) {
|
||||
readPos--;
|
||||
} else {
|
||||
readPos=items-1;
|
||||
}
|
||||
data[readPos]=item;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> void FixedQueue<T,items>::clear() {
|
||||
readPos=0;
|
||||
writePos=0;
|
||||
}
|
||||
|
||||
template <typename T, size_t items> bool FixedQueue<T,items>::empty() {
|
||||
return (readPos==writePos);
|
||||
}
|
||||
|
||||
template <typename T, size_t items> size_t FixedQueue<T,items>::size() {
|
||||
if (readPos>writePos) {
|
||||
return items+writePos-readPos;
|
||||
}
|
||||
return writePos-readPos;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -134,7 +134,24 @@ bool DivInstrumentN163::operator==(const DivInstrumentN163& other) {
|
|||
_C(wave) &&
|
||||
_C(wavePos) &&
|
||||
_C(waveLen) &&
|
||||
_C(waveMode)
|
||||
_C(waveMode) &&
|
||||
_C(perChanPos) &&
|
||||
_C(wavePosCh[0]) &&
|
||||
_C(wavePosCh[1]) &&
|
||||
_C(wavePosCh[2]) &&
|
||||
_C(wavePosCh[3]) &&
|
||||
_C(wavePosCh[4]) &&
|
||||
_C(wavePosCh[5]) &&
|
||||
_C(wavePosCh[6]) &&
|
||||
_C(wavePosCh[7]) &&
|
||||
_C(waveLenCh[0]) &&
|
||||
_C(waveLenCh[1]) &&
|
||||
_C(waveLenCh[2]) &&
|
||||
_C(waveLenCh[3]) &&
|
||||
_C(waveLenCh[4]) &&
|
||||
_C(waveLenCh[5]) &&
|
||||
_C(waveLenCh[6]) &&
|
||||
_C(waveLenCh[7])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -519,6 +536,17 @@ void DivInstrument::writeFeatureN1(SafeWriter* w) {
|
|||
w->writeC(n163.waveLen);
|
||||
w->writeC(n163.waveMode);
|
||||
|
||||
w->writeC(n163.perChanPos);
|
||||
|
||||
if (n163.perChanPos) {
|
||||
for (int i=0; i<8; i++) {
|
||||
w->writeC(n163.wavePosCh[i]);
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
w->writeC(n163.waveLenCh[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
|
@ -929,6 +957,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
|
|||
break;
|
||||
case DIV_INS_PV1000:
|
||||
break;
|
||||
case DIV_INS_K053260:
|
||||
featureSM=true;
|
||||
featureSL=true;
|
||||
break;
|
||||
case DIV_INS_TED:
|
||||
break;
|
||||
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
|
@ -2276,6 +2310,18 @@ void DivInstrument::readFeatureN1(SafeReader& reader, short version) {
|
|||
n163.waveLen=(unsigned char)reader.readC();
|
||||
n163.waveMode=(unsigned char)reader.readC();
|
||||
|
||||
if (version>=164) {
|
||||
n163.perChanPos=reader.readC();
|
||||
if (n163.perChanPos) {
|
||||
for (int i=0; i<8; i++) {
|
||||
n163.wavePosCh[i]=(unsigned char)reader.readC();
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
n163.waveLenCh[i]=(unsigned char)reader.readC();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,9 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_POKEMINI=47,
|
||||
DIV_INS_SM8521=48,
|
||||
DIV_INS_PV1000=49,
|
||||
DIV_INS_K053260=50,
|
||||
// DIV_INS_YMF292=51,
|
||||
DIV_INS_TED=52,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
@ -445,6 +448,9 @@ struct DivInstrumentX1_010 {
|
|||
struct DivInstrumentN163 {
|
||||
int wave, wavePos, waveLen;
|
||||
unsigned char waveMode;
|
||||
bool perChanPos;
|
||||
int wavePosCh[8];
|
||||
int waveLenCh[8];
|
||||
|
||||
bool operator==(const DivInstrumentN163& other);
|
||||
bool operator!=(const DivInstrumentN163& other) {
|
||||
|
@ -455,7 +461,13 @@ struct DivInstrumentN163 {
|
|||
wave(-1),
|
||||
wavePos(0),
|
||||
waveLen(32),
|
||||
waveMode(3) {}
|
||||
waveMode(3),
|
||||
perChanPos(false) {
|
||||
for (int i=0; i<8; i++) {
|
||||
wavePosCh[i]=(i&3)<<5;
|
||||
waveLenCh[i]=32;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentFDS {
|
||||
|
|
|
@ -809,6 +809,8 @@ void DivPlatformAmiga::setFlags(const DivConfig& flags) {
|
|||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
|
||||
rate=chipClock/AMIGA_DIVIDER;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#define _AMIGA_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformAmiga: public DivDispatch {
|
||||
|
|
|
@ -878,7 +878,7 @@ void DivPlatformArcade::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformArcade::reset() {
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
writes.clear();
|
||||
memset(regPool,0,256);
|
||||
if (useYMFM) {
|
||||
fm_ymfm->reset();
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#ifndef _ARCADE_H
|
||||
#define _ARCADE_H
|
||||
#include "fmshared_OPM.h"
|
||||
#include <queue>
|
||||
#include "../../../extern/opm/opm.h"
|
||||
#include "sound/ymfm/ymfm_opm.h"
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(regRemap(a),v)); if (dumpWrites) {addWrite(regRemap(a),v);} }
|
||||
|
||||
#define CHIP_DIVIDER (extMode?extDiv:((sunsoft||clockSel)?16:8))
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef _AY_H
|
||||
#define _AY_H
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../fixedQueue.h"
|
||||
#include "sound/ay8910.h"
|
||||
|
||||
class DivPlatformAY8910: public DivDispatch {
|
||||
|
@ -89,9 +89,10 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
FixedQueue<QueuedWrite,128> writes;
|
||||
ay8910_device* ay;
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
unsigned char regPool[16];
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER (clockSel?8:4)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef _AY8930_H
|
||||
#define _AY8930_H
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../fixedQueue.h"
|
||||
#include "sound/ay8910.h"
|
||||
|
||||
class DivPlatformAY8930: public DivDispatch {
|
||||
|
@ -99,9 +99,10 @@ class DivPlatformAY8930: public DivDispatch {
|
|||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
FixedQueue<QueuedWrite,128> writes;
|
||||
ay8930_device* ay;
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
unsigned char regPool[32];
|
||||
|
|