Merge branch 'master' of https://github.com/tildearrow/furnace into x1_010_bank

This commit is contained in:
cam900 2023-07-26 19:28:39 +09:00
commit a3d54ca933
97 changed files with 2536 additions and 811 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
@ -589,6 +598,7 @@ 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 +706,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

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

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

@ -2,6 +2,8 @@
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
- **new...**: create a new song.
@ -38,12 +40,12 @@ the menu bar allows you to select from five menus: file, edit, settings, window
- **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**: toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md)
- **input latch**: determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md)
- _**operation mask**:_ toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md)
- _**input latch**:_ determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md)
- **note/octave up/down**: transposes notes in the current selection.
@ -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**: shows [the Find/Replace window](../8-advanced/find-replace.md).
- _**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.
@ -213,12 +215,12 @@ all these menu items show or hide their associated windows.
- [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)
- _[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)
- [piano](piano.md)
@ -246,5 +248,5 @@ at the end of the menu bar, more information may be shown:
- 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.
> 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

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

@ -4,41 +4,85 @@ the pattern view allows you to edit the song.
![pattern view](pattern.png)
a pattern consists of columns ("channels") and rows.
a pattern consists of columns ("channels") and numbered rows.
each column has several subcolumns in this order:
1. note
2. instrument
3. volume
4. effect and effect value (several)
4. effects, split into effect type and effect value
all columns are represented in hexadecimal, except for the note column.
# managing channels
row highlights show beats and measures, and are configured in the [the Speed window](../2-interface/song-info.md).
you may mute channels, toggle solo mode, collapse channels or even hide them.
clicking on a channel name mutes that channel.
double-clicking or right-clicking it enables solo mode, in where only that channel will be audible.
clicking the `++` at the top left corner of the pattern view displays additional buttons for channel configuration:
![channel bar](channelbar.png)
to rename and/or hide channels, see the Channels window (window > channels).
![channels](channels.png)
# cursor and selection
## cursor and selection
you may change the cursor position by clicking anywhere on the pattern.
to select, press and hold the left mouse button. then drag the mouse and release the button to finish selection.
to select an area, press and hold the left mouse button. then drag the mouse and release the button to finish selection.
# keyboard layout
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.
## shortcuts
## channel bar
using the channel bar, you may adjust several aspects of the channel display.
![channel bar](channelbar.png)
clicking on a channel name mutes that channel.
double-clicking or right-clicking it enables solo mode, in which only that channel will be audible.
clicking the `++` at the top left corner of the pattern view cycles through three channel bar view modes:
- **Compact**: shows only channel names.
- **Expanded**: as shown above. adds buttons:
- **-**: collapse visible columns. changes to **+** when columns are hidden; click to expand them.
- **<**: disables the last effect column and hides it. effects are not deleted...
- **>**: adds an effects column. if one previously existed, its contents will be preserved.
- **Pattern names**: adds a text field with which one can name the current pattern. pattern names are also visible when hovering over a pattern in the order list.
right-clicking the `++` toggles the visualizer, which is active only during playback.
to rename and/or hide channels, open [the Channels window](../8-advanced/channels.md) via the window menu.
# input
## note input
![keyboard](keyboard.png)
- pressing any of the respective keys will insert a note at the cursor's location, then advance to the next row (or otherwise according to the Edit Step.)
- **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.
- **toggle edit** enables and disables editing. when editing is enabled, the cursor's row will be shaded red.
## instrument/volume input
type any hexadecimal number (0-9 and A-F). the cursor will move by the Edit Step when a suitable value is entered.
## effect input
works like the instrument/volume input.
each effect column has two subcolumns: effect and effect value.
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.
here's [a list of effect types](effects.md).
# keyboard shortcuts
these are the default key functions. all keys are configurable in the Keyboard tab of the Settings window.
key | action
------------|-----------------------------------------------------------------
@ -68,27 +112,3 @@ Ctrl-F2 | transpose selection (+1 semitone)
Ctrl-F3 | transpose selection (-1 octave)
Ctrl-F4 | transpose selection (+1 octave)
Space | toggle note input (edit)
## note input
![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 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.
## instrument/volume input
type any hexadecimal number (0-9 and A-F). the cursor will move by the Edit Step when a suitable value is entered.
## effect input
works like the instrument/volume input.
each effect column has two subcolumns: effect and effect value.
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).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

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.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

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

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

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

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

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

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

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

