Merge branch 'master' of https://github.com/tildearrow/furnace into command-palette

This commit is contained in:
YohananDiamond 2023-07-25 00:02:08 -03:00
commit f3902e151e
202 changed files with 3884 additions and 1226 deletions

View file

@ -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

View file

@ -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:

View file

@ -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

Binary file not shown.

Binary file not shown.

BIN
demos/ay8910/vibe_zone.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/msx/21492413.fur Normal file

Binary file not shown.

Binary file not shown.

View 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.

View file

@ -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

View file

@ -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)

View file

@ -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.

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -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**

View file

@ -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.

View file

@ -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.

View file

@ -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
| | **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**
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**
the interpretation of duty, wave and extra macros depends on chip/instrument type:

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View file

@ -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.

View file

@ -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 envelopes 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.

View file

@ -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
View 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
View 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.

View file

@ -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

View file

@ -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
View 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!**

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
doc/5-wave/wave-editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -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.

View file

@ -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)

View file

@ -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.

View file

@ -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.

View file

@ -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.**

View file

@ -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
View 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.

View file

@ -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 |

View file

@ -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.

View file

@ -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.**

View file

@ -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)

View file

@ -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
View 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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 161 KiB

View file

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

7
doc/8-advanced/opmask.md Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

6
doc/9-guides/README.md Normal file
View 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)

View 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.

View 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.

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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_remain < 0) // check end flag
{
if (m_loop)
{
m_addr = m_start;
m_remain = m_length;
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;
update = true;
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 = 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));
}
m_counter = (m_counter - 0x1000) + bitfield(m_pitch, 0, 12);
}
if (m_remain < 0) // check end flag
{
if (m_loop)
{
m_addr = m_start;
m_remain = m_length;
m_adpcm_buf = 0;
}
else
{
m_busy = false;
}
}
// 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,34 +258,35 @@ 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
void k053260_core::voice_t::reset()
{
m_enable = 0;
m_busy = 0;
m_loop = 0;
m_adpcm = 0;
m_pitch = 0;
m_start = 0;
m_length = 0;
m_volume = 0;
m_pan = -1;
m_counter = 0;
m_addr = 0;
m_remain = 0;
m_bitpos = 4;
m_data = 0;
m_adpcm_buf = 0;
m_enable = 0;
m_busy = 0;
m_loop = 0;
m_adpcm = 0;
m_pitch = 0;
m_reverse = 0;
m_start = 0;
m_length = 0;
m_volume = 0;
m_pan = 4;
m_counter = 0;
m_addr = 0;
m_remain = 0;
m_bitpos = 4;
m_data = 0;
m_output = 0;
m_out[0] = m_out[1] = 0;
}

View file

@ -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; }
@ -97,22 +115,23 @@ class k053260_core : public vgsound_emu_core
private:
// registers
k053260_core &m_host;
u16 m_enable : 1; // enable flag
u16 m_busy : 1; // busy status
u16 m_loop : 1; // loop flag
u16 m_adpcm : 1; // ADPCM flag
u16 m_pitch : 12; // pitch
u32 m_start = 0; // start position
u16 m_length = 0; // source length
u8 m_volume = 0; // master volume
int m_pan = -1; // 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
u16 m_enable : 1; // enable flag
u16 m_busy : 1; // busy status
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
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_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

View file

@ -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

View file

@ -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)

View file

@ -237,56 +237,60 @@ bool DivConfig::loadFromBase64(const char* buf) {
}
bool DivConfig::getBool(String key, bool fallback) const {
try {
String val=conf.at(key);
if (val=="true") {
auto val=conf.find(key);
if (val!=conf.cend()) {
if (val->second=="true") {
return true;
} else if (val=="false") {
} else if (val->second=="false") {
return false;
}
} catch (std::out_of_range& e) {
}
return fallback;
}
int DivConfig::getInt(String key, int fallback) const {
try {
String val=conf.at(key);
int ret=std::stoi(val);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
auto val=conf.find(key);
if (val!=conf.cend()) {
try {
int ret=std::stoi(val->second);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
}
}
return fallback;
}
float DivConfig::getFloat(String key, float fallback) const {
try {
String val=conf.at(key);
float ret=std::stof(val);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
auto val=conf.find(key);
if (val!=conf.cend()) {
try {
float ret=std::stof(val->second);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
}
}
return fallback;
}
double DivConfig::getDouble(String key, double fallback) const {
try {
String val=conf.at(key);
double ret=std::stod(val);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
auto val=conf.find(key);
if (val!=conf.cend()) {
try {
double ret=std::stod(val->second);
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
}
}
return fallback;
}
String DivConfig::getString(String key, String fallback) const {
try {
String val=conf.at(key);
return val;
} catch (std::out_of_range& e) {
auto val=conf.find(key);
if (val!=conf.cend()) {
return val->second;
}
return fallback;
}
@ -294,37 +298,34 @@ String DivConfig::getString(String key, String fallback) const {
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
String next;
std::vector<int> ret;
try {
String val=conf.at(key);
for (char i: val) {
if (i==',') {
auto val=conf.find(key);
if (val!=conf.cend()) {
try {
for (char i: val->second) {
if (i==',') {
int num=std::stoi(next);
ret.push_back(num);
next="";
} else {
next+=i;
}
}
if (!next.empty()) {
int num=std::stoi(next);
ret.push_back(num);
next="";
} else {
next+=i;
}
}
if (!next.empty()) {
int num=std::stoi(next);
ret.push_back(num);
}
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
}
}
return fallback;
}
bool DivConfig::has(String key) const {
try {
String test=conf.at(key);
} catch (std::out_of_range& e) {
return false;
}
return true;
auto val=conf.find(key);
return (val!=conf.cend());
}
void DivConfig::set(String key, bool value) {

View file

@ -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) {}
};

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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
View 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

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;

View file

@ -21,7 +21,6 @@
#define _AMIGA_H
#include "../dispatch.h"
#include <queue>
#include "../waveSynth.h"
class DivPlatformAmiga: public DivDispatch {

View file

@ -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();

View file

@ -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"

View file

@ -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))

View file

@ -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];

View file

@ -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)

View file

@ -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];

Some files were not shown because too many files have changed in this diff Show more