@ -25,4 +25,271 @@ Furnace isn't complete without this one...
- `C`: low square
- `D`: low square
- `E`: low pure buzzy
- `F`: low reedy
- `F`: low reedy
# tables
pitch number can be used for absolute notes in arpeggio macros.
## shape 1
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 2096.0 | C-7 | +2 | 2080.0 | C-7 | -1
| 1 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -1
| 2 | 698.7 | F-5 | 0.0| 693.3 | F-5 | -1
| 3 | 524.0 | C-5 | +2 | 520.0 | C-5 | -1
| 4 | 419.2 | G#4 | +16 | 416.0 | G#4 | +3
| 5 | 349.3 | F-4 | 0.0| 346.7 | F-4 | -13
| 6 | 299.4 | D-4 | +33 | 297.1 | D-4 | +20
| 7 | 262.0 | C-4 | +3 | 260.0 | C-4 | -11
| 8 | 232.9 | A#3 | -2 | 231.1 | A#3 | -15
| 9 | 209.6 | G#3 | +15 | 208.0 | G#3 | +2
| 10 | 190.5 | F#3 | +50 | 189.1 | F#3 | +37
| 11 | 174.7 | F-3 | +1 | 173.3 | F-3 | -13
| 12 | 161.2 | E-3 | -39 | 160.0 | D#3 | +48
| 13 | 149.7 | D-3 | +33 | 148.6 | D-3 | +20
| 14 | 139.7 | C#3 | +13 | 138.7 | C#3 | +1
| 15 | 131.0 | C-3 | +3 | 130.0 | C-3 | -11
| 16 | 123.3 | B-2 | -3 | 122.4 | B-2 | -16
| 17 | 116.4 | A#2 | 0.0| 115.6 | A#2 | -14
| 18 | 110.3 | A-2 | +5 | 109.5 | A-2 | -8
| 19 | 104.8 | G#2 | +16 | 104.0 | G#2 | +3
| 20 | 99.8 | G-2 | +31 | 99.0 | G-2 | +17
| 21 | 95.3 | G-2 | -49 | 94.5 | F#2 | +36
| 22 | 91.1 | F#2 | -27 | 90.4 | F#2 | -40
| 23 | 87.3 | F-2 | 0.0| 86.7 | F-2 | -12
| 24 | 83.8 | E-2 | +29 | 83.2 | E-2 | +16
| 25 | 80.6 | E-2 | -39 | 80.0 | D#2 | +48
| 26 | 77.6 | D#2 | -5 | 77.0 | D#2 | -18
| 27 | 74.9 | D-2 | +34 | 74.3 | D-2 | +20
| 28 | 72.3 | D-2 | -27 | 71.7 | D-2 | -41
| 29 | 69.9 | C#2 | +15 | 69.3 | C#2 | 0
| 30 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44
| 31 | 65.5 | C-2 | +3 | 65.0 | C-2 | -11
## shapes 2, 3
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44
| 1 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42
| 2 | 22.5 | F#0 | -46 | 22.4 | F-0 | +46
| 3 | 16.9 | C#0 | -44 | 16.8 | C-0 | +44
| 4 | 13.5 | | | 13.4
| 5 | 11.3 | | | 11.2
| 6 | 9.7 | | | 9.6
| 7 | 8.5 | | | 8.4
| 8 | 7.5 | | | 7.5
| 9 | 6.8 | | | 6.7
| 10 | 6.1 | | | 6.1
| 11 | 5.6 | | | 5.6
| 12 | 5.2 | | | 5.2
| 13 | 4.8 | | | 4.8
| 14 | 4.5 | | | 4.5
| 15 | 4.2 | | | 4.2
| 16 | 4.0 | | | 4.0
| 17 | 3.8 | | | 3.7
| 18 | 3.6 | | | 3.5
| 19 | 3.4 | | | 3.4
| 20 | 3.2 | | | 3.2
| 21 | 3.1 | | | 3.0
| 22 | 3.0 | | | 2.9
| 23 | 2.8 | | | 2.8
| 24 | 2.7 | | | 2.7
| 25 | 2.6 | | | 2.6
| 26 | 2.5 | | | 2.5
| 27 | 2.4 | | | 2.4
| 28 | 2.3 | | | 2.3
| 29 | 2.3 | | | 2.2
| 30 | 2.2 | | | 2.2
| 31 | 2.1 | | | 2.1
## shapes 4, 5
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 15720.0 | B-9 | -9 | 15600.0 | B-9 | -23
| 1 | 7860.0 | B-8 | -9 | 7800.0 | B-8 | -23
| 2 | 5240.0 | E-8 | -11 | 5200.0 | E-8 | -25
| 3 | 3930.0 | B-7 | -10 | 3900.0 | B-7 | -23
| 4 | 3144.0 | G-7 | +4 | 3120.0 | G-7 | -9
| 5 | 2620.0 | E-7 | -11 | 2600.0 | E-7 | -25
| 6 | 2245.7 | C#7 | +21 | 2228.6 | C#7 | +8
| 7 | 1965.0 | B-6 | -9 | 1950.0 | B-6 | -23
| 8 | 1746.7 | A-6 | -13 | 1733.3 | A-6 | -27
| 9 | 1572.0 | G-6 | +4 | 1560.0 | G-6 | -9
| 10 | 1429.1 | F-6 | +39 | 1418.2 | F-6 | +25
| 11 | 1310.0 | E-6 | -11 | 1300.0 | E-6 | -25
| 12 | 1209.2 | D-6 | +49 | 1200.0 | D-6 | +36
| 13 | 1122.9 | C#6 | +22 | 1114.3 | C#6 | +8
| 14 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -11
| 15 | 982.5 | B-5 | -10 | 975.0 | B-5 | -23
| 16 | 924.7 | A#5 | -15 | 917.6 | A#5 | -28
| 17 | 873.3 | A-5 | -14 | 866.7 | A-5 | -27
| 18 | 827.4 | G#5 | -7 | 821.1 | G#5 | -20
| 19 | 786.0 | G-5 | +4 | 780.0 | G-5 | -9
| 20 | 748.6 | F#5 | +20 | 742.9 | F#5 | +7
| 21 | 714.5 | F-5 | +39 | 709.1 | F-5 | +26
| 22 | 683.5 | F-5 | -38 | 678.3 | E-5 | +48
| 23 | 655.0 | E-5 | -12 | 650.0 | E-5 | -25
| 24 | 628.8 | D#5 | +18 | 624.0 | D#5 | +5
| 25 | 604.6 | D-5 | +49 | 600.0 | D-5 | +36
| 26 | 582.2 | D-5 | -16 | 577.8 | D-5 | -29
| 27 | 561.4 | C#5 | +21 | 557.1 | C#5 | +8
| 28 | 542.1 | C#5 | -40 | 537.9 | C-5 | +47
| 29 | 524.0 | C-5 | +2 | 520.0 | C-5 | -11
| 30 | 507.1 | B-4 | +45 | 503.2 | B-4 | +32
| 31 | 491.3 | B-4 | -9 | 487.5 | B-4 | -23
## shapes 6, 7, 9, 10
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 1014.2 | B-5 | +45 | 1006.5 | B-5 | +32
| 1 | 507.1 | B-4 | +45 | 503.2 | B-4 | +32
| 2 | 338.1 | E-4 | +43 | 335.5 | E-4 | +30
| 3 | 253.5 | B-3 | +45 | 251.6 | B-3 | +32
| 4 | 202.8 | G#3 | -42 | 201.3 | G-3 | +45
| 5 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30
| 6 | 144.9 | D-3 | -23 | 143.8 | D-3 | -37
| 7 | 126.8 | B-2 | +42 | 125.8 | B-2 | +32
| 8 | 112.7 | A-2 | +42 | 111.8 | A-2 | +28
| 9 | 101.4 | G#2 | -41 | 100.6 | G-2 | +45
| 10 | 92.2 | F#2 | -6 | 91.5 | F#2 | -19
| 11 | 84.5 | E-2 | +43 | 83.9 | E-2 | +31
| 12 | 78.0 | D#2 | +4 | 77.4 | D#2 | -9
| 13 | 72.4 | D-2 | -24 | 71.9 | D-2 | -37
| 14 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44
| 15 | 63.4 | B-1 | +46 | 62.9 | B-1 | +32
| 16 | 59.7 | A#1 | +41 | 59.2 | A#1 | +26
| 17 | 56.3 | A-1 | +39 | 55.9 | A-1 | +27
| 18 | 53.4 | G#1 | +48 | 53.0 | G#1 | +35
| 19 | 50.7 | G#1 | -41 | 50.3 | G-1 | +45
| 20 | 48.3 | G-1 | -25 | 47.9 | G-1 | -39
| 21 | 46.1 | F#1 | -4 | 45.7 | F#1 | -20
| 22 | 44.1 | F-1 | +16 | 43.8 | F-1 | +4
| 23 | 42.3 | E-1 | +44 | 41.9 | E-1 | +28
| 24 | 40.6 | E-1 | -26 | 40.3 | E-1 | -39
| 25 | 39.0 | D#1 | +4 | 38.7 | D#1 | -9
| 26 | 37.6 | D-1 | +41 | 37.3 | D-1 | +27
| 27 | 36.2 | D-1 | -24 | 35.9 | D-1 | -38
| 28 | 35.0 | C#1 | +19 | 34.7 | C#1 | +5
| 29 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42
| 30 | 32.7 | C-1 | 0.0 | 32.5 | C-1 | -11
| 31 | 31.7 | B-0 | +44 | 31.5 | B-0 | +33
## shapes 8
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 61.5 | B-1 | -6| 61.1 | B-1 | -18
| 1 | 30.8 | B-0 | -6| 30.5 | B-0 | -22
| 2 | 20.5 | E-0 | -8| 20.4 | E-0 | -17
| 3 | 15.4 | | | 15.3
| 4 | 12.3 | | | 12.2
| 5 | 10.3 | | | 10.2
| 6 | 8.8 | | | 8.7
| 7 | 7.7 | | | 7.6
| 8 | 6.8 | | | 6.8
| 9 | 6.2 | | | 6.1
| 10 | 5.6 | | | 5.6
| 11 | 5.1 | | | 5.1
| 12 | 4.7 | | | 4.7
| 13 | 4.4 | | | 4.4
| 14 | 4.1 | | | 4.1
| 15 | 3.8 | | | 3.8
| 16 | 3.6 | | | 3.6
| 17 | 3.4 | | | 3.4
| 18 | 3.2 | | | 3.2
| 19 | 3.1 | | | 3.1
| 20 | 2.9 | | | 2.9
| 21 | 2.8 | | | 2.8
| 22 | 2.7 | | | 2.7
| 23 | 2.6 | | | 2.5
| 24 | 2.5 | | | 2.4
| 25 | 2.4 | | | 2.3
| 26 | 2.3 | | | 2.3
| 27 | 2.2 | | | 2.2
| 28 | 2.1 | | | 2.1
| 29 | 2.0 | | | 2.0
| 30 | 2.0 | | | 2.0
| 31 | 1.9 | | | 1.9
## shapes 12, 13
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 5240.0 | E-8 | -11 | 5200.0 | E-8 | -25
| 1 | 2620.0 | E-7 | -11 | 2600.0 | E-7 | -25
| 2 | 1746.6 | A-6 | -14 | 1733.3 | A-6 | -27
| 3 | 1310.0 | E-6 | -11 | 1300.0 | E-6 | -25
| 4 | 1048.0 | C-6 | +2 | 1040.0 | C-6 | -11
| 5 | 873.3 | A-5 | -14 | 866.7 | A-5 | -27
| 6 | 748.6 | F#5 | +20 | 742.9 | F#5 | +7
| 7 | 655.0 | E-5 | -12 | 650.0 | E-5 | -25
| 8 | 582.2 | D-5 | -16 | 577.8 | D-5 | -29
| 9 | 524.0 | C-5 | +2 | 520.0 | C-5 | -11
| 10 | 476.4 | A#4 | +39 | 472.7 | A#4 | +23
| 11 | 436.7 | A-4 | -13 | 433.3 | A-4 | -27
| 12 | 403.1 | G-4 | +48 | 400.0 | G-4 | +34
| 13 | 374.3 | F#4 | +20 | 371.4 | F#4 | +6
| 14 | 349.3 | F-4 | 0.0 | 346.7 | F-4 | -13
| 15 | 327.5 | E-4 | -11 | 325.0 | E-4 | -25
| 16 | 308.2 | D#4 | -17 | 305.9 | D#4 | -30
| 17 | 291.1 | D-4 | -16 | 288.9 | D-4 | -29
| 18 | 275.8 | C#4 | -9 | 273.7 | C#4 | -22
| 19 | 262.0 | C-4 | +3 | 260.0 | C-4 | -11
| 20 | 249.5 | B-3 | +18 | 247.6 | B-3 | +5
| 21 | 238.2 | A#3 | +37 | 236.4 | A#3 | +24
| 22 | 227.8 | A#3 | -40 | 226.1 | A-3 | +47
| 23 | 218.3 | A-3 | -14 | 216.7 | A-3 | -27
| 24 | 209.6 | G#3 | +15 | 208.0 | G#3 | +2
| 25 | 201.5 | G-3 | +47 | 200.0 | G-3 | +34
| 26 | 194.1 | G-3 | -17 | 192.6 | G-3 | -31
| 27 | 187.1 | F#3 | +19 | 185.7 | F#3 | +6
| 28 | 180.7 | F#3 | -41 | 179.3 | F-3 | +45
| 29 | 174.7 | F-3 | +1 | 173.3 | F-3 | -13
| 30 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30
| 31 | 163.8 | E-3 | -11 | 162.5 | E-3 | -25
## shapes 14, 15
| pitch | NTSC | note | cent | PAL | note | cent
|------:|--------:|:----:|-----:|--------:|:----:|-----:
| 0 | 338.1 | E-4 | +43 | 335.5 | E-4 | +30
| 1 | 169.0 | E-3 | +43 | 167.7 | E-3 | +30
| 2 | 112.7 | A-2 | +42 | 111.8 | A-2 | +28
| 3 | 84.5 | E-2 | +43 | 83.9 | E-2 | +31
| 4 | 67.6 | C#2 | -44 | 67.1 | C-2 | +44
| 5 | 56.3 | A-1 | +39 | 55.9 | A-1 | +27
| 6 | 48.3 | G-1 | -25 | 47.9 | G-1 | -39
| 7 | 42.3 | E-1 | +44 | 41.9 | E-1 | +28
| 8 | 37.6 | D-1 | +41 | 37.3 | D-1 | +27
| 9 | 33.8 | C#1 | -42 | 33.5 | C-1 | +42
| 10 | 30.7 | B-0 | -11 | 30.5 | B-0 | -22
| 11 | 28.2 | A-0 | +44 | 28.0 | A-0 | +31
| 12 | 26.0 | G#0 | 0.0 | 25.8 | G#0 | -13
| 13 | 24.1 | G-0 | -29 | 24.0 | G-0 | -36
| 14 | 22.5 | F#0 | -46 | 22.4 | F-0 | +46
| 15 | 21.1 | E-0 | +42 | 21.0 | E-0 | +33
| 16 | 19.9 | D#0 | +42 | 19.7 | D#0 | +25
| 17 | 18.8 | D-0 | +40 | 18.6 | D-0 | +20
| 18 | 17.8 | C#0 | +45 | 17.7 | C#0 | +36
| 19 | 16.9 | C#0 | -44 | 16.8 | C-0 | +44
| 20 | 16.1 | C-0 | -30 | 16.0 | C-0 | -40
| 21 | 15.4 | | | 15.2
| 22 | 14.7 | | | 14.6
| 23 | 14.1 | | | 14
| 24 | 13.5 | | | 13.4
| 25 | 13.0 | | | 12.9
| 26 | 12.5 | | | 12.4
| 27 | 12.1 | | | 12
| 28 | 11.7 | | | 11.6
| 29 | 11.3 | | | 11.2
| 30 | 10.9 | | | 10.8
| 31 | 10.6 | | | 10.5
reference: [Atari 2600 VCS Sound Frequency and Waveform Guide](http://7800.8bitdev.org/index.php/Atari_2600_VCS_Sound_Frequency_and_Waveform_Guide)

View File

@ -23,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: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 55 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
@ -24,8 +25,3 @@ writers:
- WindowxDeveloper
- polluks
- Electric Keet
other:
- [3-pattern/keyboard.png](3-pattern/keyboard.png) licensed under CC-BY-SA 3.0.
- this is a derivative of [KB United States.svg](https://en.wikipedia.org/wiki/File:KB_United_States.svg) by Denelson83 under the same license.

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

@ -0,0 +1,98 @@
# clipboard format
when copying pattern data from Furnace, it's stored in the clipboard as plain text.
```
org.tildearrow.furnace - Pattern Data (144)
```
this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6pre7 is `162`.
the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from.
- `0`: note.
- `1`: instrument.
- `2`: volume.
- `3`: effect 1 type.
- `4`: effect 1 value. effect type is always included in the clip, even if skipped over.
- `5`: effect 2 type.
- `6`: effect 2 value. effect type is always included in the clip, even if skipped over.
- `7`: effect 3 type...
- ...and so on.
examples of the starting column:
```
org.tildearrow.furnace - Pattern Data (144)
0
D-6007F08080706|...........|
...............|...........|
...............|A#500..080F|
...............|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
1
007F08080706|...........|
............|...........|
............|A#500..080F|
............|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
2
7F08080706|...........|
..........|...........|
..........|A#500..080F|
..........|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
3
08080706|...........|
........|...........|
........|A#500..080F|
........|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
4
08080706|...........|
........|...........|
........|A#500..080F|
........|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
5
0706|...........|
....|...........|
....|A#500..080F|
....|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
6
0706|...........|
....|...........|
....|A#500..080F|
....|...........|
```
```
org.tildearrow.furnace - Pattern Data (144)
0
...........|
...........|
A#500..080F|
...........|
```
each line following the column number is verbatim from the pattern view with channels separated by `|`. each line also ends in `|`.
notes use the default settings for note display (no German notation), including note off `OFF`, note release `===`, and macro release `REL`.

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)

View File

@ -79,6 +79,7 @@
#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"
@ -507,6 +508,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
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

@ -927,12 +927,13 @@ void DivEngine::runExportThread() {
}
}
float* outBuf[2];
float* outBuf[DIV_MAX_OUTPUTS];
memset(outBuf,0,sizeof(void*)*DIV_MAX_OUTPUTS);
outBuf[0]=new float[EXPORT_BUFSIZE];
outBuf[1]=new float[EXPORT_BUFSIZE];
short* sysBuf[DIV_MAX_CHIPS];
for (int i=0; i<song.systemLen; i++) {
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
sysBuf[i]=new short[EXPORT_BUFSIZE*disCont[i].dispatch->getOutputCount()];
}
// take control of audio output

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

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;
}
@ -933,6 +961,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
featureSM=true;
featureSL=true;
break;
case DIV_INS_TED:
break;
case DIV_INS_MAX:
break;
@ -2280,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

@ -81,6 +81,8 @@ enum DivInstrumentType: unsigned short {
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
};
@ -446,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) {
@ -456,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

@ -1253,6 +1253,7 @@ int DivPlatformES5506::init(DivEngine* p, int channels, int sugRate, const DivCo
dumpWrites=false;
skipRegisterWrites=false;
volScale=0;
curPage=0;
for (int i=0; i<32; i++) {
isMuted[i]=false;

View File

@ -255,7 +255,7 @@ void DivPlatformGB::tick(bool sysTick) {
chan[i].sweepChanged=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT:
chan[i].hwSeqDelay=data+1;
chan[i].hwSeqDelay=(data+1)*parent->tickMult;
leave=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT_REL:

View File

@ -56,10 +56,10 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) {
for (int i=0; i<8; i++) {
int o=(
((regPool[12+(i>>4)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+
((regPool[12+(i>>4)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+
((regPool[12+(i>>4)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+
((regPool[12+(i>>4)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0)
((regPool[12+(i>>2)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+
((regPool[12+(i>>2)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+
((regPool[12+(i>>2)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+
((regPool[12+(i>>2)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0)
)<<2;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767);
}

View File

@ -31,10 +31,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
}
void DivPlatformMSM6258::acquire(short** buf, size_t len) {
short* outs[2]={
&msmOut,
NULL
};
for (size_t h=0; h<len; h++) {
if (--msmClockCount<0) {
if (--msmDividerCount<=0) {
@ -71,7 +67,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) {
}
}
msm->sound_stream_update(outs,1);
msm->sound_stream_update(&msmOut,1);
msmDividerCount=msmDivider;
}
msmClockCount=msmClock;

View File

@ -22,7 +22,6 @@
#include "../../ta-log.h"
#include <math.h>
#define rRead(a,v) n163.addr_w(a); n163.data_r(v);
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,m)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) \
@ -35,7 +34,7 @@
rWriteMask(0x78-(c<<3)+(a&7),v,m) \
}
#define CHIP_FREQBASE (15*32768)
#define CHIP_FREQBASE (15*524288)
const char* regCheatSheetN163[]={
"FreqL7", "40",
@ -147,7 +146,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
} else {
// load from custom
DivWavetable* wt=parent->getWave(wave);
for (int i=0; i<len; i++) {
for (int i=0; i<wt->len; i++) {
unsigned char addr=(pos+i); // address (nibble each)
if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area
break;
@ -156,7 +155,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
if (wt->max<1 || wt->len<1) {
rWriteMask(addr>>1,0,mask);
} else {
int data=wt->data[i*wt->len/len]*15/wt->max;
int data=wt->data[i]*15/wt->max;
if (data<0) data=0;
if (data>15) data=15;
rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask);
@ -167,7 +166,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
void DivPlatformN163::updateWaveCh(int ch) {
if (ch<=chanMax) {
logV("updateWave with pos %d and len %d",chan[ch].wavePos,chan[ch].waveLen);
//logV("updateWave with pos %d and len %d",chan[ch].wavePos,chan[ch].waveLen);
updateWave(ch,-1,chan[ch].wavePos,chan[ch].waveLen);
if (chan[ch].active && !isMuted[ch]) {
chan[ch].volumeChanged=true;
@ -198,11 +197,8 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.duty.had) {
if (chan[i].wavePos!=chan[i].std.duty.val) {
chan[i].wavePos=chan[i].std.duty.val;
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
if (chan[i].curWavePos!=chan[i].std.duty.val) {
chan[i].curWavePos=chan[i].std.duty.val;
chan[i].waveChanged=true;
}
}
@ -210,7 +206,7 @@ void DivPlatformN163::tick(bool sysTick) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
chan[i].wave=chan[i].std.wave.val;
chan[i].ws.changeWave1(chan[i].wave);
if (chan[i].waveMode&0x2) {
if (chan[i].waveMode) {
chan[i].waveUpdated=true;
}
}
@ -225,60 +221,11 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].waveLen=chan[i].std.ex1.val&0xfc;
chan[i].ws.setWidth(chan[i].waveLen);
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
if (chan[i].curWaveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].curWaveLen=chan[i].std.ex1.val&0xfc;
chan[i].freqChanged=true;
}
}
if (chan[i].std.ex2.had) {
if ((chan[i].waveMode&0x2)!=(chan[i].std.ex2.val&0x2)) { // update when every waveform changed
chan[i].waveMode=(chan[i].waveMode&~0x2)|(chan[i].std.ex2.val&0x2);
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
chan[i].waveChanged=true;
}
}
if ((chan[i].waveMode&0x1)!=(chan[i].std.ex2.val&0x1)) { // update waveform now
chan[i].waveMode=(chan[i].waveMode&~0x1)|(chan[i].std.ex2.val&0x1);
if (chan[i].waveMode&0x1) { // rising edge
chan[i].waveUpdated=true;
chan[i].waveChanged=true;
}
}
}
if (chan[i].std.ex3.had) {
if (chan[i].loadWave!=chan[i].std.ex3.val) {
chan[i].loadWave=chan[i].std.ex3.val;
if (chan[i].loadMode&0x2) {
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].std.alg.had) {
if (chan[i].loadPos!=chan[i].std.alg.val) {
chan[i].loadPos=chan[i].std.alg.val;
}
}
if (chan[i].std.fb.had) {
if (chan[i].loadLen!=(chan[i].std.fb.val&0xfc)) {
chan[i].loadLen=chan[i].std.fb.val&0xfc;
}
}
if (chan[i].std.fms.had) {
if ((chan[i].loadMode&0x2)!=(chan[i].std.fms.val&0x2)) { // load when every waveform changes
chan[i].loadMode=(chan[i].loadMode&~0x2)|(chan[i].std.fms.val&0x2);
}
if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1);
if (chan[i].loadMode&0x1) { // rising edge
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].volumeChanged) {
if (chan[i].active && !isMuted[i]) {
chWriteMask(i,0x7,chan[i].resVol&0xf,0xf);
@ -288,7 +235,7 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].volumeChanged=false;
}
if (chan[i].waveChanged) {
chWrite(i,0x6,chan[i].wavePos);
chWrite(i,0x6,chan[i].curWavePos);
if (chan[i].active) {
chan[i].freqChanged=true;
}
@ -309,23 +256,22 @@ void DivPlatformN163::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
// TODO: what is this mess?
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
chan[i].freq=(((chan[i].freq*chan[i].waveLen)*(chanMax+1))/16);
if (lenCompensate) {
chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/256);
} else {
chan[i].freq*=(chanMax+1);
chan[i].freq>>=3;
}
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
if (chan[i].keyOn) {
if (chan[i].wave<0) {
chan[i].wave=0;
if (chan[i].waveMode&0x2) {
updateWaveCh(i);
}
}
}
if (chan[i].keyOff && !isMuted[i]) {
chWriteMask(i,0x7,0,0xf);
}
chWrite(i,0x0,chan[i].freq&0xff);
chWrite(i,0x2,chan[i].freq>>8);
chWrite(i,0x4,((256-chan[i].waveLen)&0xfc)|((chan[i].freq>>16)&3));
chWrite(i,0x4,((256-chan[i].curWaveLen)&0xfc)|((chan[i].freq>>16)&3));
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
@ -338,14 +284,21 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163);
if (chan[c.chan].insChanged) {
chan[c.chan].wave=ins->n163.wave;
chan[c.chan].wavePos=ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.waveLen;
if (ins->n163.wave>=0) {
chan[c.chan].wave=ins->n163.wave;
}
chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,false);
chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,true);
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
}
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
chan[c.chan].waveChanged=true;
if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) {
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
}
@ -369,7 +322,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
//chan[c.chan].macroInit(NULL);
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].active=false;
@ -411,13 +364,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:16);
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:16);
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -432,68 +385,40 @@ int DivPlatformN163::dispatch(DivCommand c) {
}
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
if (chan[c.chan].waveMode&0x2) {
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].keyOn=true;
break;
case DIV_CMD_N163_WAVE_POSITION:
chan[c.chan].wavePos=c.value;
if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].curWavePos=c.value;
chan[c.chan].waveChanged=true;
break;
case DIV_CMD_N163_WAVE_LENGTH:
chan[c.chan].waveLen=c.value&0xfc;
if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].curWaveLen=c.value&0xfc;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_N163_WAVE_MODE:
chan[c.chan].waveMode=c.value&0x3;
if (chan[c.chan].waveMode&0x3) { // update now
chan[c.chan].waveUpdated=true;
chan[c.chan].waveChanged=true;
}
break;
case DIV_CMD_N163_WAVE_LOAD:
chan[c.chan].loadWave=c.value;
if (chan[c.chan].loadMode&0x2) { // load when every waveform changes
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
}
break;
case DIV_CMD_N163_WAVE_LOADPOS:
chan[c.chan].loadPos=c.value;
chan[c.chan].wavePos=c.value;
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
break;
case DIV_CMD_N163_WAVE_LOADLEN:
chan[c.chan].loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_WAVE_LOADMODE:
chan[c.chan].loadMode=c.value&0x3;
if (chan[c.chan].loadMode&0x1) { // load now
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
chan[c.chan].waveLen=c.value&0xfc;
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
loadWave=c.value;
if (loadMode&0x2) { // load when every waveform changes
updateWave(c.chan,loadWave,loadPos,loadLen);
if (loadWave>=0 && loadWave<parent->song.waveLen) {
updateWave(-1,loadWave,loadPos,-1);
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
loadPos=c.value;
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADLEN:
loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
loadMode=c.value&0x3;
if (loadMode&0x3) { // load now
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break;
case DIV_CMD_N163_CHANNEL_LIMIT:
if (chanMax!=(c.value&0x7)) {
chanMax=c.value&0x7;
@ -547,9 +472,6 @@ void DivPlatformN163::forceIns() {
chan[i].freqChanged=true;
chan[i].volumeChanged=true;
chan[i].waveChanged=true;
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
}
}
}
@ -557,7 +479,7 @@ void DivPlatformN163::forceIns() {
void DivPlatformN163::notifyWaveChange(int wave) {
for (int i=0; i<8; i++) {
if (chan[i].wave==wave) {
if (chan[i].waveMode&0x2) {
if (chan[i].waveMode) {
chan[i].ws.changeWave1(wave);
chan[i].waveUpdated=true;
}
@ -619,8 +541,6 @@ void DivPlatformN163::reset() {
chanMax=initChanMax;
loadWave=-1;
loadPos=0;
loadLen=0;
loadMode=0;
rWrite(0x7f,initChanMax<<4);
}
@ -655,6 +575,8 @@ void DivPlatformN163::setFlags(const DivConfig& flags) {
oscBuf[i]->rate=rate/(initChanMax+1);
}
lenCompensate=flags.getBool("lenCompensate",false);
// needed to make sure changing channel count won't trigger glitches
reset();
}

View File

@ -29,9 +29,8 @@ class DivPlatformN163: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
signed char resVol;
short wave, wavePos, waveLen;
unsigned char waveMode;
short loadWave, loadPos, loadLen;
unsigned char loadMode;
short curWavePos, curWaveLen;
bool waveMode;
bool volumeChanged;
bool waveChanged, waveUpdated;
DivWaveSynth ws;
@ -41,11 +40,9 @@ class DivPlatformN163: public DivDispatch {
wave(-1),
wavePos(0),
waveLen(0),
curWavePos(0),
curWaveLen(0),
waveMode(0),
loadWave(-1),
loadPos(0),
loadLen(0),
loadMode(0),
volumeChanged(false),
waveChanged(false),
waveUpdated(false) {}
@ -63,9 +60,8 @@ class DivPlatformN163: public DivDispatch {
FixedQueue<QueuedWrite,2048> writes;
unsigned char initChanMax;
unsigned char chanMax;
short loadWave, loadPos, loadLen;
unsigned char loadMode;
bool multiplex;
short loadWave, loadPos;
bool multiplex, lenCompensate;
n163_core n163;
unsigned char regPool[128];

View File

@ -230,13 +230,16 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (i>=6 && properDrums) {
drumState&=~(0x10>>(i-6));
immWrite(0x0e,0x20|drumState);
logV("properDrums %d",i);
} else if (i>=6 && drums) {
drumState&=~(0x10>>(chan[i].note%12));
immWrite(0x0e,0x20|drumState);
logV("drums %d",i);
} else {
if (i<9) {
immWrite(0x20+i,(chan[i].freqH)|(chan[i].state.alg?0x20:0));
}
logV("normal %d",i);
}
//chan[i].keyOn=false;
chan[i].keyOff=false;
@ -253,7 +256,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
for (int i=0; i<11; i++) {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
if (chan[i].fixedFreq>0 && properDrums) chan[i].freq=chan[i].fixedFreq;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>65535) chan[i].freq=65535;
int freqt=toFreq(chan[i].freq);
@ -771,11 +774,17 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
if (c.value) {
properDrums=true;
immWrite(0x0e,0x20);
drumState=0;
} else {
properDrums=false;
immWrite(0x0e,0x00);
drumState=0;
}
chan[6].freqChanged=true;
chan[7].freqChanged=true;
chan[8].freqChanged=true;
chan[9].freqChanged=true;
chan[10].freqChanged=true;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);

View File

@ -283,6 +283,7 @@ void DivPlatformPET::reset() {
memset(regPool,0,16);
chan[0]=Channel();
chan[0].std.setEngine(parent);
rWrite(10,chan[0].wave);
}
int DivPlatformPET::getOutputCount() {

View File

@ -135,9 +135,9 @@ void okim6258_device::device_reset()
// sound_stream_update - handle a stream update
//-------------------------------------------------
void okim6258_device::sound_stream_update(short** outputs, int len)
void okim6258_device::sound_stream_update(short* output, int len)
{
short* buffer = outputs[0];
short* buffer = output;
if (m_status & STATUS_PLAYING)
{

View File

@ -43,7 +43,7 @@ public:
void device_clock_changed();
// sound stream updates
void sound_stream_update(short** outputs, int len);
void sound_stream_update(short* output, int len);
private:
int16_t clock_adpcm(uint8_t nibble);

View File

@ -0,0 +1,231 @@
/*
* ted-sound.c
*
* Written by
* Andreas Boose <viceteam@t-online.de>
* Tibor Biczo <crown @ axelero . hu>
* Marco van den Heuvel <blackystardust68@yahoo.com>
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* 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., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ted-sound.h"
/* ------------------------------------------------------------------------- */
/* FIXME: Find proper volume multiplier. */
const int16_t volume_tab[16] = {
0x0000, 0x0800, 0x1000, 0x1800, 0x2000, 0x2800, 0x3000, 0x3800,
0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff
};
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int scc)
{
int i;
int j;
int16_t volume;
if (snd->digital) {
for (i = 0; i < nr; i++) {
pbuf[i] = (snd->volume * (snd->voice0_output_enabled + snd->voice1_output_enabled));
}
} else {
for (i = 0; i < nr; i++) {
snd->sample_position_remainder += snd->sample_length_remainder;
if (snd->sample_position_remainder >= snd->speed) {
snd->sample_position_remainder -= snd->speed;
snd->sample_position_integer++;
}
snd->sample_position_integer += snd->sample_length_integer;
if (snd->sample_position_integer >= 8) {
/* Advance state engine */
uint32_t ticks = snd->sample_position_integer >> 3;
if (snd->voice0_accu <= ticks) {
uint32_t delay = ticks - snd->voice0_accu;
snd->voice0_sign ^= 1;
snd->voice0_accu = 1023 - snd->voice0_reload;
if (snd->voice0_accu == 0) {
snd->voice0_accu = 1024;
}
if (delay >= snd->voice0_accu) {
snd->voice0_sign = ((delay / snd->voice0_accu)
& 1) ? snd->voice0_sign ^ 1
: snd->voice0_sign;
snd->voice0_accu = snd->voice0_accu - (delay % snd->voice0_accu);
} else {
snd->voice0_accu -= delay;
}
} else {
snd->voice0_accu -= ticks;
}
if (snd->voice1_accu <= ticks) {
uint32_t delay = ticks - snd->voice1_accu;
snd->voice1_sign ^= 1;
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
snd->voice1_accu = 1023 - snd->voice1_reload;
if (snd->voice1_accu == 0) {
snd->voice1_accu = 1024;
}
if (delay >= snd->voice1_accu) {
snd->voice1_sign = ((delay / snd->voice1_accu)
& 1) ? snd->voice1_sign ^ 1
: snd->voice1_sign;
for (j = 0; j < (int)(delay / snd->voice1_accu);
j++) {
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
}
snd->voice1_accu = snd->voice1_accu - (delay % snd->voice1_accu);
} else {
snd->voice1_accu -= delay;
}
} else {
snd->voice1_accu -= ticks;
}
}
snd->sample_position_integer = snd->sample_position_integer & 7;
volume = 0;
if (snd->voice0_output_enabled && snd->voice0_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && !snd->noise && snd->voice1_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && snd->noise && (!(snd->noise_shift_register & 1))) {
volume += snd->volume;
}
pbuf[i] = volume;
}
}
return nr;
}
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec)
{
uint8_t val;
memset(snd,0,sizeof(struct plus4_sound_s));
snd->speed = speed;
snd->sample_length_integer = cycles_per_sec / speed;
snd->sample_length_remainder = cycles_per_sec % speed;
snd->sample_position_integer = 0;
snd->sample_position_remainder = 0;
snd->noise_shift_register = 0;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
val = snd->plus4_sound_data[3];
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
return 1;
}
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val)
{
switch (addr) {
case 0x0e:
snd->plus4_sound_data[0] = val;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
case 0x0f:
snd->plus4_sound_data[1] = val;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x10:
snd->plus4_sound_data[2] = val & 3;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x11:
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
snd->plus4_sound_data[3] = val;
break;
case 0x12:
snd->plus4_sound_data[4] = val & 3;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
}
}
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr)
{
switch (addr) {
case 0x0e:
return snd->plus4_sound_data[0];
case 0x0f:
return snd->plus4_sound_data[1];
case 0x10:
return snd->plus4_sound_data[2] | 0xc0;
case 0x11:
return snd->plus4_sound_data[3];
case 0x12:
return snd->plus4_sound_data[4];
}
return 0;
}
void ted_sound_reset(struct plus4_sound_s* snd)
{
uint16_t i;
snd->noise_shift_register = 0;
for (i = 0x0e; i <= 0x12; i++) {
ted_sound_machine_store(snd,i,0);
}
}

View File

@ -0,0 +1,81 @@
/*
* ted-sound.h
*
* Written by
* Andreas Boose <viceteam@t-online.de>
* Marco van den Heuvel <blackystardust68@yahoo.com>
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* 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., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
#ifndef VICE_TEDSOUND_H
#define VICE_TEDSOUND_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
struct plus4_sound_s {
/* Voice 0 collect number of cycles elapsed */
uint32_t voice0_accu;
/* Voice 0 toggle sign and reload accu if accu reached 0 */
uint32_t voice0_reload;
/* Voice 0 sign of the square wave */
int16_t voice0_sign;
uint8_t voice0_output_enabled;
/* Voice 1 collect number of cycles elapsed */
uint32_t voice1_accu;
/* Voice 1 toggle sign and reload accu if accu reached 0 */
uint32_t voice1_reload;
/* Voice 1 sign of the square wave */
int16_t voice1_sign;
uint8_t voice1_output_enabled;
/* Volume multiplier */
int16_t volume;
/* 8 cycles units per sample */
uint32_t speed;
uint32_t sample_position_integer;
uint32_t sample_position_remainder;
uint32_t sample_length_integer;
uint32_t sample_length_remainder;
/* Digital output? */
uint8_t digital;
/* Noise generator active? */
uint8_t noise;
uint8_t noise_shift_register;
/* Registers */
uint8_t plus4_sound_data[5];
};
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec);
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int sound_chip_channels);
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val);
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr);
void ted_sound_reset(struct plus4_sound_s* snd);
#ifdef __cplusplus
};
#endif
#endif

View File

@ -626,7 +626,7 @@ void DivPlatformSoundUnit::quit() {
delete oscBuf[i];
}
delete su;
delete sampleMem;
delete[] sampleMem;
}
DivPlatformSoundUnit::~DivPlatformSoundUnit() {

354
src/engine/platform/ted.cpp Normal file
View File

@ -0,0 +1,354 @@
/**
* 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.
*/
#include "ted.h"
#include "../engine.h"
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 8
const char* regCheatSheetTED[]={
"Freq0L", "0e",
"Freq1L", "0f",
"Freq1H", "10",
"Control", "11",
"Freq0H", "12",
NULL
};
const char** DivPlatformTED::getRegisterSheet() {
return regCheatSheetTED;
}
void DivPlatformTED::acquire(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
while (!writes.empty()) {
QueuedWrite w=writes.front();
ted_sound_machine_store(&ted,w.addr,w.val);
regPool[(w.addr-0x0e)&7]=w.val;
writes.pop();
}
ted_sound_machine_calculate_samples(&ted,&buf[0][h],1,1);
oscBuf[0]->data[oscBuf[0]->needle++]=(ted.voice0_output_enabled && ted.voice0_sign)?(ted.volume<<1):0;
oscBuf[1]->data[oscBuf[1]->needle++]=(ted.voice1_output_enabled && ((ted.noise && (!(ted.noise_shift_register&1))) || (!ted.noise && ted.voice1_sign)))?(ted.volume<<1):0;
}
}
void DivPlatformTED::tick(bool sysTick) {
bool resetPhase=false;
for (int _i=0; _i<2; _i++) {
int i=chanOrder[_i];
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,MIN(8,chan[i].std.vol.val),8);
updateCtrl=true;
vol=chan[i].outVol;
}
if (chan[i].std.duty.had) {
chan[i].noise=chan[i].std.duty.val&2;
chan[i].square=chan[i].std.duty.val&1;
chan[i].freqChanged=true;
updateCtrl=true;
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
if (i==1 && chan[i].noise && !chan[i].square) chan[i].freq>>=4;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>1023) chan[i].freq=1023;
if (i==1) {
rWrite(0x0f,(1022-chan[i].freq)&0xff);
rWrite(0x10,((1022-chan[i].freq)>>8)&0xff);
} else {
rWrite(0x0e,(1022-chan[i].freq)&0xff);
rWrite(0x12,((1022-chan[i].freq)>>8)&0xff);
}
if (chan[i].keyOn) {
updateCtrl=true;
}
if (chan[i].keyOff) {
updateCtrl=true;
}
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
}
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
resetPhase=true;
updateCtrl=true;
}
}
if (resetPhase) {
rWrite(0x11,0x80);
}
if (updateCtrl) {
updateCtrl=false;
rWrite(0x11,(vol&15)|((chan[0].active && chan[0].square && !isMuted[0])?0x10:0)|((chan[1].active && chan[1].square && !isMuted[1])?0x20:0)|((chan[1].active && chan[1].noise && !isMuted[1])?0x40:0));
}
}
int DivPlatformTED::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TED);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
if (keyPriority) {
if (chanOrder[0]==c.chan) {
chanOrder[0]=chanOrder[1];
chanOrder[1]=c.chan;
}
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
chan[c.chan].insChanged=true;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
vol=chan[c.chan].outVol;
updateCtrl=true;
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_STD_NOISE_MODE:
chan[c.chan].noise=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TED));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 8;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformTED::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
updateCtrl=true;
}
void DivPlatformTED::forceIns() {
for (int i=0; i<2; i++) {
chan[i].freqChanged=true;
}
updateCtrl=true;
}
void* DivPlatformTED::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTED::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTED::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformTED::getRegisterPool() {
return regPool;
}
int DivPlatformTED::getRegisterPoolSize() {
return 5;
}
void DivPlatformTED::reset() {
writes.clear();
memset(regPool,0,8);
for (int i=0; i<2; i++) {
chan[i]=DivPlatformTED::Channel();
chan[i].std.setEngine(parent);
}
if (dumpWrites) {
addWrite(0xffffffff,0);
}
ted_sound_machine_init(&ted,1,8);
updateCtrl=true;
vol=15;
chanOrder[0]=0;
chanOrder[1]=1;
}
int DivPlatformTED::getOutputCount() {
return 1;
}
bool DivPlatformTED::keyOffAffectsArp(int ch) {
return true;
}
void DivPlatformTED::notifyInsDeletion(void* ins) {
for (int i=0; i<2; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformTED::setFlags(const DivConfig& flags) {
if (flags.getInt("clockSel",0)) {
chipClock=COLOR_PAL*2.0/5.0;
} else {
chipClock=COLOR_NTSC/2.0;
}
CHECK_CUSTOM_CLOCK;
rate=chipClock/8;
for (int i=0; i<2; i++) {
oscBuf[i]->rate=rate;
}
keyPriority=flags.getBool("keyPriority",true);
}
void DivPlatformTED::poke(unsigned int addr, unsigned short val) {
rWrite(addr,val);
}
void DivPlatformTED::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
int DivPlatformTED::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<2; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 2;
}
void DivPlatformTED::quit() {
for (int i=0; i<2; i++) {
delete oscBuf[i];
}
}
DivPlatformTED::~DivPlatformTED() {
}

78
src/engine/platform/ted.h Normal file
View File

@ -0,0 +1,78 @@
/**
* 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 _TED_H
#define _TED_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "sound/ted-sound.h"
class DivPlatformTED: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
bool noise, square;
Channel():
SharedChannel<signed char>(8),
noise(false),
square(true) {}
};
Channel chan[2];
DivDispatchOscBuffer* oscBuf[2];
bool isMuted[2];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
FixedQueue<QueuedWrite,64> writes;
struct plus4_sound_s ted;
unsigned char vol;
bool updateCtrl, keyPriority;
unsigned char chanOrder[2];
unsigned char regPool[8];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
int getOutputCount();
bool keyOffAffectsArp(int ch);
void setFlags(const DivConfig& flags);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformTED();
};
#endif

View File

@ -128,7 +128,8 @@ enum DivSystem {
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260
DIV_SYSTEM_K053260,
DIV_SYSTEM_TED
};
enum DivEffectType: unsigned short {

View File

@ -200,9 +200,7 @@ String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAccepta
return "Famicom Disk System";
}
if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_N163) {
String ret="Famicom + ";
ret+=getConfString("c163Name",DIV_C163_DEFAULT_NAME);
return ret;
return "Famicom + Namco 163";
}
if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_MMC5) {
return "Famicom + MMC5";
@ -230,11 +228,7 @@ String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAccepta
String ret="";
for (int i=0; i<ds.systemLen; i++) {
if (i>0) ret+=" + ";
if (ds.system[i]==DIV_SYSTEM_N163) {
ret+=getConfString("c163Name",DIV_C163_DEFAULT_NAME);
} else {
ret+=getSystemName(ds.system[i]);
}
ret+=getSystemName(ds.system[i]);
}
return ret;
@ -242,11 +236,6 @@ String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAccepta
const char* DivEngine::getSystemName(DivSystem sys) {
if (sysDefs[sys]==NULL) return "Unknown";
if (sys==DIV_SYSTEM_N163) {
String c1=getConfString("c163Name",DIV_C163_DEFAULT_NAME);
strncpy(c163NameCS,c1.c_str(),1023);
return c163NameCS;
}
return sysDefs[sys]->name;
}
@ -998,27 +987,24 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_N163]=new DivSysDef(
"Namco 163/C163/129/160/106/whatever", NULL, 0x8c, 0, 8, false, true, 0, false, 0,
"Namco 163", NULL, 0x8c, 0, 8, false, true, 0, false, 0,
"an expansion chip for the Famicom, with full wavetable.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
{DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163},
{},
{
{0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "18xx: Change channel limits (0 to 7, x + 1)"}},
{0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: Load a waveform into memory"}},
{0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: Set position for wave load"}}
},
{
{0x10, {DIV_CMD_WAVE, "10xx: Select waveform"}},
{0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM (single nibble unit)"}},
{0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)"}},
{0x13, {DIV_CMD_N163_WAVE_MODE, "130x: Change waveform update mode (0: off; bit 0: update now; bit 1: update when every waveform changes)"}},
{0x14, {DIV_CMD_N163_WAVE_LOAD, "14xx: Select waveform for load to RAM"}},
{0x15, {DIV_CMD_N163_WAVE_LOADPOS, "15xx: Set waveform position for load to RAM (single nibble unit)"}},
{0x16, {DIV_CMD_N163_WAVE_LOADLEN, "16xx: Set waveform length for load to RAM (04 to FC, 4 nibble unit)"}},
{0x17, {DIV_CMD_N163_WAVE_LOADMODE, "170x: Change waveform load mode (0: off; bit 0: load now; bit 1: load when every waveform changes)"}},
{0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "180x: Change channel limits (0 to 7, x + 1)"}},
{0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: (Global) Select waveform for load to RAM"}},
{0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: (Global) Set waveform position for load to RAM (single nibble unit)"}},
{0x22, {DIV_CMD_N163_GLOBAL_WAVE_LOADLEN, "22xx: (Global) Set waveform length for load to RAM (04 to FC, 4 nibble unit)"}},
{0x23, {DIV_CMD_N163_GLOBAL_WAVE_LOADMODE, "230x: (Global) Change waveform load mode (0: off; bit 0: load now; bit 1: load when every waveform changes)"}},
{0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM"}},
{0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC in steps of 4)"}},
{0x15, {DIV_CMD_N163_WAVE_LOADPOS, "15xx: Set waveform load position"}},
{0x16, {DIV_CMD_N163_WAVE_LOADLEN, "16xx: Set waveform load length (04 to FC in steps of 4)"}},
}
);
@ -1877,6 +1863,16 @@ void DivEngine::registerSystems() {
}
);
sysDefs[DIV_SYSTEM_TED]=new DivSysDef(
"MOS Technology TED", NULL, 0xcd, 0, 2, false, true, 0, false, 0,
"two square waves (one may be turned into noise). used in the Commodore Plus/4, 16 and 116.",
{"Channel 1", "Channel 2"},
{"CH1", "CH2"},
{DIV_CH_PULSE, DIV_CH_PULSE},
{DIV_INS_TED, DIV_INS_TED},
{}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
"this is a system designed for testing purposes.",

View File

@ -38,6 +38,7 @@ const char* aboutLine[]={
"akumanatt",
"cam900",
"djtuBIG-MaliceX",
"Eknous-P",
"laoo",
"MooingLemur",
"OPNA2608",
@ -114,6 +115,7 @@ const char* aboutLine[]={
"potatoTeto",
"psxdominator",
"Raijin",
"railzen7",
"SnugglyBun",
"SuperJet Spade",
"SwapXFO",
@ -182,6 +184,8 @@ const char* aboutLine[]={
"Stella by Stella Team",
"QSound emulator by superctr and Valley Bell",
"VICE VIC-20 sound core by Rami Rasanen and viznut",
"VICE TED sound core by Andreas Boose, Tibor Biczo",
"and Marco van den Heuvel",
"VERA sound core by Frank van den Hoef",
"mzpokeysnd POKEY emulator by Michael Borisov",
"ASAP POKEY emulator by Piotr Fusik",

View File

@ -308,6 +308,7 @@ void FurnaceGUI::drawChanOsc() {
"- %I: instrument number (decimal)\n"
"- %x: instrument number (hex)\n"
"- %s: chip name\n"
"- %p: chip part number\n"
"- %S: chip ID\n"
"- %v: volume (decimal)\n"
"- %V: volume (percentage)\n"
@ -510,6 +511,10 @@ void FurnaceGUI::drawChanOsc() {
text+=e->getSystemName(e->sysOfChan[ch]);
break;
}
case 'p': {
text+=FurnaceGUI::getSystemPartNumber(e->sysOfChan[ch], e->song.systemFlags[e->dispatchOfChan[ch]]);
break;
}
case 'S': {
text+=fmt::sprintf("%d",e->dispatchOfChan[ch]);
break;

View File

@ -271,9 +271,15 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (cursor.y>=e->curSubSong->patLen) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=0;
if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1);
if (settings.wrapVertical>1) {
if (!e->isPlaying() || !followPattern) {
if (curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1);
} else if (settings.wrapVertical==3) {
setOrder(0);
} else {
cursor.y=e->curSubSong->patLen-1;
}
} else {
cursor.y=e->curSubSong->patLen-1;
}
@ -289,9 +295,15 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (cursor.y<0) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=e->curSubSong->patLen-1;
if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder>0) {
setOrder(curOrder-1);
if (settings.wrapVertical>1) {
if (!e->isPlaying() || !followPattern) {
if (curOrder>0) {
setOrder(curOrder-1);
} else if (settings.wrapVertical==3) {
setOrder(e->curSubSong->ordersLen-1);
} else {
cursor.y=0;
}
} else {
cursor.y=0;
}

View File

@ -77,7 +77,6 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
if (i>=0 && i<e->song.insLen) {
DivInstrument* ins=e->song.ins[i];
insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
if (ins->type==DIV_INS_N163) insType=settings.c163Name.c_str();
switch (ins->type) {
case DIV_INS_FM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
@ -283,6 +282,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_TED:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TED]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);

View File

@ -462,8 +462,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- chanMax: %d",ch->chanMax);
ImGui::Text("- loadWave: %d",ch->loadWave);
ImGui::Text("- loadPos: %d",ch->loadPos);
ImGui::Text("- loadLen: %d",ch->loadLen);
ImGui::Text("- loadMode: %d",ch->loadMode);
COMMON_CHIP_DEBUG_BOOL;
ImGui::TextColored(ch->multiplex?colorOn:colorOff,">> Multiplex");
break;
@ -876,10 +874,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- wavepos: %d",ch->wavePos);
ImGui::Text("- wavelen: %d",ch->waveLen);
ImGui::Text("- wavemode: %d",ch->waveMode);
ImGui::Text("- loadwave: %d",ch->loadWave);
ImGui::Text("- loadpos: %d",ch->loadPos);
ImGui::Text("- loadlen: %d",ch->loadLen);
ImGui::Text("- loadmode: %d",ch->loadMode);
ImGui::Text("- resVol: %.2x",ch->resVol);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged");

View File

@ -98,6 +98,10 @@ const char* FurnaceGUI::noteName(short note, short octave) {
if (seek<0 || seek>=180) {
return "???";
}
if (settings.flatNotes) {
if (settings.germanNotation) return noteNamesGF[seek];
return noteNamesF[seek];
}
if (settings.germanNotation) return noteNamesG[seek];
return noteNames[seek];
}
@ -606,10 +610,11 @@ void FurnaceGUI::autoDetectSystem() {
std::map<DivSystem,int> sysCountMap;
std::map<DivSystem,DivConfig> sysConfMap;
for (int i=0; i<e->song.systemLen; i++) {
try {
sysCountMap.at(e->song.system[i])++;
} catch (std::exception& ex) {
auto it=sysCountMap.find(e->song.system[i]);
if (it==sysCountMap.cend()) {
sysCountMap[e->song.system[i]]=1;
} else {
it->second++;
}
sysConfMap[e->song.system[i]]=e->song.systemFlags[i];
}
@ -627,10 +632,11 @@ void FurnaceGUI::autoDetectSystem() {
defCountMap.clear();
defConfMap.clear();
for (FurnaceGUISysDefChip& k: j.orig) {
try {
defCountMap.at(k.sys)++;
} catch (std::exception& ex) {
auto it=defCountMap.find(k.sys);
if (it==defCountMap.cend()) {
defCountMap[k.sys]=1;
} else {
it->second++;
}
DivConfig dc;
dc.loadFromMemory(k.flags);
@ -643,27 +649,37 @@ void FurnaceGUI::autoDetectSystem() {
logV("- %s: %d",e->getSystemName(k.first),k.second);
}*/
for (std::pair<DivSystem,int> k: defCountMap) {
try {
if (sysCountMap.at(k.first)!=k.second) {
isMatch=false;
break;
}
DivConfig& sysDC=sysConfMap.at(k.first);
for (std::pair<String,String> l: defConfMap.at(k.first).configMap()) {
if (!sysDC.has(l.first)) {
isMatch=false;
break;
}
if (sysDC.getString(l.first,"")!=l.second) {
isMatch=false;
break;
}
}
if (!isMatch) break;
} catch (std::exception& ex) {
auto countI=sysCountMap.find(k.first);
if (countI==sysCountMap.cend()) {
isMatch=false;
break;
} else if (countI->second!=k.second) {
isMatch=false;
break;
}
auto confI=sysConfMap.find(k.first);
if (confI==sysConfMap.cend()) {
isMatch=false;
break;
}
DivConfig& sysDC=confI->second;
auto defConfI=defConfMap.find(k.first);
if (defConfI==defConfMap.cend()) {
isMatch=false;
break;
}
for (std::pair<String,String> l: defConfI->second.configMap()) {
if (!sysDC.has(l.first)) {
isMatch=false;
break;
}
if (sysDC.getString(l.first,"")!=l.second) {
isMatch=false;
break;
}
}
if (!isMatch) break;
}
if (isMatch) {
logV("match found!");
@ -682,11 +698,7 @@ void FurnaceGUI::autoDetectSystem() {
if (k.second>1) {
e->song.systemName+=fmt::sprintf("%d×",k.second);
}
if (k.first==DIV_SYSTEM_N163) {
e->song.systemName+=settings.c163Name;
} else {
e->song.systemName+=e->getSystemName(k.first);
}
e->song.systemName+=e->getSystemName(k.first);
isFirst=false;
}
}
@ -1097,8 +1109,9 @@ void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
}
void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
try {
int key=noteKeys.at(scancode);
auto it=noteKeys.find(scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
@ -1110,7 +1123,6 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
e->synchronized([this,num]() {
e->autoNoteOff(-1,num);
});
} catch (std::out_of_range& e) {
}
}
@ -1310,8 +1322,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
break;
}
} else {
try {
int num=valueKeys.at(ev.key.keysym.sym);
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
switch (latchTarget) {
case 1: // instrument
changeLatch(latchIns);
@ -1326,7 +1339,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
changeLatch(latchEffectVal);
break;
}
} catch (std::out_of_range& e) {
}
}
return;
@ -1339,8 +1351,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
alterSampleMap(true,-1);
return;
}
try {
int key=noteKeys.at(ev.key.keysym.scancode);
auto it=noteKeys.find(ev.key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
@ -1348,7 +1361,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
alterSampleMap(true,num);
return;
} catch (std::out_of_range& e) {
}
} else {
// TODO: map?
@ -1356,34 +1368,35 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
alterSampleMap(false,-1);
return;
}
try {
int num=valueKeys.at(ev.key.keysym.sym);
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
if (num<10) {
alterSampleMap(false,num);
return;
}
} catch (std::out_of_range& e) {
}
}
}
// PER-WINDOW KEYS
switch (curWindow) {
case GUI_WINDOW_PATTERN:
try {
int action=actionMapPat.at(mapped);
case GUI_WINDOW_PATTERN: {
auto actionI=actionMapPat.find(mapped);
if (actionI!=actionMapPat.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
// pattern input otherwise
if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break;
if (!ev.key.repeat) {
if (cursor.xFine==0) { // note
try {
int key=noteKeys.at(ev.key.keysym.scancode);
auto it=noteKeys.find(ev.key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
@ -1392,34 +1405,36 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
if (edit) {
noteInput(num,key);
}
} catch (std::out_of_range& e) {
}
} else if (edit) { // value
try {
int num=valueKeys.at(ev.key.keysym.sym);
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
valueInput(num);
} catch (std::out_of_range& e) {
}
}
}
break;
case GUI_WINDOW_ORDERS:
try {
int action=actionMapOrders.at(mapped);
}
case GUI_WINDOW_ORDERS: {
auto actionI=actionMapOrders.find(mapped);
if (actionI!=actionMapOrders.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
// order input otherwise
if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break;
if (orderEditMode!=0) {
try {
int num=valueKeys.at(ev.key.keysym.sym);
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0;
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;
@ -1439,62 +1454,66 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
}
} catch (std::out_of_range& e) {
}
}
break;
case GUI_WINDOW_SAMPLE_EDIT:
try {
int action=actionMapSample.at(mapped);
}
case GUI_WINDOW_SAMPLE_EDIT: {
auto actionI=actionMapSample.find(mapped);
if (actionI!=actionMapSample.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
break;
case GUI_WINDOW_INS_LIST:
try {
int action=actionMapInsList.at(mapped);
}
case GUI_WINDOW_INS_LIST: {
auto actionI=actionMapInsList.find(mapped);
if (actionI!=actionMapInsList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
break;
case GUI_WINDOW_WAVE_LIST:
try {
int action=actionMapWaveList.at(mapped);
}
case GUI_WINDOW_WAVE_LIST: {
auto actionI=actionMapWaveList.find(mapped);
if (actionI!=actionMapWaveList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
break;
case GUI_WINDOW_SAMPLE_LIST:
try {
int action=actionMapSampleList.at(mapped);
}
case GUI_WINDOW_SAMPLE_LIST: {
auto actionI=actionMapSampleList.find(mapped);
if (actionI!=actionMapSampleList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
break;
}
default:
break;
}
// GLOBAL KEYS
try {
int action=actionMapGlobal.at(mapped);
auto actionI=actionMapGlobal.find(mapped);
if (actionI!=actionMapGlobal.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
} catch (std::out_of_range& e) {
}
}
@ -2980,9 +2999,10 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
if (settings.notePreviewBehavior==0) return 1;
switch (curWindow) {
case GUI_WINDOW_SAMPLE_EDIT:
case GUI_WINDOW_SAMPLE_LIST:
try {
int key=noteKeys.at(ev->key.keysym.scancode);
case GUI_WINDOW_SAMPLE_LIST: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (key!=100 && key!=101 && key!=102) {
int pStart=-1;
@ -3003,13 +3023,14 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
samplePreviewKey=ev->key.keysym.scancode;
samplePreviewNote=num;
}
} catch (std::out_of_range& e) {
}
break;
}
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT:
try {
int key=noteKeys.at(ev->key.keysym.scancode);
case GUI_WINDOW_WAVE_EDIT: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (key!=100 && key!=101 && key!=102) {
e->previewWave(curWave,num);
@ -3017,9 +3038,9 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
wavePreviewKey=ev->key.keysym.scancode;
wavePreviewNote=num;
}
} catch (std::out_of_range& e) {
}
break;
}
case GUI_WINDOW_ORDERS: // ignore here
break;
case GUI_WINDOW_PATTERN:
@ -3029,9 +3050,10 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
if (edit && cursor.xFine!=0) break;
}
// fall-through
default:
try {
int key=noteKeys.at(ev->key.keysym.scancode);
default: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
@ -3040,9 +3062,9 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
if (key!=100 && key!=101 && key!=102) {
previewNote(cursor.xCoarse,num);
}
} catch (std::out_of_range& e) {
}
break;
}
}
}
} else if (ev->type==SDL_KEYUP) {

View File

@ -240,6 +240,8 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_SM8521,
GUI_COLOR_INSTR_PV1000,
GUI_COLOR_INSTR_K053260,
GUI_COLOR_INSTR_SCSP,
GUI_COLOR_INSTR_TED,
GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG,
@ -1419,6 +1421,7 @@ class FurnaceGUI {
int chipNames;
int overflowHighlight;
int partyTime;
int flatNotes;
int germanNotation;
int stepOnDelete;
int scrollStep;
@ -1521,13 +1524,13 @@ class FurnaceGUI {
int pullDeleteRow;
int newSongBehavior;
int memUsageUnit;
int cursorFollowsWheel;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
String audioDevice;
String midiInDevice;
String midiOutDevice;
String c163Name;
String renderBackend;
String renderDriver;
String initialSysName;
@ -1675,13 +1678,13 @@ class FurnaceGUI {
pullDeleteRow(1),
newSongBehavior(0),
memUsageUnit(1),
cursorFollowsWheel(0),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),
audioDevice(""),
midiInDevice(""),
midiOutDevice(""),
c163Name(""),
renderBackend(""),
renderDriver(""),
initialSysName("Sega Genesis/Mega Drive"),
@ -2287,6 +2290,7 @@ class FurnaceGUI {
bool quitRender();
const char* getSystemName(DivSystem which);
const char* getSystemPartNumber(DivSystem sys, DivConfig& flags);
public:
void editStr(String* which);

View File

@ -62,6 +62,42 @@ const char* noteNamesG[180]={
"C-9", "C#9", "D-9", "D#9", "E-9", "F-9", "F#9", "G-9", "G#9", "A-9", "A#9", "H-9"
};
const char* noteNamesF[180]={
"c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "bd5", "b_5",
"c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "bd4", "b_4",
"c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "bd3", "b_3",
"c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "bd2", "b_2",
"c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "bd1", "b_1",
"C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "Bb0", "B-0",
"C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "Bb1", "B-1",
"C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "Bb2", "B-2",
"C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "Bb3", "B-3",
"C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "Bb4", "B-4",
"C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "Bb5", "B-5",
"C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "Bb6", "B-6",
"C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "Bb7", "B-7",
"C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "Bb8", "B-8",
"C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "Bb9", "B-9"
};
const char* noteNamesGF[180]={
"c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "b_5", "h_5",
"c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "b_4", "h_4",
"c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "b_3", "h_3",
"c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "b_2", "h_2",
"c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "b_1", "h_1",
"C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "B-0", "H-0",
"C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "B-1", "H-1",
"C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "B-2", "H-2",
"C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "B-3", "H-3",
"C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "B-4", "H-4",
"C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "B-5", "H-5",
"C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "B-6", "H-6",
"C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "B-7", "H-7",
"C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "B-8", "H-8",
"C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "B-9", "H-9"
};
const char* pitchLabel[11]={
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
};
@ -132,6 +168,8 @@ const char* insTypes[DIV_INS_MAX+1]={
"SM8521",
"PV-1000",
"K053260",
"SCSP",
"TED",
NULL
};
@ -853,6 +891,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)),
D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_SCSP,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_INSTR_TED,"",ImVec4(0.7f,0.6f,1.0f,1.0f)),
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -1037,6 +1077,7 @@ const int availableSystems[]={
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260,
DIV_SYSTEM_TED,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1087,6 +1128,7 @@ const int chipsSquare[]={
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_TED,
0 // don't remove this last one!
};

View File

@ -38,6 +38,8 @@ struct FurnaceGUIColorDef {
extern const int opOrder[4];
extern const char* noteNames[180];
extern const char* noteNamesG[180];
extern const char* noteNamesF[180];
extern const char* noteNamesGF[180];
extern const char* pitchLabel[11];
extern const char* insTypes[];
extern const char* sampleLoopModes[];

View File

@ -267,6 +267,10 @@ const char* msm5232ControlBits[7]={
"16'", "8'", "4'", "2'", "sustain", NULL
};
const char* tedControlBits[3]={
"square", "noise", NULL
};
const char* x1_010EnvBits[8]={
"enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL
};
@ -2305,7 +2309,7 @@ void FurnaceGUI::drawInsEdit() {
ins->type=(DivInstrumentType)insType;
}
*/
if (ImGui::BeginCombo("##Type",insType==DIV_INS_N163?settings.c163Name.c_str():insTypes[insType])) {
if (ImGui::BeginCombo("##Type",insTypes[insType])) {
std::vector<DivInstrumentType> insTypeList;
if (settings.displayAllInsTypes) {
for (int i=0; insTypes[i]; i++) {
@ -2315,7 +2319,7 @@ void FurnaceGUI::drawInsEdit() {
insTypeList=e->getPossibleInsTypes();
}
for (DivInstrumentType i: insTypeList) {
if (ImGui::Selectable(i==DIV_INS_N163?settings.c163Name.c_str():insTypes[i],insType==i)) {
if (ImGui::Selectable(insTypes[i],insType==i)) {
ins->type=i;
// reset macro zoom
@ -4665,30 +4669,79 @@ void FurnaceGUI::drawInsEdit() {
sampleMapFocused=false;
}
}
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) {
if (ImGui::InputInt("Waveform##WAVE",&ins->n163.wave,1,10)) { PARAMETER
if (ins->n163.wave<0) ins->n163.wave=0;
if (ins->n163.wave>=e->song.waveLen) ins->n163.wave=e->song.waveLen-1;
}
if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER
if (ins->n163.wavePos<0) ins->n163.wavePos=0;
if (ins->n163.wavePos>255) ins->n163.wavePos=255;
}
if (ImGui::InputInt("Length##WAVELEN",&ins->n163.waveLen,4,16)) { PARAMETER
if (ins->n163.waveLen<0) ins->n163.waveLen=0;
if (ins->n163.waveLen>252) ins->n163.waveLen=252;
ins->n163.waveLen&=0xfc;
}
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem("Namco 163")) {
bool preLoad=ins->n163.waveMode&0x1;
if (ImGui::Checkbox("Load waveform before playback",&preLoad)) { PARAMETER
if (ImGui::Checkbox("Load waveform",&preLoad)) { PARAMETER
ins->n163.waveMode=(ins->n163.waveMode&~0x1)|(preLoad?0x1:0);
}
bool waveMode=ins->n163.waveMode&0x2;
if (ImGui::Checkbox("Update waveforms into RAM when every waveform changes",&waveMode)) { PARAMETER
ins->n163.waveMode=(ins->n163.waveMode&~0x2)|(waveMode?0x2:0);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, a waveform will be loaded into RAM.\nwhen disabled, only the offset and length change.");
}
if (preLoad) {
if (ImGui::InputInt("Waveform##WAVE",&ins->n163.wave,1,10)) { PARAMETER
if (ins->n163.wave<0) ins->n163.wave=0;
if (ins->n163.wave>=e->song.waveLen) ins->n163.wave=e->song.waveLen-1;
}
}
ImGui::Separator();
P(ImGui::Checkbox("Per-channel wave offset/length",&ins->n163.perChanPos));
if (ins->n163.perChanPos) {
if (ImGui::BeginTable("N1PerChPos",3)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.5f);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.5f);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Ch");
ImGui::TableNextColumn();
ImGui::Text("Offset");
ImGui::TableNextColumn();
ImGui::Text("Length");
for (int i=0; i<8; i++) {
ImGui::PushID(64+i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Dummy(ImVec2(dpiScale,ImGui::GetFrameHeightWithSpacing()));
ImGui::SameLine();
ImGui::Text("%d",i+1);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##pcOff",&ins->n163.wavePosCh[i],1,16)) { PARAMETER
if (ins->n163.wavePosCh[i]<0) ins->n163.wavePosCh[i]=0;
if (ins->n163.wavePosCh[i]>255) ins->n163.wavePosCh[i]=255;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##pcLen",&ins->n163.waveLenCh[i],4,16)) { PARAMETER
if (ins->n163.waveLenCh[i]<0) ins->n163.waveLenCh[i]=0;
if (ins->n163.waveLenCh[i]>252) ins->n163.waveLenCh[i]=252;
ins->n163.waveLenCh[i]&=0xfc;
}
ImGui::PopID();
}
ImGui::EndTable();
}
} else {
if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER
if (ins->n163.wavePos<0) ins->n163.wavePos=0;
if (ins->n163.wavePos>255) ins->n163.wavePos=255;
}
if (ImGui::InputInt("Length##WAVELEN",&ins->n163.waveLen,4,16)) { PARAMETER
if (ins->n163.waveLen<0) ins->n163.waveLen=0;
if (ins->n163.waveLen>252) ins->n163.waveLen=252;
ins->n163.waveLen&=0xfc;
}
}
ImGui::EndTabItem();
}
if (ins->type==DIV_INS_FDS) if (ImGui::BeginTabItem("FDS")) {
@ -5340,7 +5393,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6258) {
volMax=0;
}
if (ins->type==DIV_INS_MSM6295) {
if (ins->type==DIV_INS_MSM6295 || ins->type==DIV_INS_TED) {
volMax=8;
}
if (ins->type==DIV_INS_ADPCMA) {
@ -5426,6 +5479,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="On/Off";
dutyMax=1;
}
if (ins->type==DIV_INS_TED) {
dutyLabel="Square/Noise";
dutyMax=2;
}
if (ins->type==DIV_INS_SWAN) {
dutyLabel="Noise";
dutyMax=ins->amiga.useSample?0:8;
@ -5442,10 +5499,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="Duty";
dutyMax=63;
}
/*if (ins->type==DIV_INS_N163) {
dutyLabel="Waveform pos.";
if (ins->type==DIV_INS_N163) {
dutyLabel="Wave Pos";
dutyMax=255;
}*/
}
if (ins->type==DIV_INS_VRC6) {
dutyLabel="Duty";
dutyMax=ins->amiga.useSample?0:7;
@ -5509,6 +5566,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_K053260) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_TED) waveMax=0;
if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7;
if (ins->type==DIV_INS_PET) {
waveMax=8;
@ -5547,7 +5605,6 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_N163) {
ex1Max=252;
ex2Max=2;
}
if (ins->type==DIV_INS_FDS) {
ex1Max=63;
@ -5655,6 +5712,8 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits));
} else if (ins->type==DIV_INS_POKEY) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,pokeyCtlBits));
} else if (ins->type==DIV_INS_TED) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,80,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,tedControlBits));
} else if (ins->type==DIV_INS_MSM5232) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits));
} else if (ins->type==DIV_INS_ES5506) {
@ -5722,7 +5781,8 @@ void FurnaceGUI::drawInsEdit() {
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_K053260) {
ins->type==DIV_INS_K053260 ||
ins->type==DIV_INS_TED) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ex1Max>0) {
@ -5732,8 +5792,8 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,saaEnvBits));
} else if (ins->type==DIV_INS_X1_010 && !ins->amiga.useSample) {
macroList.push_back(FurnaceGUIMacroDesc("Envelope Mode",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,x1_010EnvBits));
/*} else if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("Wave Length",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));*/
} else if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("Wave Length",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Depth",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SU) {
@ -5755,8 +5815,6 @@ void FurnaceGUI::drawInsEdit() {
if (ex2Max>0) {
if (ins->type==DIV_INS_C64) {
macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER]));
/*} else if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("Wave Update",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));*/
} else if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SU) {
@ -5786,12 +5844,6 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ins->type==DIV_INS_N163) {
/*macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Wave",&ins->std.ex3Macro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Pos",&ins->std.algMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Len",&ins->std.fbMacro,0,252,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Trigger",&ins->std.fmsMacro,0,2,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));*/
}
if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Position",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
}

View File

@ -337,9 +337,11 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -353,9 +355,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -365,9 +368,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -380,9 +384,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -415,22 +420,24 @@ void FurnaceGUI::drawMixer() {
// draw connections
for (unsigned int i: e->song.patchbay) {
if ((i>>20)==selectedPortSet) continue;
try {
ImVec2 portSrc=portPos.at(i>>16);
ImVec2 portDest=portPos.at(0x10000|(i&0xffff));
auto portSrcI=portPos.find(i>>16);
auto portDestI=portPos.find(0x10000|(i&0xffff));
if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) {
ImVec2 portSrc=portSrcI->second;
ImVec2 portDest=portDestI->second;
dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION_BG]),2.0f*dpiScale);
} catch (std::out_of_range& e) {
}
}
// foreground
for (unsigned int i: e->song.patchbay) {
if ((i>>20)!=selectedPortSet) continue;
try {
ImVec2 portSrc=portPos.at(i>>16);
ImVec2 portDest=portPos.at(0x10000|(i&0xffff));
auto portSrcI=portPos.find(i>>16);
auto portDestI=portPos.find(0x10000|(i&0xffff));
if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) {
ImVec2 portSrc=portSrcI->second;
ImVec2 portDest=portDestI->second;
dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION]),2.0f*dpiScale);
} catch (std::out_of_range& e) {
}
}
}

View File

@ -128,8 +128,10 @@ void FurnaceGUI::drawNewSong() {
for (size_t chipIndex=0; chipIndex<chips.size(); chipIndex++) {
DivSystem chip=chips[chipIndex];
const DivSysDef* sysDef=e->getSystemDef(chip);
ImGui::PushTextWrapPos(MIN(scrW*dpiScale,400.0f*dpiScale));
ImGui::Text("%s (x%d): ",sysDef->name,chipCounts[chip]);
ImGui::TextWrapped("%s",sysDef->description);
ImGui::Text("%s",sysDef->description);
ImGui::PopTextWrapPos();
if (chipIndex+1<chips.size()) {
ImGui::Separator();
}

View File

@ -412,7 +412,7 @@ void FurnaceGUI::drawPattern() {
ImGui::SetNextWindowPos(patWindowPos);
ImGui::SetNextWindowSize(patWindowSize);
}
if (ImGui::Begin("Pattern",&patternOpen,globalWinFlags|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0))) {
if (ImGui::Begin("Pattern",&patternOpen,globalWinFlags|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0)|(settings.cursorFollowsWheel?ImGuiWindowFlags_NoScrollWithMouse:0))) {
if (!mobileUI) {
patWindowPos=ImGui::GetWindowPos();
patWindowSize=ImGui::GetWindowSize();
@ -440,7 +440,7 @@ void FurnaceGUI::drawPattern() {
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+centerOff);
}
}
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea)) {
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea|(settings.cursorFollowsWheel?ImGuiTableFlags_NoScrollWithMouse:0))) {
ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed);
char chanID[2048];
float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);
@ -951,9 +951,16 @@ void FurnaceGUI::drawPattern() {
demandScrollX=false;
}
// cursor follows wheel
if (settings.cursorFollowsWheel && (!e->isPlaying() || !followPattern) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
if (wheelX!=0 || wheelY!=0) {
moveCursor(wheelX,wheelY,false);
}
}
// overflow changes order
// TODO: this is very unreliable and sometimes it can warp you out of the song
if (settings.scrollChangesOrder && !e->isPlaying() && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
if (settings.scrollChangesOrder && (!e->isPlaying() || !followPattern) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && !settings.cursorFollowsWheel) {
if (wheelY!=0) {
if (wheelY>0) {
if (ImGui::GetScrollY()<=0) {
@ -962,6 +969,10 @@ void FurnaceGUI::drawPattern() {
setOrder(curOrder-1);
ImGui::SetScrollY(ImGui::GetScrollMaxY());
updateScroll(e->curSubSong->patLen);
} else if (settings.scrollChangesOrder==2) {
setOrder(e->curSubSong->ordersLen-1);
ImGui::SetScrollY(ImGui::GetScrollMaxY());
updateScroll(e->curSubSong->patLen);
}
haveHitBounds=false;
} else {
@ -977,6 +988,10 @@ void FurnaceGUI::drawPattern() {
setOrder(curOrder+1);
ImGui::SetScrollY(0);
updateScroll(0);
} else if (settings.scrollChangesOrder==2) {
setOrder(0);
ImGui::SetScrollY(0);
updateScroll(0);
}
haveHitBounds=false;
} else {

View File

@ -278,6 +278,12 @@ void FurnaceGUI::initSystemPresets() {
ENTRY(
"Commodore VIC-20", {
CH(DIV_SYSTEM_VIC20, 1.0f, 0, "clockSel=1")
},
"tickRate=50"
);
ENTRY(
"Commodore Plus/4", {
CH(DIV_SYSTEM_TED, 1.0f, 0, "")
}
);
ENTRY(
@ -2436,6 +2442,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_PV1000, 1.0f, 0, "")
}
);
ENTRY(
"MOS Technology TED", {
CH(DIV_SYSTEM_TED, 1.0f, 0, "clockSel=1")
}
);
CATEGORY_END;
CATEGORY_BEGIN("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis.");

View File

@ -572,11 +572,6 @@ void FurnaceGUI::drawSettings() {
settings.effectDeletionAltersValue=effectDeletionAltersValueB;
}
bool scrollChangesOrderB=settings.scrollChangesOrder;
if (ImGui::Checkbox("Change order when scrolling outside of pattern bounds",&scrollChangesOrderB)) {
settings.scrollChangesOrder=scrollChangesOrderB;
}
bool stepOnInsertB=settings.stepOnInsert;
if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) {
settings.stepOnInsert=stepOnInsertB;
@ -592,6 +587,11 @@ void FurnaceGUI::drawSettings() {
settings.cursorMoveNoScroll=cursorMoveNoScrollB;
}
bool cursorFollowsWheelB=settings.cursorFollowsWheel;
if (ImGui::Checkbox("Move cursor with scroll wheel",&cursorFollowsWheelB)) {
settings.cursorFollowsWheel=cursorFollowsWheelB;
}
bool doubleClickColumnB=settings.doubleClickColumn;
if (ImGui::Checkbox("Double click selects entire column",&doubleClickColumnB)) {
settings.doubleClickColumn=doubleClickColumnB;
@ -758,6 +758,21 @@ void FurnaceGUI::drawSettings() {
if (ImGui::RadioButton("Yes, and move to next/prev pattern##wrapV2",settings.wrapVertical==2)) {
settings.wrapVertical=2;
}
if (ImGui::RadioButton("Yes, and move to next/prev pattern (wrap around)##wrapV2",settings.wrapVertical==3)) {
settings.wrapVertical=3;
}
ImGui::Text("Change order when scrolling outside of pattern bounds:");
if (ImGui::RadioButton("No##pscroll0",settings.scrollChangesOrder==0)) {
settings.scrollChangesOrder=0;
}
if (ImGui::RadioButton("Yes##pscroll1",settings.scrollChangesOrder==1)) {
settings.scrollChangesOrder=1;
}
if (ImGui::RadioButton("Yes, and wrap around song##pscroll2",settings.scrollChangesOrder==2)) {
settings.scrollChangesOrder=2;
}
ImGui::Text("Cursor movement keys behavior:");
if (ImGui::RadioButton("Move by one##cmk0",settings.scrollStep==0)) {
@ -1638,12 +1653,6 @@ void FurnaceGUI::drawSettings() {
ImGui::Separator();
ImGui::Text("Namco 163 chip name");
ImGui::SameLine();
ImGui::InputTextWithHint("##C163Name",DIV_C163_DEFAULT_NAME,&settings.c163Name);
ImGui::Separator();
ImGui::Text("Channel colors:");
if (ImGui::RadioButton("Single##CHC0",settings.channelColors==0)) {
settings.channelColors=0;
@ -1774,6 +1783,11 @@ void FurnaceGUI::drawSettings() {
settings.viewPrevPattern=viewPrevPatternB;
}
bool flatNotesB=settings.flatNotes;
if (ImGui::Checkbox("Use flats instead of sharps",&flatNotesB)) {
settings.flatNotes=flatNotesB;
}
bool germanNotationB=settings.germanNotation;
if (ImGui::Checkbox("Use German notation",&germanNotationB)) {
settings.germanNotation=germanNotationB;
@ -2067,11 +2081,7 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL,"FM (OPL)");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_FDS,"FDS");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_VBOY,"Virtual Boy");
// special case
String c163Label=fmt::sprintf("%s##CC_GUI_COLOR_INSTR_N163",settings.c163Name);
if (ImGui::ColorEdit4(c163Label.c_str(),(float*)&uiColors[GUI_COLOR_INSTR_N163])) {
applyUISettings(false);
}
UI_COLOR_CONFIG(GUI_COLOR_INSTR_N163,"Namco 163");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SCC,"Konami SCC");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPZ,"FM (OPZ)");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY");
@ -2640,6 +2650,11 @@ void FurnaceGUI::drawSettings() {
settingsOpen=false;
syncSettings();
}
ImGui::SameLine();
if (ImGui::Button("Apply##SettingsApply")) {
settingsOpen=true;
willCommit=true;
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SETTINGS;
ImGui::End();
@ -2662,7 +2677,6 @@ void FurnaceGUI::syncSettings() {
settings.audioChans=e->getConfInt("audioChans",2);
settings.midiInDevice=e->getConfString("midiInDevice","");
settings.midiOutDevice=e->getConfString("midiOutDevice","");
settings.c163Name=e->getConfString("c163Name",DIV_C163_DEFAULT_NAME);
settings.renderDriver=e->getConfString("renderDriver","");
settings.sdlAudioDriver=e->getConfString("sdlAudioDriver","");
settings.audioQuality=e->getConfInt("audioQuality",0);
@ -2696,6 +2710,7 @@ void FurnaceGUI::syncSettings() {
settings.chipNames=e->getConfInt("chipNames",0);
settings.overflowHighlight=e->getConfInt("overflowHighlight",0);
settings.partyTime=e->getConfInt("partyTime",0);
settings.flatNotes=e->getConfInt("flatNotes",0);
settings.germanNotation=e->getConfInt("germanNotation",0);
settings.stepOnDelete=e->getConfInt("stepOnDelete",0);
settings.scrollStep=e->getConfInt("scrollStep",0);
@ -2805,6 +2820,7 @@ void FurnaceGUI::syncSettings() {
settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1);
settings.newSongBehavior=e->getConfInt("newSongBehavior",0);
settings.memUsageUnit=e->getConfInt("memUsageUnit",1);
settings.cursorFollowsWheel=e->getConfInt("cursorFollowsWheel",0);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
@ -2830,13 +2846,14 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.soloAction,0,2);
clampSetting(settings.pullDeleteBehavior,0,1);
clampSetting(settings.wrapHorizontal,0,2);
clampSetting(settings.wrapVertical,0,2);
clampSetting(settings.wrapVertical,0,3);
clampSetting(settings.macroView,0,1);
clampSetting(settings.fmNames,0,2);
clampSetting(settings.allowEditDocking,0,1);
clampSetting(settings.chipNames,0,1);
clampSetting(settings.overflowHighlight,0,1);
clampSetting(settings.partyTime,0,1);
clampSetting(settings.flatNotes,0,1);
clampSetting(settings.germanNotation,0,1);
clampSetting(settings.stepOnDelete,0,1);
clampSetting(settings.scrollStep,0,1);
@ -2874,7 +2891,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.insEditColorize,0,1);
clampSetting(settings.metroVol,0,200);
clampSetting(settings.pushNibble,0,1);
clampSetting(settings.scrollChangesOrder,0,1);
clampSetting(settings.scrollChangesOrder,0,2);
clampSetting(settings.oplStandardWaveNames,0,1);
clampSetting(settings.cursorMoveNoScroll,0,1);
clampSetting(settings.lowLatency,0,1);
@ -2932,6 +2949,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.pullDeleteRow,0,1);
clampSetting(settings.newSongBehavior,0,1);
clampSetting(settings.memUsageUnit,0,1);
clampSetting(settings.cursorFollowsWheel,0,1);
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
@ -3011,7 +3029,6 @@ void FurnaceGUI::commitSettings() {
e->setConf("audioDevice",settings.audioDevice);
e->setConf("midiInDevice",settings.midiInDevice);
e->setConf("midiOutDevice",settings.midiOutDevice);
e->setConf("c163Name",settings.c163Name);
e->setConf("renderDriver",settings.renderDriver);
e->setConf("sdlAudioDriver",settings.sdlAudioDriver);
e->setConf("audioQuality",settings.audioQuality);
@ -3046,6 +3063,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("chipNames",settings.chipNames);
e->setConf("overflowHighlight",settings.overflowHighlight);
e->setConf("partyTime",settings.partyTime);
e->setConf("flatNotes",settings.flatNotes);
e->setConf("germanNotation",settings.germanNotation);
e->setConf("stepOnDelete",settings.stepOnDelete);
e->setConf("scrollStep",settings.scrollStep);
@ -3156,6 +3174,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("pullDeleteRow",settings.pullDeleteRow);
e->setConf("newSongBehavior",settings.newSongBehavior);
e->setConf("memUsageUnit",settings.memUsageUnit);
e->setConf("cursorFollowsWheel",settings.cursorFollowsWheel);
// colors
for (int i=0; i<GUI_COLOR_MAX; i++) {

View File

@ -968,6 +968,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
int clockSel=flags.getInt("clockSel",0);
int channels=flags.getInt("channels",0)+1;
bool multiplex=flags.getBool("multiplex",false);
bool lenCompensate=flags.getBool("lenCompensate",false);
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) {
@ -991,12 +992,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
if (ImGui::Checkbox("Disable hissing",&multiplex)) {
altered=true;
}
if (ImGui::Checkbox("Scale frequency to wave length",&lenCompensate)) {
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("clockSel",clockSel);
flags.set("channels",channels-1);
flags.set("multiplex",multiplex);
flags.set("lenCompensate",lenCompensate);
});
}
break;
@ -1908,6 +1913,40 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
}
break;
}
case DIV_SYSTEM_TED: {
int clockSel=flags.getInt("clockSel",0);
bool keyPriority=flags.getBool("keyPriority",true);
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) {
clockSel=0;
altered=true;
}
if (ImGui::RadioButton("PAL (1.77MHz)",clockSel==1)) {
clockSel=1;
altered=true;
}
ImGui::Text("Global parameter priority:");
if (ImGui::RadioButton("Left to right",!keyPriority)) {
keyPriority=false;
altered=true;
}
if (ImGui::RadioButton("Last used channel",keyPriority)) {
keyPriority=true;
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("clockSel",clockSel);
flags.set("keyPriority",keyPriority);
});
}
break;
}
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:

272
src/gui/sysPartNumber.cpp Normal file
View File

@ -0,0 +1,272 @@
/**
* 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.
*/
#include "gui.h"
const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) {
switch (sys) {
case DIV_SYSTEM_YMU759:
return "YMU759";
break;
case DIV_SYSTEM_SMS:{
int chipType=flags.getInt("chipType",0);
if (chipType==4) {
return "SN76489A";
} else if (chipType==5) {
return "SN76496";
} else if (chipType==6) {
return "8496";
} else if (chipType==7) {
return "PSSJ";//not part number
} else if (chipType==8) {
return "SN94624";
} else if (chipType==9) {
return "SN76494";
} else {
return "SN76489";
}
break;
}
case DIV_SYSTEM_PCE:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "HuC6280A";
} else {
return "HuC6280";
}
}
case DIV_SYSTEM_NES:
return "2A03";
break;
case DIV_SYSTEM_C64_6581:
return "MOS 6581";
break;
case DIV_SYSTEM_C64_8580:
return "MOS 8580";
break;
case DIV_SYSTEM_Y8950:
case DIV_SYSTEM_Y8950_DRUMS:
return "Y8950";
break;
case DIV_SYSTEM_AY8910:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "YM2149(F)";
} else if (chipType==2) {
return "5B";
} else if (chipType==3) {
return "AY-3-8914";
} else {
return "AY-3-8910";
}
break;
}
case DIV_SYSTEM_YM2151:
return "YM2151";
break;
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_CSM:
case DIV_SYSTEM_YM2612_DUALPCM:
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
case DIV_SYSTEM_YM2612_EXT:{
int chipType=0;
if (flags.has("chipType")) {
chipType=flags.getInt("chipType",0);
} else {
chipType=flags.getBool("ladderEffect",0)?1:0;
}
if (chipType==0) {
return "YM3438";
} else if (chipType==2) {
return "YMF276";
} else {
return "YM2612";
}
break;
}
case DIV_SYSTEM_TIA:
return "TIA";
break;
case DIV_SYSTEM_SAA1099:
return "SAA1099";
break;
case DIV_SYSTEM_AY8930:
return "AY8930";
break;
case DIV_SYSTEM_VIC20:
return "VIC";
break;
case DIV_SYSTEM_PET:
return "PET";
break;
case DIV_SYSTEM_VRC6:
return "VRC6";
break;
case DIV_SYSTEM_FDS:
return "FDS";
break;
case DIV_SYSTEM_MMC5:
return "MMC5";
break;
case DIV_SYSTEM_N163:
return "N163";
break;
case DIV_SYSTEM_YM2203:
case DIV_SYSTEM_YM2203_EXT:
case DIV_SYSTEM_YM2203_CSM:
return "YM2203";
break;
case DIV_SYSTEM_YM2608:
case DIV_SYSTEM_YM2608_CSM:
case DIV_SYSTEM_YM2608_EXT:
return "YM2608";
break;
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL_DRUMS:{
int patchSet=flags.getInt("patchSet",0);
if (patchSet==1) {
return "YMF281";
} else if (patchSet==2) {
return "YM2423";
} else if (patchSet==3) {
return "VRC7";
} else {
return "YM2413";
}
break;
}
case DIV_SYSTEM_OPL2:
case DIV_SYSTEM_OPL2_DRUMS:
return "YM3812";
break;
case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL3_DRUMS:
return "YMF262";
break;
case DIV_SYSTEM_OPL4:
case DIV_SYSTEM_OPL4_DRUMS:
return "YMF278";
break;
case DIV_SYSTEM_MULTIPCM:
return "YMW258-F";
break;
case DIV_SYSTEM_RF5C68:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "RF5C164";
} else {
return "RF5C68";
}
break;
}
case DIV_SYSTEM_OPZ:
return "YM2414";
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM";
break;
case DIV_SYSTEM_VRC7:
return "VRC7";
break;
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_CSM:
case DIV_SYSTEM_YM2610B_EXT:
return "YM2610B";
break;
case DIV_SYSTEM_SFX_BEEPER:
case DIV_SYSTEM_SFX_BEEPER_QUADTONE:
return "ZXS Beeper";
break;
case DIV_SYSTEM_SCC:
return "SCC";
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_CSM:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
return "YM2610";
break;
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
return "YM2413";
break;
case DIV_SYSTEM_QSOUND:
return "QSound";
break;
case DIV_SYSTEM_X1_010:
return "X1-010";
break;
case DIV_SYSTEM_BUBSYS_WSG:
return "Konami WSG";
break;
case DIV_SYSTEM_ES5506:
return "ES5506";
break;
case DIV_SYSTEM_SCC_PLUS:
return "SCC+";
break;
case DIV_SYSTEM_SOUND_UNIT:
return "TSU";
break;
case DIV_SYSTEM_MSM6295:
return "MSM6295";
break;
case DIV_SYSTEM_MSM6258:
return "MSM6258";
break;
case DIV_SYSTEM_YMZ280B:
return "YMZ280B";
break;
case DIV_SYSTEM_NAMCO_15XX:
return "C15";
break;
case DIV_SYSTEM_NAMCO_CUS30:
return "C30";
break;
case DIV_SYSTEM_MSM5232:
return "MSM5232";
break;
case DIV_SYSTEM_K007232:
return "K007232";
break;
case DIV_SYSTEM_GA20:
return "GA20";
break;
case DIV_SYSTEM_PCM_DAC:
return "DAC";
break;
case DIV_SYSTEM_SM8521:
return "SM8521";
break;
case DIV_SYSTEM_PV1000:
return "PV-1000";
break;
case DIV_SYSTEM_K053260:
return "K053260";
break;
case DIV_SYSTEM_TED:
return "TED";
break;
default:
return FurnaceGUI::getSystemName(sys);
break;
}
}

View File

@ -191,6 +191,7 @@ TAParamResult pVersion(String) {
printf("- MAME SegaPCM core by Hiromitsu Shioya and Olivier Galibert (BSD 3-clause)\n");
printf("- QSound core by superctr (BSD 3-clause)\n");
printf("- VICE VIC-20 by Rami Rasanen and viznut (GPLv2)\n");
printf("- VICE TED by Andreas Boose, Tibor Biczo and Marco van den Heuvel (GPLv2)\n");
printf("- VERA core by Frank van den Hoef (BSD 2-clause)\n");
printf("- SAASound by Dave Hooper and Simon Owen (BSD 3-clause)\n");
printf("- SameBoy by Lior Halphon (MIT)\n");