Merge branch 'master' into feature/esfm

This commit is contained in:
Kagamiin~ 2023-11-18 09:15:48 -03:00
commit 56d774bb3b
123 changed files with 2732 additions and 707 deletions

View file

@ -110,7 +110,7 @@ option(WITH_DEMOS "Install demo songs" ON)
option(WITH_INSTRUMENTS "Install instruments" ON)
option(WITH_WAVETABLES "Install wavetables" ON)
set(DEPENDENCIES_INCLUDE_DIRS "")
set(DEPENDENCIES_INCLUDE_DIRS extern/IconFontCppHeaders src/icon)
if (ANDROID AND NOT TERMUX)
set(DEPENDENCIES_DEFINES "IS_MOBILE")
@ -812,6 +812,7 @@ src/gui/tutorial.cpp
src/gui/util.cpp
src/gui/waveEdit.cpp
src/gui/volMeter.cpp
src/gui/xyOsc.cpp
src/gui/gui.cpp
)
@ -922,7 +923,6 @@ if (BUILD_GUI)
list(APPEND DEPENDENCIES_INCLUDE_DIRS
extern/imgui_patched
extern/imgui_patched/backends
extern/IconFontCppHeaders
extern/igfd
)
if (WIN32 OR APPLE)

View file

@ -4,11 +4,8 @@
# THE REAL TO-DO LIST
- Amiga's Period Modulation not working
- Song is silent after playing an order after loop point
- Select loaded instrument on open - rewrite because I want a setting...
- re-engineer volume handling? Sound Unit cries at me
- finish status view
- finish auto-clone
once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1

Binary file not shown.

BIN
demos/c64/deadlock.fur Normal file

Binary file not shown.

View file

@ -26,4 +26,6 @@ once familiar with the tracker, look to [9-guides](../9-guides/README.md) for us
## tutorial?
[How to Learn Chiptune Trackers](https://www.youtube.com/watch?v=Q37XuOLz0jw): video tutorial created by Button Masher. covers the basic mechanics of chiptune tracking using Furnace for demonstration.
[Furnace Tutorials](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1): video tutorials created by Spinning Square Waves. be noted that these may not apply to the current version.

View file

@ -55,5 +55,12 @@ everything from the wavetables list applies here also, with the addition of one
samples are saved as standard wave (.wav) files.
- right-clicking the Save button brings up a menu with the following options:
right-clicking the Save button brings up a menu with the following options:
- **save raw sample...**: saves the selected sample as raw data.
right-clicking a sample in the list brings up a menu:
- **make instrument**: creates a new instrument which is set to use the selected sample.
- **duplicate**: makes a copy of the selected sample.
- **replace...**: opens a file dialog to choose a replacement sample.
- **save**: opens a file dialog to choose where to save the sample.
- **delete**: removes the sample.

View file

@ -21,7 +21,8 @@ the following keyboard shortcuts work while on a text field:
these work similar to text fields, but you may only input numbers.
they also usually have `+` and `-` buttons which allow you to increase/decrease the amount when clicked (and rapidly do so when click-holding).
they also usually have `+` and `-` buttons which allow you to increase/decrease the value when clicked (and rapidly do so when holding).
additionally, Ctrl-clicking these buttons may increase/decrease the value by a coarse amount.
## sliders
@ -29,7 +30,8 @@ sliders are used for controlling values in a quick manner by being dragged.
using the scroll wheel while holding Ctrl will change the slider's value by small amounts.
right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field for a short period of time, allowing you to input precise values.
right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn it into a number input field, allowing you to input precise values.
once you click away it will become a slider again.
## windows
@ -37,13 +39,13 @@ right-clicking or Ctrl-clicking or a slider (Command-click on macOS) will turn i
windows may be moved, collapsed, closed or even docked around the workspace.
to move a window, press and hold the mouse button while on title bar or any empty space on it.
to move a window, press and hold the left mouse button while on the title bar or any empty space on it.
then drag your mouse, and release it to stop moving.
to resize a window, drag the bottom right corner (marked by a triangular tab) or the borders.
to collapse a window, click on the triangle in the title bar.
clicking again expands it.
clicking again expands the window.
to close a window, click on the `X` at the top right corner, or select it from the "window" menu.

View file

@ -49,6 +49,7 @@ items in _italic_ don't appear in basic mode and are only available in advanced
- macOS: `~/Library/Application Support/Furnace/backups`
- Linux/other: `~/.config/furnace/backups`
- this directory grows in size as you use Furnace. remember to delete old backups periodically to save space.
- **do NOT rely on the backup system as auto-save!** you should save a restored backup because Furnace will not save backups of backups.
- **exit**: closes Furnace.

View file

@ -47,6 +47,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- **When creating new song**:
- **Display system preset selector**
- **Start with initial system**
- **Default author name**
### Start-up
@ -92,7 +93,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- setting this to a high value increases latency.
- **Exclusive mode**: enables Exclusive Mode, which may offer latency improvements.
- only available on WASAPI devices in the PortAudio backend!
- **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode.
- **Low-latency mode**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode.
- only enable if your buffer size is small (10ms or less).
- **Force mono audio**: use if you're unable to hear stereo audio (e.g. single speaker or hearing loss in one ear).
- **want:** displays requested audio configuration.
@ -104,6 +105,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device.
- this avoids activating Windows' built-in limiter.
- this option shall be enabled when using PortAudio backend with a DirectSound device.
- **DC offset correction**: apply a filter to remove DC bias, where the output is overall above or below zero. default is on.
### Metronome
@ -299,6 +301,9 @@ below all the binds, select a key from the dropdown list to add it. it will appe
- applies when playback is stopped.
- **Don't scroll when moving cursor**
- **Move cursor with scroll wheel**
- **No**
- **Yes**
- **Inverted**
## Appearance

View file

@ -38,18 +38,39 @@ 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:
clicking the `++` at the top left corner of the pattern view pops up a small menu to set view modes:
- **Effect columns/collapse**: displays extra options for collapsing channels and adding/removing effect columns:
- **-**: 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.
- **>**: adds an effect column. if one previously existed, its contents will be preserved.
- **Pattern names**: displays pattern names (per channel). 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.
- **Channel group hints**: display indicators when channels are paired in some way (e.g. OPL3 4-op mode).
- **Visualizer**: during playback, show visual effects in the pattern view.
- also can be toggled by right-clicking on the `++` button.
- **Channel status**: displays icons that indicate activity in the channel. see the "channel status" section below.
to rename and/or hide channels, open [the Channels window](../8-advanced/channels.md) via the window menu.
### channel status
- note status:
- ![note off](status-note-off.png) note off
- ![note on](status-note-on.png) note on
- ![macro released](status-note-on-rel.png) note on but macro released (`REL`)
- ![note released](status-note-off-rel.png) note released (`===`)
- pitch alteration:
- ![no effect](status-pitch-none.png) nothing
- ![pitch up](status-pitch-up.png) pitch slide up
- ![pitch down](status-pitch-down.png) pitch slide down
- ![portamento](status-pitch-porta.png) portamento
- ![arpeggio](status-pitch-arpeg.png) arpeggio
- volume alteration:
- ![no effect](status-volume-none.png) nothing
- ![volume up](status-volume-up.png) volume slide up
- ![volume down](status-volume-down.png) volume slide down
- ![tremolo](status-volume-tremolo.png) tremolo
- other icons may be present depending on the used chips.
## input

View file

@ -154,16 +154,16 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx
W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform |
1 | | AMD | AMD | | | Duty | | FilterMode |
2 | | PMD | PMD | | Envelope | Envelope | | Resonance |
3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special |
A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | |
3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | |
A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | Cutoff |
B | FB | FB | FB | | | Noise AND | | |
C | FMS | FMS | FMS | | | Noise OR | | |
D | AMS | AMS | AMS | | | | | |
4 | OpMask | OpMask | | | | | | Test/Gate |
5 | | | AMD2 | | | | | |
6 | | | PMD2 | | | | | |
7 | | | LFO2Speed | | | | | |
8 | | | LFO2Shape | | | | | |
4 | OpMask | OpMask | | | | | | Special |
5 | | | AMD2 | | | | | Attack |
6 | | | PMD2 | | | | | Decay |
7 | | | LFO2Speed | | | | | Sustain |
8 | | | LFO2Shape | | | | | Release |
ex | SAA1099 | X1-010 | Namco 163 | FDS | Sound Unit | ES5506 | MSM6258 |
---|----------|------------|------------|-----------|------------|-----------|----------|

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

View file

@ -96,24 +96,37 @@ every macro can be defined though one of three methods, selectable with the left
- ![ADSR](macro-button-ADSR.png) **ADSR:** this is a traditional ADSR envelope, defined by the rate of increase and decrease of value over time.
- ![LFO](macro-button-LFO.png) **LFO:** the Low Frequency Oscillator generates a repeating wave of values.
some macros are "bitmap" style. they represent a number of "bits" that can be toggled individually, and the values listed represent the sum of which bits are turned on.
### sequence
![sequence macro editor](macro-seq.png)
![clipped sequence macro editor](macro-seq-clip.png)
![bitmask sequence macro editor](macro-seq-bitmask.png)
the number between the macro type label and the macro type button is the macro length in steps. the `-` and `+` buttons change the length of the macro. start out by adding at least a few steps.
the values of the macro can be drawn in the "bar graph" box.
- arpeggio and pitch macros may have values above or below the visible area; small chevrons will be shown until they are scrolled into view.
- bitmask-style macros show labels for each of their bits, and these are edited as toggles.
just beneath the box is a shorter bar that controls looping.
arpeggio macros have a short bar for setting whether to interpret the values as being "relative" or "fixed".
- by default, values are offsets **relative** to the note.
- if clicked on, a value becomes **fixed** and will be played at its corresponding note without regard to the note entered into the pattern.
- values are counted from `C-0`. for example, a fixed value of 48 produces a `C-4` note.
- fixed values are especially useful for noise instruments with preset periods.
below this is a short bar that controls macro loop and release.
- click to set the start point of a loop; the end point is the last value or release point. it appears as half-height bars. right-click to remove the loop.
- shift-click to set the release point. when played, the macro will hold here until the note is released. it appears as a full-height bar. right-click to remove the release point.
finally, the sequence of values can be directly edited in the text box at the bottom.
- the loop start is entered as a `|`.
- the release point is entered as a `/`.
- in arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). no matter the note entered in the pattern, `@` values will be played at that exact note. this is especially useful for noise instruments with preset periods.
- in arpeggio macros, a value starting with a `@` is a fixed value as described above.
- in bitmask-style macros, the values are added up in binary and converted to decimal. see [the hexadecimal guide](../1-intro/hex.md) for more info.
### ADSR

View file

@ -5,15 +5,14 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet
## C64
- **Waveform**: allows selecting a waveform.
- more than one waveform can be selected at once. in that case logical AND mix of waves will occur.
- more than one waveform can be selected at once. in that case a logical AND mix of waves will occur...
- due to hardware flaws, the mixing is a bit weird and sounds different between the 6581 and the 8580.
- noise is an exception. it cannot be used with any of the other waveforms.
- **Attack**: determines the rising time for the sound. the bigger the value, the slower the attack. (0 to 15).
- **Decay**: determines the diminishing time for the sound. the higher the value, the longer the decay (0 to 15).
- **Sustain**: sets the volume level at which the sound stops decaying and holds steady (0 to 15).
- **Release**: determines the rate at which the sound fades out after note off. the higher the value, the longer the release (0 to 15).
- **Duty**: specifies the width of a pulse wave (0 to 4095).
- **Ring Modulation**: when enabled, the channel's output will be multiplied with the previous channel's.
- **Oscillator Sync**: enables oscillator hard sync. as the previous channel's oscillator finishes a cycle, it resets the period of the channel's oscillator, forcing the latter to have the same base frequency. this can produce a harmonically rich sound, the timbre of which can be altered by varying the synchronized oscillator's frequency.
@ -25,10 +24,9 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet
- **low**: a low-pass filter. the lower the cutoff, the darker the sound.
- **high**: a high-pass filter. higher cutoff values result in a less "bassy" sound.
- **band**: a band-pass filter. cutoff determines which part of the sound is heard (from bass to treble).
- **ch3off**: mutes channel 3 when enabled. not sure why is this part of the chip's design, but it is.
- **ch3off**: mutes channel 3 when enabled. it was originally planned for usage with two registers where program could read current oscillator and envelope outputs, thus making vibrato and SFX generation easier. but who wanted to sacrifice one channel out of three! so aforementioned was just done in software, and the feature was never used.
- multiple filter modes can be selected simultaneously. for example, selecting both "low" and "high" results in a bandstop (notch) filter.
- **Volume Macro is Cutoff Macro**: turns the volume macro in the Macros tab into a filter cutoff macro.
- volume control is global (affects entire chip output), hence the option.
- **Absolute Cutoff Macro**: when enabled, the cutoff macro will go from 0 to 2047, and it will be absolute (in other words, control the cutoff directly rather than being relative).
- **Absolute Duty Macro**: when enabled, the duty macro will go from 0 to 4095.
- **Don't test before new note**: this option disables the one-tick hard reset and test bit before a new note.
@ -37,12 +35,26 @@ the C64 instrument editor consists of two tabs: "C64" to control various paramet
- **Volume**: volume sequence.
- warning: volume sequence is global! this means it controls the chip's volume and therefore affects all channels.
- this macro becomes **Cutoff** when the **Volume Macro is Cutoff Macro** option is enabled in the C64 tab.
- **Arpeggio**: pitch sequence.
- **Duty**: pulse width sequence.
- **Waveform**: select the waveform used by instrument.
- **Pitch**: fine pitch.
- **Cutoff**: filter cutoff.
- **Filter mode**: select the filter mode.
- **Resonance**: filter resonance sequence.
- **Special**: ring and oscillator sync selector.
- **Test/Gate**: when on, the test bit is set, which mutes the channel.
- **Special**: ring and oscillator sync selector, as well as:
- **gate bit**:
- set (1): key on. if previous state was 0 it triggers envelope start/restart; if previous state was 1, it does nothing.
- reset (0): key off. if previous state was 1 it triggers envelope release; if previous state was 0, it does nothing.
- **test bit**:
- set (1): immediately mute channel
- if the channel is a source of ring mod and/or hard sync, those stop working until the bit is reset.
- reset (0): unmute channel and restore ring mod/hard sync.
- **Attack**: sets envelope attack speed.
- if you modify attack speed when the envelope is in attack phase it immediately changes.
- **Decay**: sets envelope decay speed.
- if you modify decay speed when envelope is in decay phase it immediately changes.
- **Sustain**: sets envelope sustain level.
- if you modify sustain level when envelope is in sustain phase it immediately changes, although you can only go down. for example, 9-to-8 and 8-to-8 both work, but 8-to-9 immediately mutes the channel.
- **Release**: sets envelope release speed.
- if you modify release speed when envelope is in release phase it immediately changes.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -159,3 +159,14 @@ in there, you can modify certain data pertaining to your sample, such as the:
- **Create instrument from sample**: creates a new instrument with its sample set to the current sample.
- **Zoom**: shows and sets sample view zoom level.
- **Zoom mode**: switches between "Auto" (entire sample fits in window) and "100%" (each horizontal pixel represents one sample point).
in the sample viewer:
- left-click and drag to select a region of the sample.
- right-click to display a menu:
- **cut**: puts the selection in the sample clipboard and deletes it from the sample.
- **copy**: copies the selection into the sample clipboard.
- **paste**: inserts the sample clipboard at the start of the selection.
- **paste (replace)**: replaces the selection with the sample clipboard.
- **paste (mix)**: mixes the sample clipboard into the existing sample, beginning at the start of the selection.
- **set loop to selection**: changes loop region to match selection.
- **create wavetable from selection**: copies the selection into a new wavetable entry.

View file

@ -6,6 +6,28 @@ in this very computer music trackers were born...
imported MOD files use this chip, and will set A-4 tuning to 436.
## amplitude/period modulation
Amiga has support for (rather primitive) amplitude and period (frequency) modulation.
however, nobody has used this feature as it is rather useless, not well-documented and works in a complicated way.
Amiga sample playback is done by two chips: Paula (the one that you probably know) and Agnus (the one that actually feeds Paula with samples).
Agnus has several DMA (direct memory access) units which read from chip memory independent of the CPU. four of these DMA units are used for samples.
when DMA is enabled, Paula requests sample data from Agnus, and then plays these samples back.
there's a catch though. since the data bus is 16-bit, Paula requests **two** 8-bit samples at once! this explains why:
- the sample length registers are in words rather than bytes (thereby allowing samples up to 131070 in length)
- the maximum playback rate (31250Hz PAL; ~31469Hz NTSC) is two times the HBlank rate (Agnus fetches samples on HBlank, around 15625Hz on PAL or ~15734Hz on NTSC)
during normal sample playback, the first sample is output and then the second. afterwards, two more samples are fetched, and so on.
now, when amplitude or period modulation are enabled, things work differently.
the channel is silenced, and the two 8-bit samples are **treated as a big-endian 16-bit number**, which is then written to the next channel's volume or period.
in the case of amplitude modulation, only the second sample is significant because the volume register uses 7 bits (to represent 0 to 64 (65 to 127 are treated as 64)) and the other bits are ignored.
in the case of period modulation, both samples are significant. the first sample is the upper byte, and the second is the lower byte.
## effects
- `10xx`: **toggle low-pass filter.** `0` turns it off and `1` turns it on.

View file

@ -7,6 +7,13 @@ this chip features:
- stereo soft panning
- accepts either 8-bit PCM or proprietary 8-bit µ-law compressed PCM samples
## sample memory notice
this chip is rather unique when it comes to sample memory. be sure to read this notice.
the channels are in groups of four. a sample bank (128KB) may be selected for each group.
if a sample that is on a different bank plays in a group, the group is switched to that bank, and other channels will be silenced.
## effects
- `11xx`: **set noise mode.**

View file

@ -68,3 +68,14 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro
## info
this chip uses the [C64](../4-instrument/c64.md) instrument editor.
## channel status
the following icons are displayed when channel status is enabled in the pattern view:
- channel is silent:
- ![not muted](status-C64-none.png) it's not
- ![gate bit disabled](status-C64-gate-off.png) gate bit disabled
- ![gate bit disabled and test bit enabled](status-C64-gate-off-test-on.png) gate bit disabled and test bit enabled
- ![text bit enabled](status-C64-test-on.png) test bit enabled
- ![ch3off enabled in filter mode](status-C64-ch3off.png) ch3off enabled in filter mode

View file

@ -19,8 +19,16 @@ furthermore, it has some PCM and LFO!
- when LFO is enabled, channel 2 is muted and its output is passed to channel 1's frequency.
- `13xx`: **set LFO speed.**
- `17xx`: **toggle LEGACY sample mode.**
- **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.**
- **this effect exists only for compatibility reasons! its use is NOT recommended. use Sample type instruments instead.**
## info
this chip uses the [PC Engine](../4-instrument/pce.md) instrument editor.
## channel status
the following icons are displayed when channel status is enabled in the pattern view:
- noise mode (channels 5 and 6 only):
- ![noise mode off](status-PCE-noise-off.png) off
- ![noise mode on](status-PCE-noise-on.png) on

View file

@ -65,6 +65,20 @@ Furnace also allows the SNES to use wavetables (and the wavetable synthesizer) i
this chip uses the [SNES](../4-instrument/snes.md) instrument editor.
## channel status
the following icons are displayed when channel status is enabled in the pattern view:
- envelope/gain status:
- ![direct gain, envelope off](status-SNES-gain-direct.png) direct gain
- ![linear gain decrease](status-SNES-gain-dec-lin.png) linear gain decrease
- ![logarithmic gain decrease](status-SNES-gain-dec-log.png) logarithmic gain decrease
- ![linear gain increase](status-SNES-gain-inc-lin.png) linear gain increase
- ![bent-line gain increase](status-SNES-gain-inc-bent.png) bent-line gain increase
- ![envelope attack](status-SNES-env-A.png) envelope attack
- ![envelope decay](status-SNES-env-D.png) envelope decay
- ![envelope sustain](status-SNES-env-S.png) envelope sustain
- ![envelope release](status-SNES-env-R.png) envelope release
## ADSR

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

View file

@ -24,3 +24,17 @@ it has 4 wavetable channels. some of them have additional capabilities:
## info
this chip uses the [WonderSwan](../4-instrument/wonderswan.md) instrument editor.
## channel status
the following icons are displayed when channel status is enabled in the pattern view:
- PCM mode (channel 2):
- ![PCM mode off](status-Swan-PCM-off.png) off
- ![PCM mode on](status-Swan-PCM-on.png) on
- sweep (channel 3):
- ![sweep mode off](status-Swan-sweep-off.png) disabled
- ![sweep mode on](status-Swan-sweep-on.png) enabled
- noise mode (channel 4):
- ![noise mode off](status-Swan-noise-off.png) off
- ![noise mode on](status-Swan-noise-on.png) on

View file

@ -18,8 +18,13 @@ as listed in the "Window" menu:
- [piano](piano.md)
- [oscilloscope](osc.md)
- [oscilloscope (X-Y)](xyosc.md)
- [oscilloscopes (per-channel)](chanosc.md)
- [clock](clock.md)
- [register view](regview.md)
- [log viewer](log-viewer.md)
- [stats](stats.md)
other:
- [command line usage](command-line.md)

View file

@ -5,8 +5,8 @@ the "Channels" dialog allows manipulation of the song's channels.
![channels dialog](channels.png)
each channel has the following options:
- **Visible**: uncheck the box to hide the channel from view. pattern data will be kept.
- **Visible**: uncheck the box to hide the channel from the pattern view. pattern data will be kept.
- crossed-arrows button: click and drag to rearrange pattern data throughout the song.
- note: this does _not_ move channels around within a chip! it only affects pattern data.
- **Name** is the name displayed at the top of each channel in the tracker view.
- to the right of that is the abbreviation used above each channel in the order view.
- note: this does **not** move channels around! it only moves the channel's pattern data.
- **Name**: the name displayed at the top of each channel in the pattern view.
- the next setting is "short name", which is displayed in the orders view and/or when a channel is collapsed.

View file

@ -1,25 +1,24 @@
# oscilloscope (per-channel)
the "Oscilloscope (per-channel)" dialog shows an individual oscilloscope for each channel during playback.
the "Oscilloscope (per-channel)" windows displays several oscilloscope views (one per channel).
![oscilloscope per-channel configuration view](chanosc.png)
right-clicking within the view will change it to the configuration view shown above:
- **Columns**: arranges oscilloscopes into this many columns.
- **Size (ms)**: sets what length of audio is visible in each oscilloscope.
- **Center waveform**: does its best to latch the waveform to the channel's note frequency and centers the display.
right-clicking the view will display the configuration view shown above:
- **Columns**: sets the amount of columns for the oscilloscope views.
- **Size (ms)**: sets how much of a channel's output is captured for the oscilloscope view.
- **Center waveform**: when enabled, the displayed waveforms will be centered using an auto-correlation algorithm.
- **Automatic columns**: sets the number of columns based on the number of channels.
- **Off**: use the Columns setting.
- **Mode 1**: always fewer columns than rows.
- **Mode 2**: bias slightly toward more columns.
- **Mode 3**: always more columns than rows.
- **Amplitude**: scales amplitude for all oscilloscopes.
- **Gradient**: see below.
- the color selector sets the color for all waveforms. right-clicking on it pops up an option dialog:
- **Amplitude**: scales amplitude for all oscilloscope views.
- **Gradient**: this allows you to use a gradient for determining the waveforms' colors instead of a single color. see the gradient section for more information.
- if this option is off, a color selector will be displayed. right-click on it for some options:
- select between the square selector and the color wheel selector.
- **Alpha bar**: adds a transparency selector.
- the boxes below that are for selecting colors numerically by red-green-blue-alpha, hue-saturation-value-alpha, and HTML-style RGBA in hex.
- **Text format**: this string determins what text is shown in the top-left of each oscilloscope. it can be any text, and the following shortcodes will be replaced with information about the channel:
- **Alpha bar**: display an opacity bar.
- **Text format**: this allows you to display some text on each oscilloscope view. the following codes may be used:
- `%c`: channel name
- `%C`: channel short name
- `%d`: channel number (starting from 0)
@ -35,27 +34,32 @@ right-clicking within the view will change it to the configuration view shown ab
- `%V`: volume (percentage)
- `%b`: volume (hex)
- `%%`: percent sign
- the OK button returns from options view to the oscilloscopes.
click on OK to return to the main view.
## gradient
![oscilloscope per-channel gradient configuration view](chanosc-gradient.png)
in this mode, the color selector is replaced by a square field onto which circular "stops" can be placed. each stop adds a soft circle of color. the resulting image is used to look up the oscilloscope color as determined by each axis.
when enabling the Gradient setting, a gradient view is displayed in where circular "points" can be placed.
each point adds a color spot.
the resulting image is used to look up the waveform's color as determined by each axis.
- right-click to place a stop.
- left-click on a stop to change its color. the color selector is the same as above, with two additions:
- right-click to place a point.
- left-click on a point to change its color:
- a color picker is displayed, alongside two settings.
- **Distance**: the size of the circle.
- **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the target color.
- **Spread**: the size of the solid center of the circle. increasing it fills more of the circle with the color.
- middle-click on a point to delete it.
- **Background**: sets background color for entire field.
- **X Axis**: determines what the horizontal maps to, from left to right.
- **Y Axis**: determines what the vertical maps to. from bottom to top. these can be set to the following:
- **None (0%)**: stays at the left or bottom.
- **None (50%)**: stays at the center.
- **None (100%)**: stays at the right or top.
- **Frequency**: changes color with note frequency.
- **Volume**: changes color with volume.
- **Channel**: changes color based on channel number.
- **Brightness**: currently does nothing.
- **Note Trigger**: changes color when a new note is played.
- **Background**: sets the gradient's background color.
- **X Axis**: determines what the horizontal axis maps to.
- **Y Axis**: determines what the vertical axis maps to. these can be set to the following:
- **None (0%)**: always left or bottom
- **None (50%)**: always center
- **None (100%)**: always right or top
- **Frequency**: changes color with frequency
- **Volume**: changes color with volume
- **Channel**: changes color based on channel number (first channel is 0% and last is 100%)
- **Brightness**: currently does nothing
- **Note Trigger**: changes color when a new note is played

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 639 B

View file

@ -1,15 +1,15 @@
# chip manager
the **chip manager** window does exactly what it says.
the **chip manager** window allows you to manage chips, including adding, changing, moving or removing them.
![chip manager](chip-manager.png)
**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. if turned off, pattern data will rearrange to match (the default, and usually the desired behavior).
**Preserve channel order**: make existing pattern data stay in place even when chips are rearranged. when turned off, pattern data will be arranged to match (the default, and usually desired behavior).
to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to its left.
to move a chip around, click and drag the ![crossed-arrows](chip-manager-move.png) button to the left.
to replace a chip with a different one, click the ![down-angle](chip-manager-change.png) and select the replacement.
to replace a chip with a different one, click the **Change** button. this will display the chip selector.
to remove a chip entirely, click the ![X](chip-manager-remove.png) button.
to remove a chip, click the ![X](chip-manager-remove.png) button.
click a chip's name to open its options, where one can set clock rate, chip variant, and other specifics.
click on a chip's name to open chip configuration. this allows you to change chip options, such as clock rate, chip type and so on.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View file

@ -0,0 +1,100 @@
# command line usage
## NAME
Furnace - a chiptune tracker
## SYNOPSIS
`furnace [params...] [file]`
## DESCRIPTION
Furnace is a chiptune tracker that supports many systems and sound chips from the 8/16-bit era.
even though it is primarily controlled by using its graphical user interface, Furnace also offers a command line interface, which is described here.
## USAGE
starting Furnace without arguments will start the graphical user interface (GUI), as long as Furnace has been compiled with GUI enabled.
passing the path to a file will open that file at start-up. if Furnace cannot open that file, it will report an error and quit.
the following parameters may be used:
**general**
- `-help`: display the following help.
- `-console`: enable command-line interface (CLI) player.
- see the COMMAND LINE INTERFACE section for more information
- `-loglevel <level>`: set the logging level to one of the following:
- `error`: critical errors only
- `warning`: errors and warnings
- `info`: errors, warnings, and useful information
- `debug`: all of the above, including debug information
- `trace`: like debug, but with even more details (default)
- `-info`: get information about a song.
- you must provide a file, otherwise Furnace will quit.
- `-version`: display version information.
- `-warranty`: view warranty disclaimer.
**engine**
- `-audio sdl|jack|portaudio`: override audio backend to one of the following:
- `sdl`: SDL (default)
- `jack`: JACK Audio Connection Kit
- `portaudio`: PortAudio
- `-view <type>`: set visualization of data to one of the following:
- `pattern`: order and pattern
- `commands`: engine commands
- `nothing`: guess (default)
- `-loops <count>`: set number of loops
- `-1` means loop forever.
- `-subsong <number>`: set sub-song to play.
- `-safemode`: enable safe mode (software rendering without audio).
- `-safeaudio`: enable safe mode (software rendering with audio).
- `-benchmark render|seek`: run performance test and output total time.
- `render`: measure render time
- `seek`: measure time to seek through the entire song
- you must provide a file, otherwise Furnace will quit.
**audio export**
- `-output path`: export audio in .wav format to `path`.
- you must provide a file, otherwise Furnace will quit.
- `-outmode one|persys|perchan`: set audio export output mode.
- `one`: single file (default)
- `persys`: one file per chip (`_sXX` will be appended to file name, where `XX` is the chip number)
- `perchan`: one file per channel (`_cXX` will be appended to file name, where `XX` is the channel number)
**VGM export**
- `-vgmout path`: output VGM data to `path`.
- you must provide a file, otherwise Furnace will quit.
- `-direct`: enable VGM export direct stream mode.
- this mode is useful for DualPCM export.
- note that this will increase file size by a huge amount!
**export (other)**
- `-zsmout path`: output Zsound Music data for Commander X16.
- you must provide a file, otherwise Furnace will quit.
- `-cmdout path`: output command stream dump to `path`.
- you must provide a file, otherwise Furnace will quit.
- `-binary`: set command stream output format to binary.
## COMMAND LINE INTERFACE
Furnace provides a command-line interface (CLI) player which may be activated through the `-console` option.
the following controls may be used:
- `Left`/`H`: go to previous order.
- `Right`/`L`: go to next order.
- `Space`: pause/resume playback.
## SEE ALSO
the Furnace user manual in the `manual.pdf` file.

View file

@ -4,4 +4,4 @@
comments, credits, or any arbitrary text may be entered here. it has no effect on the song.
there is no word wrap; long lines must be broken manually with the Enter key.
there is no word wrap; long lines must be broken manually with the Enter/Return key.

View file

@ -1,5 +1,5 @@
# compatibility flags
the **Compatibility Flags** window contains several tabs full of settings that change aspects of tracking and playback. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options.
the **Compatibility Flags** window contains several tabs full of settings that change playback behavior. a new Furnace song will have these disabled, while opening a DefleMask module, .mod, or earlier Furnace file will automatically set the appropriate options.
hovering over most options will bring up additional info about them. it is not recommended to change any of these, especially the ones in the DefleMask and Old Furnace sections.

View file

@ -1,9 +1,6 @@
# grooves
grooves are macros for speed.
a **groove** is the equivalent of repeating `0Fxx` commands on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3...
a **groove** is the equivalent of repeating `0Fxx` effects on each row to get a cycle of speeds. for example, a groove of "6 4 5 3" makes the first row 6 ticks long, the next row 4 ticks, then 5, 3, 6, 4, 5, 3...
![groove](groove.png)
@ -13,18 +10,17 @@ to set the song's groove:
- click again so it becomes "Groove".
- enter a sequence of up to 16 speeds.
![groove patterns](grooves.png)
the "Grooves" window is for entering preset groove patterns.
- the **`+`** button adds a new groove pattern; click in the pattern to edit it.
the "Grooves" window displays the list of groove patterns in the song.
- the **`+`** button adds a new groove pattern; click in the groove pattern to edit it.
- the **`×`** buttons remove them.
a single `09xx` command will switch to the matching numbered groove pattern.
## BPM
## tempo
this is a non-exhaustive list of grooves and their equivalent BPM.
this is a non-exhaustive list of grooves and their equivalent tempo in BPM.
note: this table assumes a song's tick rate setting is left at its default value for the chosen engine speed: 60 for NTSC, or 50 for PAL.

View file

@ -1,9 +1,9 @@
# oscilloscope
the Oscilloscope shows the waveform of the mix of all currently playing sounds.
the Oscilloscope shows the audio output as a waveform.
![oscilloscope view](osc.png)
right-clicking on the oscilloscope toggles the adjustment sliders:
- waveform height zoom
- width of viewed audio (window size) in milliseconds.
right-clicking on the oscilloscope toggles adjustment sliders:
- waveform height (zoom)
- window size (how much of the output to display) in milliseconds.

19
doc/8-advanced/xyosc.md Normal file
View file

@ -0,0 +1,19 @@
# oscilloscope (X-Y)
also called "vectorscope", this is similar to the normal oscilloscope in that it draws audio output as a waveform, but instead of being one-dimensional and going from left to right, it is plotted in two dimensions (usually X represents left output and Y represents right output).
(TODO: image)
right-clicking the X-Y oscilloscope window displays settings:
- **X Channel**: the output channel which will affect the X (horizontal) axis. default is 1 (left).
- **Invert**: flips the axis, therefore flipping the view horizontally.
- **Y Channel**: the output channel which will affect the Y (vertical) axis. default is 2 (right).
- **Invert**: flips the axis, therefore flipping the view vertically.
- **Zoom**: changes amplitude, making the plot bigger or smaller.
- **Samples**: the maximum number of samples to use for plotting.
- this setting may be replaced by another or removed in a future version.
- **Decay Time (ms)**: sets how long it takes for the points/lines to fade away.
- **Intensity**: changes the brightness of the dots/lines.
- **Line Thickness**: sets how thick lines are.
click **OK** to return to the oscilloscope view.

View file

@ -234,6 +234,7 @@ size | description
| - 0xce: Namco C140 - 24 channels
| - 0xcf: Namco C219 - 16 channels
| - 0xd0: Namco C352 - 32 channels (UNAVAILABLE)
| - 0xd1: ESFM - 18 channels (UNAVAILABLE)
| - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels
| - 0xfc: Pong - 1 channel
@ -633,7 +634,9 @@ size | description
1 | osc sync
1 | to filter
1 | init filter
1 | vol macro is cutoff
1 | vol macro is cutoff (<187) or reserved
| - from version 187 onwards, volume and cutoff macros are separate.
| - if this is on and the version is less than 187, move the volume macro into the ALG one.
1 | resonance
1 | low pass
1 | band pass
@ -1121,6 +1124,24 @@ size | description
- `val[14]`: loop
- `val[15]`: global (not sure how will I implement this)
## C64 compatibility note (>=187)
in Furnace dev187 the volume and cutoff macros have been separated, as noted above.
however, there are two other changes as well: **inverted relative (non-absolute) cutoff macro**; and a new, improved Special macro.
if version is less than 187, you must convert the Special macro:
1. do not continue if ex4 is not a Sequence type macro!
2. move bit 0 of ex4 macro data into bit 3.
3. set bit 0 on all steps of ex4 macro to 1.
4. if ex3 is not a Sequence type macro, stop here.
5. if ex3 macro length is 0, stop here.
6. merge the ex3 macro (former Special) into ex4 (former Test).
- use the largest size (between ex3 and ex4).
- if the ex3 macro is shorter than the ex4 one, use the last value of ex3, and vice-versa.
- if the ex4 macro length is 0, expand it to the largest size, and set all steps to 1.
don't worry about loop or release...
# wavetable
```

View file

@ -295,7 +295,9 @@ size | description
1 | flags 1
| - bit 7: dutyIsAbs
| - bit 6: initFilter
| - bit 5: volIsCutoff
| - bit 5: volIsCutoff (<187)
| - from version 187 onwards, volume and cutoff macros are separate.
| - if this is on and the version is less than 187, move the volume macro into the ALG one.
| - bit 4: toFilter
| - bit 3: noise on
| - bit 2: pulse on
@ -322,6 +324,24 @@ size | description
| - bit 0-10: cutoff
```
## C64 compatibility note (>=187)
in Furnace dev187 the volume and cutoff macros have been separated, as noted above.
however, there are two other changes as well: **inverted relative (non-absolute) cutoff macro**; and a new, improved Special macro.
if version is less than 187, you must convert the Special macro:
1. do not continue if ex4 is not a Sequence type macro!
2. move bit 0 of ex4 macro data into bit 3.
3. set bit 0 on all steps of ex4 macro to 1.
4. if ex3 is not a Sequence type macro, stop here.
5. if ex3 macro length is 0, stop here.
6. merge the ex3 macro (former Special) into ex4 (former Test).
- use the largest size (between ex3 and ex4).
- if the ex3 macro is shorter than the ex4 one, use the last value of ex3, and vice-versa.
- if the ex4 macro length is 0, expand it to the largest size, and set all steps to 1.
don't worry about loop or release...
# Game Boy data (GB)
```

View file

@ -21,7 +21,7 @@ OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1691897631
ModificationTime: 1697616170
ModificationTime: 1698523374
PfmFamily: 81
TTFWeight: 400
TTFWidth: 5
@ -53,7 +53,7 @@ FitToEm: 0
WinInfo: 57552 16 10
BeginPrivate: 0
EndPrivate
BeginChars: 65536 75
BeginChars: 65536 95
StartChar: space
Encoding: 32 32 0
@ -5717,49 +5717,49 @@ SplineSet
635 935 l 5
203 935 l 5
203 1027 l 5
118 675 m 29
118 578 l 29
955.375 578 l 5
1029.47460938 364.203125 l 5
1080.17480469 426.109375 l 5
1142.57519531 257.4921875 l 5
1191.32519531 371.125976562 l 5
1238.125 165.8515625 l 5
1300.52539062 393.122070312 l 5
1359.02539062 132.860351562 l 5
1417.52539062 217.170898438 l 5
1472.125 77.8740234375 l 5
1497.47460938 184.1796875 l 5
1546.22460938 -39.423828125 l 5
1561.82519531 106.947265625 l 5
1616.42480469 -193.381835938 l 5
1671.27539062 67.0830078125 l 5
1710.02539062 557.821289062 l 5
1710.02539062 695.178710938 l 5
1671.27539062 1185.91699219 l 5
1616.42480469 1446.38183594 l 5
1561.82519531 1109.14160156 l 5
1546.22460938 1292.42382812 l 5
1497.47460938 1068.8203125 l 5
1472.125 1175.12597656 l 5
1417.52539062 1035.82910156 l 5
1359.02539062 1120.13964844 l 5
1300.52539062 859.877929688 l 5
1238.125 1087.1484375 l 5
1191.32519531 881.874023438 l 5
1142.57519531 995.5078125 l 5
1080.17480469 826.890625 l 5
1029.47460938 888.796875 l 5
955.375 675 l 5
118 675 l 29
805 318 m 5
373 318 l 5
373 478 l 5
83 272 l 5
373 66 l 5
373 226 l 5
805 226 l 5
805 318 l 5
118 675 m 25
118 578 l 25
955.375 578 l 1
1029.47460938 364.203125 l 1
1080.17480469 426.109375 l 1
1142.57519531 257.4921875 l 1
1191.32519531 371.125976562 l 1
1238.125 165.8515625 l 1
1300.52539062 393.122070312 l 1
1359.02539062 132.860351562 l 1
1417.52539062 217.170898438 l 1
1472.125 77.8740234375 l 1
1497.47460938 184.1796875 l 1
1546.22460938 -39.423828125 l 1
1561.82519531 106.947265625 l 1
1616.42480469 -193.381835938 l 1
1671.27539062 67.0830078125 l 1
1710.02539062 557.821289062 l 1
1710.02539062 695.178710938 l 1
1671.27539062 1185.91699219 l 1
1616.42480469 1446.38183594 l 1
1561.82519531 1109.14160156 l 1
1546.22460938 1292.42382812 l 1
1497.47460938 1068.8203125 l 1
1472.125 1175.12597656 l 1
1417.52539062 1035.82910156 l 1
1359.02539062 1120.13964844 l 1
1300.52539062 859.877929688 l 1
1238.125 1087.1484375 l 1
1191.32519531 881.874023438 l 1
1142.57519531 995.5078125 l 1
1080.17480469 826.890625 l 1
1029.47460938 888.796875 l 1
955.375 675 l 1
118 675 l 25
805 318 m 1
373 318 l 1
373 478 l 1
83 272 l 1
373 66 l 1
373 226 l 1
805 226 l 1
805 318 l 1
EndSplineSet
EndChar
@ -6028,7 +6028,7 @@ EndChar
StartChar: uniE144
Encoding: 57668 57668 74
Width: 1792
Flags: HWO
Flags: HW
LayerCount: 2
Fore
SplineSet
@ -6056,5 +6056,681 @@ SplineSet
1551.17675781 680.580078125 1569.90136719 713.986328125 1612.40039062 713.986328125 c 0
EndSplineSet
EndChar
StartChar: uniE145
Encoding: 57669 57669 75
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
522.990195527 -133 m 4
522.990195527 -160.625937316 500.62567195 -182.990195527 473 -182.990195527 c 6
120 -182.990195527 l 6
92.3740626842 -182.990195527 70.009804473 -160.62567195 70.009804473 -133 c 4
70.009804473 -105.374062684 92.3743280499 -83.009804473 120 -83.009804473 c 6
473 -83.009804473 l 6
500.625937316 -83.009804473 522.990195527 -105.37432805 522.990195527 -133 c 4
120 10.009804473 m 4
92.3740626842 10.009804473 70.009804473 32.3743280499 70.009804473 60 c 4
70.009804473 64.8134431008 70.699727686 69.6268862016 72.079574112 74.2784931335 c 6
425.079574112 1264.27849313 l 6
431.263927162 1285.12659548 450.525009059 1299.99019553 473 1299.99019553 c 4
479.835355796 1299.99019553 499.699888713 1298.26011808 513.435208596 1279.42115066 c 6
899.438504977 749.990195527 l 5
1295 749.990195527 l 6
1301.13102947 749.990195527 1323.80711626 748.43031074 1337.51061627 726.339026088 c 6
1734.51061627 86.3390260882 l 6
1739.49700244 78.300519415 1741.99019553 69.1502597075 1741.99019553 60 c 4
1741.99019553 32.3740626842 1719.62567195 10.009804473 1692 10.009804473 c 4
1685.86897053 10.009804473 1663.19288374 11.5696892605 1649.48938373 33.6609739118 c 6
1267.16049977 650.009804473 l 5
874 650.009804473 l 6
867.164644204 650.009804473 847.300111287 651.739881916 833.564791404 670.578849337 c 6
492.17532648 1138.81876382 l 5
167.920425888 45.7215068665 l 6
161.736072838 24.8734045156 142.474990941 10.009804473 120 10.009804473 c 4
291 1363.99509776 m 4
304.812968658 1363.99509776 315.995097764 1352.81283598 315.995097764 1339 c 4
315.995097764 1336.72748524 302.589006251 1222.55285612 289.855072456 1113.33612268 c 4
289.58961524 1110.82609608 288.953231482 1108.42736209 288.001699464 1106.19569974 c 4
285.999860964 1101.49353775 282.607669759 1097.55307198 278.356613679 1094.86460664 c 4
274.495333198 1092.4207025 269.916875116 1091.00599258 265.005682215 1091.00490287 c 6
265 1091.00490224 l 6
261.605274126 1091.00490224 258.210548251 1091.69122514 255.042904102 1093.06387093 c 6
45.042904102 1184.06387093 l 6
36.1517873883 1187.91668818 30.0049022365 1196.77638349 30.0049022365 1207 c 4
30.0049022365 1220.81296866 41.187164025 1231.99509776 55 1231.99509776 c 4
58.3947258743 1231.99509776 61.7894517486 1231.30877486 64.957095898 1229.93612907 c 6
204.490746756 1169.47154703 l 5
41.4497078318 1446.31845966 l 6
39.1531707682 1450.21802507 38.0049022365 1454.60901253 38.0049022365 1459 c 4
38.0049022365 1472.81296866 49.187164025 1483.99509776 63 1483.99509776 c 4
65.9338592397 1483.99509776 77.7382982208 1483.24844096 84.5502921682 1471.68154034 c 6
248.788761019 1192.80136799 l 5
266.174214397 1341.91506427 l 6
267.637145507 1354.46251187 278.267491735 1363.99509776 291 1363.99509776 c 4
EndSplineSet
EndChar
StartChar: uniE146
Encoding: 57670 57670 76
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
923.990195527 -133 m 0
923.990195527 -160.625937316 901.62567195 -182.990195527 874 -182.990195527 c 2
473 -182.990195527 l 2
445.374062684 -182.990195527 423.009804473 -160.62567195 423.009804473 -133 c 0
423.009804473 -105.374062684 445.37432805 -83.009804473 473 -83.009804473 c 2
874 -83.009804473 l 2
901.625937316 -83.009804473 923.990195527 -105.37432805 923.990195527 -133 c 0
120 10.009804473 m 0
92.3740626842 10.009804473 70.009804473 32.3743280499 70.009804473 60 c 0
70.009804473 64.8134431008 70.699727686 69.6268862016 72.079574112 74.2784931335 c 2
425.079574112 1264.27849313 l 2
431.263927162 1285.12659548 450.525009059 1299.99019553 473 1299.99019553 c 0
479.835355796 1299.99019553 499.699888713 1298.26011808 513.435208596 1279.42115066 c 2
899.438504977 749.990195527 l 1
1295 749.990195527 l 2
1301.13102947 749.990195527 1323.80711626 748.43031074 1337.51061627 726.339026088 c 2
1734.51061627 86.3390260882 l 2
1739.49700244 78.300519415 1741.99019553 69.1502597075 1741.99019553 60 c 0
1741.99019553 32.3740626842 1719.62567195 10.009804473 1692 10.009804473 c 0
1685.86897053 10.009804473 1663.19288374 11.5696892605 1649.48938373 33.6609739118 c 2
1267.16049977 650.009804473 l 1
874 650.009804473 l 2
867.164644204 650.009804473 847.300111287 651.739881916 833.564791404 670.578849337 c 2
492.17532648 1138.81876382 l 1
167.920425888 45.7215068665 l 2
161.736072838 24.8734045156 142.474990941 10.009804473 120 10.009804473 c 0
987 1293.13572276 m 0
1000.81296866 1293.13572276 1011.99509776 1281.95346098 1011.99509776 1268.140625 c 0
1011.99509776 1263.69241786 1010.81670769 1259.24421072 1008.45992755 1255.30688847 c 2
935.698971275 1133.74980006 l 1
1236.81087137 1237.76397869 l 2
1239.45825701 1238.67847474 1242.22912851 1239.13572276 1245 1239.13572276 c 0
1258.81296866 1239.13572276 1269.99509776 1227.95346098 1269.99509776 1214.140625 c 0
1269.99509776 1203.27422071 1263.04780398 1193.92278939 1253.18912863 1190.51727131 c 2
939.748292604 1082.24426351 l 1
1077.25037188 1004.93905019 l 2
1079.69668307 1003.56370656 1089.99509776 996.953556381 1089.99509776 983.140625 c 0
1089.99509776 969.327656342 1078.81283598 958.145527236 1065 958.145527236 c 0
1060.77011153 958.145527236 1056.54022306 959.211084762 1052.74962812 961.342199814 c 2
866.749628119 1065.91348888 l 2
866.618722401 1065.98708554 866.465332358 1066.07567196 866.292238034 1066.17954775 c 0
863.033869485 1068.10546545 860.2515148 1070.74998574 858.162663277 1073.89560304 c 0
855.849429066 1077.31041594 854.004904086 1081.86612897 854.004902236 1087.71191406 c 0
854.004902236 1090.53137826 854.472612732 1093.24885041 855.337535819 1095.7869306 c 0
855.896115778 1097.42970853 856.630294239 1099.02574468 857.540072447 1100.54565059 c 2
965.540072447 1280.97436153 l 2
967.067066953 1283.52541385 973.701841651 1293.13572276 987 1293.13572276 c 0
EndSplineSet
EndChar
StartChar: uniE147
Encoding: 57671 57671 77
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1344.99019553 -133 m 0
1344.99019553 -160.625937316 1322.62567195 -182.990195527 1295 -182.990195527 c 2
874 -182.990195527 l 2
846.374062684 -182.990195527 824.009804473 -160.62567195 824.009804473 -133 c 0
824.009804473 -105.374062684 846.37432805 -83.009804473 874 -83.009804473 c 2
1295 -83.009804473 l 2
1322.62593732 -83.009804473 1344.99019553 -105.37432805 1344.99019553 -133 c 0
120 10.009804473 m 0
92.3740626842 10.009804473 70.009804473 32.3743280499 70.009804473 60 c 0
70.009804473 64.8134431008 70.699727686 69.6268862016 72.079574112 74.2784931335 c 2
425.079574112 1264.27849313 l 2
431.263927162 1285.12659548 450.525009059 1299.99019553 473 1299.99019553 c 0
479.835355796 1299.99019553 499.699888713 1298.26011808 513.435208596 1279.42115066 c 2
899.438504977 749.990195527 l 1
1295 749.990195527 l 2
1301.13102947 749.990195527 1323.80711626 748.43031074 1337.51061627 726.339026088 c 2
1734.51061627 86.3390260882 l 2
1739.49700244 78.300519415 1741.99019553 69.1502597075 1741.99019553 60 c 0
1741.99019553 32.3740626842 1719.62567195 10.009804473 1692 10.009804473 c 0
1685.86897053 10.009804473 1663.19288374 11.5696892605 1649.48938373 33.6609739118 c 2
1267.16049977 650.009804473 l 1
874 650.009804473 l 2
867.164644204 650.009804473 847.300111287 651.739881916 833.564791404 670.578849337 c 2
492.17532648 1138.81876382 l 1
167.920425888 45.7215068665 l 2
161.736072838 24.8734045156 142.474990941 10.009804473 120 10.009804473 c 0
1084.00495181 1430.66194045 m 2
1084.00495181 1444.57909512 1095.22502858 1455.70701183 1109 1455.70701183 c 0
1122.79340754 1455.70701183 1133.96768049 1444.55520556 1133.99504819 1430.76188767 c 2
1134.84372708 1003.00652189 l 1
1191.01019316 1078.20250251 1280.58910304 1196.65635616 1282.32233047 1198.38958359 c 0
1287.20061596 1203.26786908 1293.60030798 1205.70701183 1300 1205.70701183 c 0
1313.81296866 1205.70701183 1324.99509776 1194.52475004 1324.99509776 1180.71191406 c 0
1324.99509776 1175.39825496 1323.31356069 1170.08459585 1319.95048655 1165.64179831 c 2
1319.95048655 1165.64179831 1169.74933909 967.202659181 1136.79718212 921.589936639 c 1
1134.99509776 926.711914062 l 0
1134.99509776 914.405239676 1126.88992674 905.533770837 1116.96812187 902.69947513 c 0
1114.75667886 902.059674388 1112.41866713 901.716818782 1110 901.716816299 c 0
1109.21533751 901.716816299 1108.43915082 901.752904718 1107.67300982 901.823514982 c 0
1102.42285625 902.229856341 1097.16630151 904.09245251 1092.05299755 909.205756573 c 0
1083.69666264 917.562091485 1049.14355642 964.844828621 1007.26491083 1021.09775066 c 0
955.99749067 1089.96201665 899.880227391 1165.86808633 899.880227391 1165.86808633 c 2
896.630010621 1170.26443217 895.004902236 1175.48817312 895.004902236 1180.71191406 c 0
895.004902236 1194.52488272 906.187164025 1205.70701183 920 1205.70701183 c 0
923.445371477 1205.70701183 933.259523451 1204.83513145 940.119772609 1195.5557418 c 2
940.119772609 1195.5557418 1029.57692545 1075.08662563 1084.85734273 1001.0569193 c 1
1084.00495181 1430.66194045 l 2
EndSplineSet
EndChar
StartChar: uniE148
Encoding: 57672 57672 78
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1741.99019553 -133 m 0
1741.99019553 -160.625937316 1719.62567195 -182.990195527 1692 -182.990195527 c 2
1295 -182.990195527 l 2
1267.37406268 -182.990195527 1245.00980447 -160.62567195 1245.00980447 -133 c 0
1245.00980447 -105.374062684 1267.37432805 -83.009804473 1295 -83.009804473 c 2
1692 -83.009804473 l 2
1719.62593732 -83.009804473 1741.99019553 -105.37432805 1741.99019553 -133 c 0
120 10.009804473 m 0
92.3740626842 10.009804473 70.009804473 32.3743280499 70.009804473 60 c 0
70.009804473 64.8134431008 70.699727686 69.6268862016 72.079574112 74.2784931335 c 2
425.079574112 1264.27849313 l 2
431.263927162 1285.12659548 450.525009059 1299.99019553 473 1299.99019553 c 0
479.835355796 1299.99019553 499.699888713 1298.26011808 513.435208596 1279.42115066 c 2
899.438504977 749.990195527 l 1
1295 749.990195527 l 2
1301.13102947 749.990195527 1323.80711626 748.43031074 1337.51061627 726.339026088 c 2
1734.51061627 86.3390260882 l 2
1739.49700244 78.300519415 1741.99019553 69.1502597075 1741.99019553 60 c 0
1741.99019553 32.3740626842 1719.62567195 10.009804473 1692 10.009804473 c 0
1685.86897053 10.009804473 1663.19288374 11.5696892605 1649.48938373 33.6609739118 c 2
1267.16049977 650.009804473 l 1
874 650.009804473 l 2
867.164644204 650.009804473 847.300111287 651.739881916 833.564791404 670.578849337 c 2
492.17532648 1138.81876382 l 1
167.920425888 45.7215068665 l 2
161.736072838 24.8734045156 142.474990941 10.009804473 120 10.009804473 c 0
1647.27476051 1191.59987793 m 0
1650.59441446 1201.62267929 1660.02764304 1208.70701183 1671 1208.70701183 c 0
1684.81296866 1208.70701183 1695.99509776 1197.52475004 1695.99509776 1183.71191406 c 0
1695.99509776 1181.04593052 1695.57181167 1178.37994697 1694.72523949 1175.8239502 c 0
1653.66887383 1051.86530771 1594.4265485 873.585135594 1560.31492304 774.14523513 c 1
1723.338566 880.654015196 l 2
1727.47427684 883.356012949 1732.23713842 884.707011826 1737 884.707011826 c 0
1750.81296866 884.707011826 1761.99509776 873.524750038 1761.99509776 859.711914062 c 0
1761.99509776 851.02772187 1757.55561844 843.274013429 1750.661434 838.769812929 c 2
1526.70849225 692.453890982 l 2
1523.19152841 689.855872872 1518.3636137 687.716816299 1512 687.716816299 c 0
1507.04339917 687.716816299 1502.42555525 689.15668701 1498.5415823 691.64129618 c 0
1494.3486555 694.321072629 1491.00027694 698.22285387 1489.01330483 702.872514023 c 0
1488.81598153 703.3333405 1488.63210474 703.801314398 1488.4621627 704.275941682 c 2
1389.5442774 973.052821943 l 2
1388.51802729 975.841319716 1388.00490224 978.776616889 1388.00490224 981.711914062 c 0
1388.00490224 995.52488272 1399.18716402 1006.70701183 1413 1006.70701183 c 0
1423.6987823 1006.70701183 1432.92175444 999.973404506 1436.4557226 990.371006182 c 2
1511.5898978 786.218550321 l 1
1532.32115736 846.743221881 1572.08799988 964.611311617 1647.27476051 1191.59987793 c 0
EndSplineSet
EndChar
StartChar: uniE149
Encoding: 57673 57673 79
Width: 1792
Flags: H
LayerCount: 2
Fore
SplineSet
1484 1461.6171875 m 5
1484 1103.6171875 l 5
1667 1173.6171875 l 5
1441 884.6171875 l 5
1215 1173.6171875 l 5
1398 1103.6171875 l 5
1398 1461.6171875 l 5
1484 1461.6171875 l 5
1666.98529329 -56.2001953125 m 0
1666.98529329 -97.6391012863 1633.43850793 -131.185488603 1592 -131.185488603 c 0
1572.80092394 -131.185488603 1553.60184788 -123.868060369 1538.96699141 -109.233203901 c 2
146.966991411 1282.7667961 l 2
132.332134943 1297.40165257 125.014706709 1316.60072863 125.014706709 1335.79980469 c 0
125.014706709 1377.23871066 158.561492075 1410.78509798 200 1410.78509798 c 0
219.199076061 1410.78509798 238.398152121 1403.46766974 253.033008589 1388.83281328 c 2
1645.03300859 -3.16718672351 l 2
1659.66786506 -17.8020431912 1666.98529329 -37.0011192519 1666.98529329 -56.2001953125 c 0
EndSplineSet
EndChar
StartChar: uniE14A
Encoding: 57674 57674 80
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1484 1461.6171875 m 1
1484 1103.6171875 l 1
1667 1173.6171875 l 1
1441 884.6171875 l 1
1215 1173.6171875 l 1
1398 1103.6171875 l 1
1398 1461.6171875 l 1
1484 1461.6171875 l 1
1666.98529329 -56.2001953125 m 0
1666.98529329 -97.6391012863 1633.43850793 -131.185488603 1592 -131.185488603 c 0
1591.52523691 -131.185488603 1591.05047382 -131.181014029 1590.57577974 -131.17206488 c 0
1204.22474011 -123.888399549 871.515114873 8.26117947017 618.582599933 261.19369441 c 0
371.118582643 508.6577117 203.65752734 866.213710135 126.078697051 1323.16817722 c 0
125.369370157 1327.34625121 125.014706709 1331.57302795 125.014706709 1335.79980469 c 0
125.014706709 1377.23871066 158.561492075 1410.78509798 200 1410.78509798 c 0
236.80294028 1410.78509798 267.845706788 1384.21788054 273.921302949 1348.43143216 c 0
347.777920162 913.401527306 505.150856207 586.757472492 724.648617111 367.259711588 c 0
948.32678825 143.58154045 1240.28993931 25.4291221611 1593.42422026 18.7716742555 c 0
1634.28501058 18.0013480719 1666.98529329 -15.3022691437 1666.98529329 -56.2001953125 c 0
EndSplineSet
EndChar
StartChar: uniE14B
Encoding: 57675 57675 81
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1398 -160.3828125 m 5
1398 197.6171875 l 5
1215 127.6171875 l 5
1441 416.6171875 l 5
1667 127.6171875 l 5
1484 197.6171875 l 5
1484 -160.3828125 l 5
1398 -160.3828125 l 5
200 -131.185488603 m 0
158.561094026 -131.185488603 125.014706709 -97.6387032376 125.014706709 -56.2001953125 c 0
125.014706709 -37.0011192519 132.332134943 -17.8020431912 146.966991411 -3.16718672351 c 2
1538.96699141 1388.83281328 l 2
1553.60184788 1403.46766974 1572.80092394 1410.78509798 1592 1410.78509798 c 0
1633.43890597 1410.78509798 1666.98529329 1377.23831261 1666.98529329 1335.79980469 c 0
1666.98529329 1316.60072863 1659.66786506 1297.40165257 1645.03300859 1282.7667961 c 2
253.033008589 -109.233203901 l 2
238.398152121 -123.868060369 219.199076061 -131.185488603 200 -131.185488603 c 0
EndSplineSet
EndChar
StartChar: uniE14C
Encoding: 57676 57676 82
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1398 -160.3828125 m 1
1398 197.6171875 l 1
1215 127.6171875 l 1
1441 416.6171875 l 1
1667 127.6171875 l 1
1484 197.6171875 l 1
1484 -160.3828125 l 1
1398 -160.3828125 l 1
200 -131.185488603 m 0
158.561094026 -131.185488603 125.014706709 -97.6387032376 125.014706709 -56.2001953125 c 0
125.014706709 -46.3359567044 126.9463412 -36.4717180962 130.809610181 -27.2265159787 c 0
180.222951228 91.0247182868 222.892032868 202.02925922 262.91073691 306.157577084 c 0
385.479688717 625.080917548 482.300015367 877.006560584 669.496680182 1064.2032254 c 0
860.176064791 1254.88261001 1135.57605261 1366.88874873 1584.71163148 1410.43269233 c 0
1587.13489866 1410.66762943 1589.56744933 1410.78509798 1592 1410.78509798 c 0
1633.43890597 1410.78509798 1666.98529329 1377.23831261 1666.98529329 1335.79980469 c 0
1666.98529329 1297.0734414 1637.60142618 1264.88138924 1599.28836852 1261.16691704 c 0
983.643746929 1201.47982935 759.100861728 1016.28364015 593.588205401 701.489986386 c 0
527.530145003 575.852112797 469.624761613 425.83922682 402.895181451 252.209442936 c 0
362.875526167 148.078649948 319.626145727 35.5241040949 269.190389819 -85.1738746463 c 0
257.838987207 -112.338954786 231.005903287 -131.185488603 200 -131.185488603 c 0
EndSplineSet
EndChar
StartChar: uniE14D
Encoding: 57677 57677 83
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1195.09712219 749.990195527 m 1
1295 749.990195527 l 2
1301.13102947 749.990195527 1323.80711626 748.43031074 1337.51061627 726.339026088 c 2
1734.51061627 86.3390260882 l 2
1739.49700244 78.300519415 1741.99019553 69.1502597075 1741.99019553 60 c 0
1741.99019553 32.3740626842 1719.62567195 10.009804473 1692 10.009804473 c 0
1685.86897053 10.009804473 1663.19288374 11.5696892605 1649.48938373 33.6609739118 c 2
1267.16049977 650.009804473 l 1
1131.95161768 650.009804473 l 1
1195.09712219 749.990195527 l 1
1131.95161768 650.009804473 m 1025
731.259986684 810.89716005 m 1
492.17532648 1138.81876382 l 1
167.920425888 45.7215068665 l 2
161.736072838 24.8734045156 142.474990941 10.009804473 120 10.009804473 c 0
92.3740626842 10.009804473 70.009804473 32.3743280499 70.009804473 60 c 0
70.009804473 64.8134431008 70.699727686 69.6268862016 72.079574112 74.2784931335 c 2
425.079574112 1264.27849313 l 2
431.263927162 1285.12659548 450.525009059 1299.99019553 473 1299.99019553 c 0
479.835355796 1299.99019553 499.699888713 1298.26011808 513.435208596 1279.42115066 c 2
790.499437572 899.407869025 l 1
731.259986684 810.89716005 l 1
1350.99804688 1459.29681673 m 0
1392.43695285 1459.29681673 1425.98334017 1425.75003136 1425.98334017 1384.31152344 c 0
1425.98334017 1370.32875638 1422.10197898 1356.34598932 1414.3392566 1344.1264358 c 2
463.339256601 -152.873564196 l 2
458.314262729 -160.783570323 438.406450284 -187.673769853 399.998046875 -187.673769853 c 0
358.559140901 -187.673769853 325.012753584 -154.126984488 325.012753584 -112.688476563 c 0
325.012753584 -98.7057095056 328.894114773 -84.7229424487 336.656837149 -72.5033889291 c 2
1287.65683715 1424.49661107 l 2
1292.68183102 1432.4066172 1312.58964347 1459.29681673 1350.99804688 1459.29681673 c 0
EndSplineSet
EndChar
StartChar: uniE14E
Encoding: 57678 57678 84
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
120 10 m 0
106 10 94.1666666667 14.8333333333 84.5 24.5 c 128
74.8333333333 34.1666666667 70 46 70 60 c 0
70 64.6666666667 70.6666666667 69.3333333333 72 74 c 2
425 1264 l 2
428.333333333 1274.66666667 434.333333333 1283.33333333 443 1290 c 128
451.666666667 1296.66666667 461.666666667 1300 473 1300 c 0
489.666666667 1300 503 1293 513 1279 c 2
899 750 l 1
1295 750 l 2
1313.66666667 750 1328 742 1338 726 c 2
1735 86 l 2
1739.66666667 78 1742 69.3333333333 1742 60 c 0
1742 46 1737.16666667 34.1666666667 1727.5 24.5 c 128
1717.83333333 14.8333333333 1706 10 1692 10 c 0
1673.33333333 10 1659 18 1649 34 c 2
1267 650 l 1
874 650 l 2
857.333333333 650 844 657 834 671 c 2
492 1139 l 1
168 46 l 2
164.666666667 35.3333333333 158.666666667 26.6666666667 150 20 c 128
141.333333333 13.3333333333 131.333333333 10 120 10 c 0
EndSplineSet
EndChar
StartChar: uniE14F
Encoding: 57679 57679 85
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
651.5 1454 m 128
651.833333333 1454 671 1435 709 1397 c 0
710.333333333 1395.66666667 733 1164 777 702 c 1
827 1285 l 1
884 1341 l 1
941 1285 l 1
1055 528 l 1
1099 925 l 1
1156 981 l 1
1213 925 l 1
1276 510 l 1
1299 1361 l 1
1356 1417 l 1
1413 1361 l 1
1553 881 l 1
1592 572 l 1
1661 1170 l 1
1773 1157 l 1
1641 17 l 1
1584 -40 l 1
1527 17 l 1
1451 881 l 1
1404 1032 l 1
1373 -115 l 1
1316 -172 l 1
1259 -115 l 1
1165 498 l 1
1121 97 l 1
1064 40 l 1
1007 97 l 1
900 809 l 1
837 77 l 1
780 20 l 1
723 77 l 1
723 82.3333333333 699.666666667 328.333333333 653 815 c 1
651 778.333333333 648 730 644 670 c 0
637 561 637 561 637 561 c 1
497 0 l 1
440 -57 l 1
383 0 l 1
289 877 l 1
217 -59 l 1
160 -116 l 1
103 -59 l 1
19 1381 l 1
132 1387 l 1
168 777 l 1
175 877 l 1
263 1113 263 1113 263 1113 c 1
320 1169 l 1
377 1113 l 1
462 316 l 1
523 561 l 2
523.666666667 561 528.333333333 629.333333333 537 766 c 128
545.666666667 902.666666667 555.666666667 1040.16666667 567 1178.5 c 128
578.333333333 1316.83333333 587.333333333 1389.66666667 594 1397 c 2
650 1453 l 2
650.666666667 1453.66666667 651.166666667 1454 651.5 1454 c 128
EndSplineSet
EndChar
StartChar: uniE150
Encoding: 57680 57680 86
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
404 70 m 1
1632 70 l 1
1632 -50 l 1
284 -50 l 1
284 1210 l 1
160 1210 l 1
160 1330 l 1
404 1330 l 1
404 70 l 1
EndSplineSet
EndChar
StartChar: uniE151
Encoding: 57681 57681 87
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
588 70 m 1
1632 70 l 1
1632 -50 l 1
468 -50 l 1
468 1210 l 1
160 1210 l 1
160 1330 l 1
588 1330 l 1
588 70 l 1
EndSplineSet
EndChar
StartChar: uniE152
Encoding: 57682 57682 88
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
772 70 m 1
1632 70 l 1
1632 -50 l 1
652 -50 l 1
652 1210 l 1
160 1210 l 1
160 1330 l 1
772 1330 l 1
772 70 l 1
EndSplineSet
EndChar
StartChar: uniE153
Encoding: 57683 57683 89
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
956 70 m 1
1632 70 l 1
1632 -50 l 1
836 -50 l 1
836 1210 l 1
160 1210 l 1
160 1330 l 1
956 1330 l 1
956 70 l 1
EndSplineSet
EndChar
StartChar: uniE154
Encoding: 57684 57684 90
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1140 70 m 1
1632 70 l 1
1632 -50 l 1
1020 -50 l 1
1020 1210 l 1
160 1210 l 1
160 1330 l 1
1140 1330 l 1
1140 70 l 1
EndSplineSet
EndChar
StartChar: uniE155
Encoding: 57685 57685 91
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1324 70 m 1
1632 70 l 1
1632 -50 l 1
1204 -50 l 1
1204 1210 l 1
160 1210 l 1
160 1330 l 1
1324 1330 l 1
1324 70 l 1
EndSplineSet
EndChar
StartChar: uniE156
Encoding: 57686 57686 92
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1508 70 m 1
1632 70 l 1
1632 -50 l 1
1388 -50 l 1
1388 1210 l 1
160 1210 l 1
160 1330 l 1
1508 1330 l 1
1508 70 l 1
EndSplineSet
EndChar
StartChar: uniE157
Encoding: 57687 57687 93
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
896 1330 m 0
912.666666667 1330 926.833333333 1324.16666667 938.5 1312.5 c 128
950.166666667 1300.83333333 956 1286.66666667 956 1270 c 0
956 1082 956 1082 956 705.5 c 128
956 329 956 329 956 140 c 1
1593 686 l 2
1604.33333333 695.333333333 1617.33333333 700 1632 700 c 0
1648.66666667 700 1662.83333333 694.166666667 1674.5 682.5 c 128
1686.16666667 670.833333333 1692 656.666666667 1692 640 c 0
1692 621.333333333 1685 606 1671 594 c 2
935 -36 l 2
923.666666667 -45.3333333333 910.666666667 -50 896 -50 c 0
879.333333333 -50 865.166666667 -44.1666666667 853.5 -32.5 c 128
841.833333333 -20.8333333333 836 -6.66666666667 836 10 c 2
836 1140 l 1
199 594 l 2
187.666666667 584.666666667 174.666666667 580 160 580 c 0
143.333333333 580 129.166666667 585.833333333 117.5 597.5 c 128
105.833333333 609.166666667 100 623.333333333 100 640 c 0
100 658.666666667 107 674 121 686 c 2
857 1316 l 2
868.333333333 1325.33333333 881.333333333 1330 896 1330 c 0
EndSplineSet
EndChar
StartChar: uniE158
Encoding: 57688 57688 94
Width: 1792
Flags: HW
LayerCount: 2
Fore
SplineSet
1632 700 m 0
1648.66666667 700 1662.83333333 694.166666667 1674.5 682.5 c 128
1686.16666667 670.833333333 1692 656.666666667 1692 640 c 0
1692 627.333333333 1688.33333333 615.666666667 1681 605 c 2
1313 85 l 2
1301 68.3333333333 1284.66666667 60 1264 60 c 128
1243.33333333 60 1227 68.3333333333 1215 85 c 2
528 1056 l 1
209 605 l 2
197 588.333333333 180.666666667 580 160 580 c 0
143.333333333 580 129.166666667 585.833333333 117.5 597.5 c 128
105.833333333 609.166666667 100 623.333333333 100 640 c 0
100 652.666666667 103.666666667 664.333333333 111 675 c 2
111 675 111 675 479 1195 c 0
491 1211.66666667 507.333333333 1220 528 1220 c 128
548.666666667 1220 565 1211.66666667 577 1195 c 2
1264 224 l 1
1583 675 l 2
1595 691.666666667 1611.33333333 700 1632 700 c 0
EndSplineSet
EndChar
EndChars
EndSplineFont

Binary file not shown.

View file

@ -1,3 +1,24 @@
YOU SHALL READ THE FOLLOWING TEXT BEFORE PROCEEDING!!!
IF YOU DO NOT READ IT, YOU MAY NOT BE ABLE TO USE FURNACE AT ALL.
if you are using a recent version of macOS, you may get an error saying that Furnace is damaged.
in that case, open Terminal, and type this:
```
xattr -d com.apple.quarantine /path/to/Furnace.app
```
(replace `/path/to/Furnace.app` with the path where Furnace.app is located, e.g. `/Applications/Furnace.app`)
you may need to reboot after doing this before launching Furnace.
END OF MACOS ISSUE ACKNOWLEDGEMENT - license text follows.
------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991

30
src/divasm/divasm.h Normal file
View file

@ -0,0 +1,30 @@
struct DivASMResult {
int line, err;
DivASMResult():
line(-1),
err(0) {}
};
struct DivASMFile {
String name;
SafeReader* data;
};
enum DivASMTarget {
DIV_ASM_TARGET_DUMMY=0,
DIV_ASM_TARGET_6502,
DIV_ASM_TARGET_SPC700
};
class DivASM {
std::vector<DivASMFile> files;
SafeWriter* result;
public:
DivASMResult getError();
SafeWriter* assemble(String name);
void addFile(String name, SafeReader* data);
DivASM(DivASMTarget target);
~DivASM();
};

View file

@ -67,6 +67,7 @@ struct blip_t
int avail;
int size;
int integrator;
unsigned char hipass;
};
typedef int buf_t;
@ -119,6 +120,7 @@ blip_t* blip_new( int size )
{
m->factor = time_unit / blip_max_ratio;
m->size = size;
m->hipass = 1;
blip_clear( m );
check_assumptions();
}
@ -135,6 +137,10 @@ void blip_delete( blip_t* m )
}
}
void blip_set_dc( blip_t* m, unsigned char enable ) {
m->hipass=enable;
}
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
{
double factor = time_unit * sample_rate / clock_rate;
@ -231,8 +237,10 @@ int blip_read_samples( blip_t* m, short out [], int count, int stereo )
out += step;
/* High-pass filter */
if (m->hipass) {
sum -= s << (delta_bits - bass_shift);
}
}
while ( in != end );
m->integrator = sum;

View file

@ -5,6 +5,9 @@ Sample buffer that resamples from input clock rate to output sample rate */
#ifndef BLIP_BUF_H
#define BLIP_BUF_H
// MODIFIED by tildearrow:
// - add option to disable high-pass filter
#ifdef __cplusplus
extern "C" {
#endif
@ -18,6 +21,9 @@ so that there are blip_max_ratio clocks per sample. Returns pointer to new
buffer, or NULL if insufficient memory. */
blip_t* blip_new( int sample_count );
/** (tildearrow) sets whether to enable high-pass filter. */
void blip_set_dc( blip_t*, unsigned char enable );
/** Sets approximate input clock rate and output sample rate. For every
clock_rate input clocks, approximately sample_rate samples are generated. */
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );

View file

@ -4,6 +4,11 @@ Author : Shay Green <gblargg@gmail.com>
Website : http://www.slack.net/~ant/
License : GNU Lesser General Public License (LGPL)
MODIFICATION DISCLAIMER
-----------------------
I have modified this library in order to add a function that disables the DC offset correction high-pass filter.
- tildearrow
Contents
--------

View file

@ -393,7 +393,23 @@ struct DivChannelModeHints {
// - 1: volume
// - 2: pitch
// - 3: panning
// - 4: ???
// - 4: chip primary
// - 5: chip secondary
// - 6: mixing
// - 7: DSP
// - 8: note
// - 9: misc 1
// - 10: misc 2
// - 11: misc 3
// - 12: attack
// - 13: decay
// - 14: sustain
// - 15: release
// - 16: dec linear
// - 17: dec exp
// - 18: inc linear
// - 19: inc bent
// - 20: direct
unsigned char type[4];
// up to 4
unsigned char count;

View file

@ -97,8 +97,13 @@ void DivDispatchContainer::setRates(double gotRate) {
rateMemory=gotRate;
}
void DivDispatchContainer::setQuality(bool lowQual) {
void DivDispatchContainer::setQuality(bool lowQual, bool dcHiPass) {
lowQuality=lowQual;
hiPass=dcHiPass;
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
if (bb[i]==NULL) continue;
blip_set_dc(bb[i],dcHiPass);
}
}
void DivDispatchContainer::grow(size_t size) {
@ -124,6 +129,7 @@ void DivDispatchContainer::grow(size_t size) {
logE("not enough memory!"); \
return; \
} \
blip_set_dc(bb[i],hiPass); \
blip_set_rates(bb[i],dispatch->rate,rateMemory); \
\
if (bbIn[i]==NULL) bbIn[i]=new short[bbInLen]; \
@ -166,11 +172,13 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
if (dcOffCompensation && runtotal>0) {
dcOffCompensation=false;
if (hiPass) {
for (int i=0; i<outs; i++) {
if (bbIn[i]==NULL) continue;
prevSample[i]=bbIn[i][0];
}
}
}
if (lowQuality) {
for (int i=0; i<outs; i++) {
if (bbIn[i]==NULL) continue;
@ -215,7 +223,7 @@ void DivDispatchContainer::clear() {
prevSample[i]=0;
}
if (dispatch->getDCOffRequired()) {
if (dispatch->getDCOffRequired() && hiPass) {
dcOffCompensation=true;
}
}
@ -625,6 +633,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
bbOut[i]=new short[bbInLen];
memset(bbIn[i],0,bbInLen*sizeof(short));
memset(bbOut[i],0,bbInLen*sizeof(short));
blip_set_dc(bb[i],hiPass);
}
}

View file

@ -1081,7 +1081,7 @@ bool DivEngine::addSystem(DivSystem which) {
song.patchbay.push_back((i<<20)|j);
}
} else {
song.patchbay.reserve(outs);
if (outs>0) song.patchbay.reserve(outs);
for (unsigned int j=0; j<outs; j++) {
song.patchbay.push_back((i<<20)|(j<<16)|j);
}
@ -1189,11 +1189,11 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
// prepare swap list
int index=0;
swapList.reserve(song.systemLen);
if (song.systemLen>0) swapList.reserve(song.systemLen);
for (int i=0; i<song.systemLen; i++) {
chanList.clear();
const int channelCount=getChannelCount(song.system[i]);
chanList.reserve(channelCount);
if (channelCount>0) chanList.reserve(channelCount);
for (int j=0; j<channelCount; j++) {
chanList.push_back(index);
index++;
@ -1427,6 +1427,11 @@ DivChannelPair DivEngine::getChanPaired(int ch) {
return disCont[dispatchOfChan[ch]].dispatch->getPaired(dispatchChanOfChan[ch]);
}
DivChannelModeHints DivEngine::getChanModeHints(int ch) {
if (ch<0 || ch>=chans) return DivChannelModeHints();
return disCont[dispatchOfChan[ch]].dispatch->getModeHints(dispatchChanOfChan[ch]);
}
unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) {
if (sys<0 || sys>=song.systemLen) return NULL;
if (disCont[sys].dispatch==NULL) return NULL;
@ -3354,7 +3359,7 @@ bool DivEngine::switchMaster(bool full) {
if (initAudioBackend()) {
for (int i=0; i<song.systemLen; i++) {
disCont[i].setRates(got.rate);
disCont[i].setQuality(lowQuality);
disCont[i].setQuality(lowQuality,dcHiPass);
}
if (!output->setRun(true)) {
logE("error while activating audio!");
@ -3453,10 +3458,14 @@ void DivEngine::initDispatch(bool isRender) {
BUSY_BEGIN;
logV("initializing dispatch...");
if (isRender) logI("render cores set");
lowQuality=getConfInt("audioQuality",0);
dcHiPass=getConfInt("audioHiPass",1);
for (int i=0; i<song.systemLen; i++) {
disCont[i].init(song.system[i],this,getChannelCount(song.system[i]),got.rate,song.systemFlags[i],isRender);
disCont[i].setRates(got.rate);
disCont[i].setQuality(lowQuality);
disCont[i].setQuality(lowQuality,dcHiPass);
}
if (song.patchbayAuto) {
saveLock.lock();
@ -3535,7 +3544,6 @@ bool DivEngine::initAudioBackend() {
}
#endif
lowQuality=getConfInt("audioQuality",0);
forceMono=getConfInt("forceMono",0);
clampSamples=getConfInt("clampSamples",0);
lowLatency=getConfInt("lowLatency",0);
@ -3783,6 +3791,7 @@ bool DivEngine::init() {
logE("not enough memory!");
return false;
}
blip_set_dc(samp_bb,0);
samp_bbOut=new short[32768];

View file

@ -54,8 +54,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "dev186"
#define DIV_ENGINE_VERSION 186
#define DIV_VERSION "dev187"
#define DIV_ENGINE_VERSION 187
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -205,7 +205,7 @@ struct DivDispatchContainer {
short* bbInMapped[DIV_MAX_OUTPUTS];
short* bbIn[DIV_MAX_OUTPUTS];
short* bbOut[DIV_MAX_OUTPUTS];
bool lowQuality, dcOffCompensation;
bool lowQuality, dcOffCompensation, hiPass;
double rateMemory;
// used in multi-thread
@ -213,7 +213,7 @@ struct DivDispatchContainer {
unsigned int size;
void setRates(double gotRate);
void setQuality(bool lowQual);
void setQuality(bool lowQual, bool dcHiPass);
void grow(size_t size);
void acquire(size_t offset, size_t count);
void flush(size_t count);
@ -230,6 +230,7 @@ struct DivDispatchContainer {
lastAvail(0),
lowQuality(false),
dcOffCompensation(false),
hiPass(true),
rateMemory(0.0),
cycles(0),
size(0) {
@ -389,6 +390,7 @@ class DivEngine {
int chans;
bool active;
bool lowQuality;
bool dcHiPass;
bool playing;
bool freelance;
bool shallStop, shallStopSched;
@ -1017,6 +1019,9 @@ class DivEngine {
// get channel pairs
DivChannelPair getChanPaired(int chan);
// get channel mode hints
DivChannelModeHints getChanModeHints(int chan);
// get register pool
unsigned char* getRegisterPool(int sys, int& size, int& depth);
@ -1221,6 +1226,7 @@ class DivEngine {
chans(0),
active(false),
lowQuality(false),
dcHiPass(true),
playing(false),
freelance(false),
shallStop(false),

View file

@ -343,7 +343,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.insLen=16;
}
logI("reading instruments (%d)...",ds.insLen);
ds.ins.reserve(ds.insLen);
if (ds.insLen>0) ds.ins.reserve(ds.insLen);
for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument;
unsigned char mode=0;
@ -601,6 +601,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
if (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580) {
bool volIsCutoff=false;
ins->c64.triOn=reader.readC();
ins->c64.sawOn=reader.readC();
ins->c64.pulseOn=reader.readC();
@ -617,9 +619,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ins->c64.oscSync=reader.readC();
ins->c64.toFilter=reader.readC();
if (ds.version<0x11) {
ins->c64.volIsCutoff=reader.readI();
volIsCutoff=reader.readI();
} else {
ins->c64.volIsCutoff=reader.readC();
volIsCutoff=reader.readC();
}
ins->c64.initFilter=reader.readC();
@ -631,10 +633,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ins->c64.ch3off=reader.readC();
// weird storage
if (ins->c64.volIsCutoff) {
for (int j=0; j<ins->std.volMacro.len; j++) {
ins->std.volMacro.val[j]-=18;
if (volIsCutoff) {
// move to alg (new cutoff)
ins->std.algMacro.len=ins->std.volMacro.len;
ins->std.algMacro.loop=ins->std.volMacro.loop;
ins->std.algMacro.rel=ins->std.volMacro.rel;
for (int j=0; j<ins->std.algMacro.len; j++) {
ins->std.algMacro.val[j]=-(ins->std.volMacro.val[j]-18);
}
ins->std.volMacro.len=0;
memset(ins->std.volMacro.val,0,256*sizeof(int));
}
for (int j=0; j<ins->std.dutyMacro.len; j++) {
ins->std.dutyMacro.val[j]-=12;
@ -671,7 +679,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (ds.version>0x0b) {
ds.waveLen=(unsigned char)reader.readC();
logI("reading wavetables (%d)...",ds.waveLen);
ds.wave.reserve(ds.waveLen);
if (ds.waveLen>0) ds.wave.reserve(ds.waveLen);
for (int i=0; i<ds.waveLen; i++) {
DivWavetable* wave=new DivWavetable;
wave->len=(unsigned char)reader.readI();
@ -841,7 +849,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
// it appears this byte stored the YMU759 sample rate
ymuSampleRate=reader.readC();
}
ds.sample.reserve(ds.sampleLen);
if (ds.sampleLen>0) ds.sample.reserve(ds.sampleLen);
for (int i=0; i<ds.sampleLen; i++) {
DivSample* sample=new DivSample;
int length=reader.readI();
@ -1054,9 +1062,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.systemFlags[0].set("dpcmMode",false);
}
// C64 no key priority
// C64 no key priority, reset time and multiply relative
if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) {
ds.systemFlags[0].set("keyPriority",false);
ds.systemFlags[0].set("initResetTime",1);
ds.systemFlags[0].set("multiplyRel",true);
}
// OPM broken pitch
@ -1953,6 +1963,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
logD("systems:");
ds.systemLen=0;
for (int i=0; i<DIV_MAX_CHIPS; i++) {
unsigned char sysID=reader.readC();
ds.system[i]=systemFromFileFur(sysID);
@ -1973,6 +1984,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
tchans=DIV_MAX_CHANS;
logW("too many channels!");
}
logV("system len: %d",ds.systemLen);
if (ds.systemLen<1) {
logE("zero chips!");
lastError="zero chips!";
delete[] file;
return false;
}
// system volume
for (int i=0; i<DIV_MAX_CHIPS; i++) {
@ -2361,7 +2379,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// patchbay
unsigned int conns=reader.readI();
ds.patchbay.reserve(conns);
if (conns>0) ds.patchbay.reserve(conns);
for (unsigned int i=0; i<conns; i++) {
ds.patchbay.push_back((unsigned int)reader.readI());
}
@ -3017,6 +3035,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// C64 original reset time and multiply relative
if (ds.version<187) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
ds.systemFlags[i].set("initResetTime",1);
ds.systemFlags[i].set("multiplyRel",true);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -6061,23 +6089,43 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
}
}
} else { // STD
bool volIsCutoff=false;
if (sys!=DIV_SYSTEM_GB) {
int realVolMacroLen=i->std.volMacro.len;
if (realVolMacroLen>127) realVolMacroLen=127;
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
if (i->std.algMacro.len>0) volIsCutoff=true;
if (volIsCutoff) {
if (i->std.volMacro.len>0) {
addWarning(".dmf only supports volume or cutoff macro in C64, but not both. volume macro will be lost.");
}
realVolMacroLen=i->std.algMacro.len;
if (realVolMacroLen>127) realVolMacroLen=127;
w->writeC(realVolMacroLen);
if ((sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) && i->c64.volIsCutoff) {
for (int j=0; j<realVolMacroLen; j++) {
w->writeI(i->std.volMacro.val[j]+18);
w->writeI((-i->std.algMacro.val[j])+18);
}
} else {
w->writeC(realVolMacroLen);
for (int j=0; j<realVolMacroLen; j++) {
w->writeI(i->std.volMacro.val[j]);
}
}
} else {
w->writeC(realVolMacroLen);
for (int j=0; j<realVolMacroLen; j++) {
w->writeI(i->std.volMacro.val[j]);
}
}
if (realVolMacroLen>0) {
if (volIsCutoff) {
w->writeC(i->std.algMacro.loop);
} else {
w->writeC(i->std.volMacro.loop);
}
}
}
bool arpMacroMode=false;
int arpMacroHowManyFixed=0;
@ -6166,7 +6214,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(i->c64.oscSync);
w->writeC(i->c64.toFilter);
w->writeC(i->c64.volIsCutoff);
w->writeC(volIsCutoff);
w->writeC(i->c64.initFilter);
w->writeC(i->c64.res);

View file

@ -349,6 +349,8 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
}
if (ins->type==DIV_INS_C64) {
bool volIsCutoff=false;
ins->c64.triOn=reader.readC();
ins->c64.sawOn=reader.readC();
ins->c64.pulseOn=reader.readC();
@ -365,9 +367,9 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
ins->c64.oscSync=reader.readC();
ins->c64.toFilter=reader.readC();
if (version<0x07) { // TODO: UNSURE
ins->c64.volIsCutoff=reader.readI();
volIsCutoff=reader.readI();
} else {
ins->c64.volIsCutoff=reader.readC();
volIsCutoff=reader.readC();
}
ins->c64.initFilter=reader.readC();
@ -379,10 +381,16 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
ins->c64.ch3off=reader.readC();
// weird storage
if (ins->c64.volIsCutoff) {
for (int j=0; j<ins->std.volMacro.len; j++) {
ins->std.volMacro.val[j]-=18;
if (volIsCutoff) {
// move to alg (new cutoff)
ins->std.algMacro.len=ins->std.volMacro.len;
ins->std.algMacro.loop=ins->std.volMacro.loop;
ins->std.algMacro.rel=ins->std.volMacro.rel;
for (int j=0; j<ins->std.algMacro.len; j++) {
ins->std.algMacro.val[j]=-(ins->std.volMacro.val[j]-18);
}
ins->std.volMacro.len=0;
memset(ins->std.volMacro.val,0,256*sizeof(int));
}
for (int j=0; j<ins->std.dutyMacro.len; j++) {
ins->std.dutyMacro.val[j]-=12;

View file

@ -101,7 +101,6 @@ bool DivInstrumentC64::operator==(const DivInstrumentC64& other) {
_C(ringMod) &&
_C(oscSync) &&
_C(toFilter) &&
_C(volIsCutoff) &&
_C(initFilter) &&
_C(dutyIsAbs) &&
_C(filterIsAbs) &&
@ -414,7 +413,6 @@ void DivInstrument::writeFeature64(SafeWriter* w) {
w->writeC(
(c64.dutyIsAbs?0x80:0)|
(c64.initFilter?0x40:0)|
(c64.volIsCutoff?0x20:0)|
(c64.toFilter?0x10:0)|
(c64.noiseOn?8:0)|
(c64.pulseOn?4:0)|
@ -1372,7 +1370,7 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(c64.oscSync);
w->writeC(c64.toFilter);
w->writeC(c64.initFilter);
w->writeC(c64.volIsCutoff);
w->writeC(0); // this was volIsCutoff
w->writeC(c64.res);
w->writeC(c64.lp);
w->writeC(c64.bp);
@ -2143,13 +2141,13 @@ void DivInstrument::readFeatureMA(SafeReader& reader, short version) {
READ_FEAT_END;
}
void DivInstrument::readFeature64(SafeReader& reader, short version) {
void DivInstrument::readFeature64(SafeReader& reader, bool& volIsCutoff, short version) {
READ_FEAT_BEGIN;
unsigned char next=reader.readC();
c64.dutyIsAbs=next&128;
c64.initFilter=next&64;
c64.volIsCutoff=next&32;
volIsCutoff=next&32;
c64.toFilter=next&16;
c64.noiseOn=next&8;
c64.pulseOn=next&4;
@ -2677,6 +2675,7 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) {
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
unsigned char featCode[2];
bool volIsCutoff=false;
int dataLen=reader.size()-4;
if (!fui) {
@ -2705,7 +2704,7 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
} else if (memcmp(featCode,"MA",2)==0) { // macros
readFeatureMA(reader,version);
} else if (memcmp(featCode,"64",2)==0) { // C64
readFeature64(reader,version);
readFeature64(reader,volIsCutoff,version);
} else if (memcmp(featCode,"GB",2)==0) { // Game Boy
readFeatureGB(reader,version);
} else if (memcmp(featCode,"SM",2)==0) { // sample
@ -2756,6 +2755,24 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
}
}
// <187 C64 cutoff macro compatibility
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
std.algMacro.macroType=DIV_MACRO_ALG;
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
if (!c64.filterIsAbs) {
for (int i=0; i<std.algMacro.len; i++) {
std.algMacro.val[i]=-std.algMacro.val[i];
}
}
}
// <187 special/test/gate merge
if (type==DIV_INS_C64 && version<187) {
convertC64SpecialMacro();
}
return DIV_DATA_SUCCESS;
}
@ -2763,6 +2780,7 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
for (int macroValPos=0; macroValPos<y; macroValPos++) x[macroValPos]=reader.readI();
DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
bool volIsCutoff=false;
reader.readI(); // length. ignored.
reader.readS(); // format version. ignored.
@ -2845,7 +2863,7 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
c64.oscSync=reader.readC();
c64.toFilter=reader.readC();
c64.initFilter=reader.readC();
c64.volIsCutoff=reader.readC();
volIsCutoff=reader.readC();
c64.res=reader.readC();
c64.lp=reader.readC();
c64.bp=reader.readC();
@ -2903,7 +2921,7 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
}
}
if (type==DIV_INS_C64 && version<87) {
if (c64.volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
if (volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
std.volMacro.val[j]-=18;
}
if (!c64.dutyIsAbs) for (int j=0; j<std.dutyMacro.len; j++) {
@ -3489,6 +3507,24 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
}
}
// <187 C64 cutoff macro compatibility
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
std.algMacro.macroType=DIV_MACRO_ALG;
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
if (!c64.filterIsAbs) {
for (int i=0; i<std.algMacro.len; i++) {
std.algMacro.val[i]=-std.algMacro.val[i];
}
}
}
// <187 special/test/gate merge
if (type==DIV_INS_C64 && version<187) {
convertC64SpecialMacro();
}
return DIV_DATA_SUCCESS;
}
@ -3518,6 +3554,42 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS
return readInsDataOld(reader,version);
}
void DivInstrument::convertC64SpecialMacro() {
// merge special and test/gate macros into new special macro
int maxLen=MAX(std.ex3Macro.len,std.ex4Macro.len);
// skip if ex4 is not a sequence macro
if (std.ex4Macro.open&6) return;
// move ex4 macro up and fill in gate
for (int i=0; i<std.ex4Macro.len; i++) {
std.ex4Macro.val[i]=(std.ex4Macro.val[i]&1)?9:1;
}
// merge ex3 into ex4 if viable to
if (std.ex3Macro.len>0 && !(std.ex3Macro.open&6)) {
if (std.ex4Macro.len>0 && std.ex4Macro.len<maxLen) {
for (int i=std.ex4Macro.len; i<maxLen; i++) {
std.ex4Macro.val[i]=std.ex3Macro.val[std.ex4Macro.len-1];
}
} else {
for (int i=0; i<maxLen; i++) {
std.ex4Macro.val[i]=1;
}
}
for (int i=0; i<maxLen; i++) {
if (i>=std.ex3Macro.len) {
std.ex4Macro.val[i]|=(std.ex3Macro.val[std.ex3Macro.len-1]&3)<<1;
} else {
std.ex4Macro.val[i]|=(std.ex3Macro.val[i]&3)<<1;
}
}
}
std.ex4Macro.len=maxLen;
std.ex3Macro=DivInstrumentMacro(DIV_MACRO_EX3);
}
bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song, bool writeInsName) {
SafeWriter* w=new SafeWriter();
w->init();
@ -3709,7 +3781,7 @@ bool DivInstrument::saveDMP(const char* path) {
w->writeC(c64.ringMod);
w->writeC(c64.oscSync);
w->writeC(c64.toFilter);
w->writeC(c64.volIsCutoff);
w->writeC(0); // this was volIsCutoff...
w->writeC(c64.initFilter);
w->writeC(c64.res);
w->writeC((c64.cut*100)/2047);

View file

@ -413,7 +413,7 @@ struct DivInstrumentC64 {
unsigned char a, d, s, r;
unsigned short duty;
unsigned char ringMod, oscSync;
bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs, noTest;
bool toFilter, initFilter, dutyIsAbs, filterIsAbs, noTest;
unsigned char res;
unsigned short cut;
bool hp, lp, bp, ch3off;
@ -436,7 +436,6 @@ struct DivInstrumentC64 {
ringMod(0),
oscSync(0),
toFilter(false),
volIsCutoff(false),
initFilter(false),
dutyIsAbs(false),
filterIsAbs(false),
@ -869,7 +868,7 @@ struct DivInstrument {
void readFeatureNA(SafeReader& reader, short version);
void readFeatureFM(SafeReader& reader, short version);
void readFeatureMA(SafeReader& reader, short version);
void readFeature64(SafeReader& reader, short version);
void readFeature64(SafeReader& reader, bool& volIsCutoff, short version);
void readFeatureGB(SafeReader& reader, short version);
void readFeatureSM(SafeReader& reader, short version);
void readFeatureOx(SafeReader& reader, int op, short version);
@ -890,6 +889,8 @@ struct DivInstrument {
DivDataErrors readInsDataOld(SafeReader& reader, short version);
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
void convertC64SpecialMacro();
/**
* save the instrument to a SafeWriter.
* @param w the SafeWriter in question.

View file

@ -81,7 +81,18 @@ const char** DivPlatformAmiga::getRegisterSheet() {
void DivPlatformAmiga::acquire(short** buf, size_t len) {
thread_local int outL, outR, output;
for (size_t h=0; h<len; h++) {
if (--delay<0) delay=0;
if (!writes.empty() && delay<=0) {
QueuedWrite w=writes.front();
if (w.addr==0x96 && !(w.val&0x8000)) delay=4096/AMIGA_DIVIDER;
amiga.write(w.addr,w.val);
writes.pop();
}
bool hsync=bypassLimits;
outL=0;
outR=0;
@ -99,7 +110,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
}
for (int i=0; i<4; i++) {
// run DMA
if (amiga.dmaEn && amiga.audEn[i] && !amiga.audIr[i]) {
if (amiga.audEn[i]) amiga.mustDMA[i]=true;
if (amiga.dmaEn && amiga.mustDMA[i] && !amiga.audIr[i]) {
amiga.audTick[i]-=AMIGA_DIVIDER;
if (amiga.audTick[i]<0) {
amiga.audTick[i]+=MAX(AMIGA_DIVIDER,amiga.audPer[i]);
@ -114,6 +126,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
amiga.audWord[i]=!amiga.audWord[i];
}
amiga.mustDMA[i]=amiga.audEn[i];
amiga.audByte[i]=!amiga.audByte[i];
if (!amiga.audByte[i] && (amiga.useV[i] || amiga.useP[i])) {
amiga.nextOut2[i]=((unsigned char)amiga.audDat[0][i])<<8|((unsigned char)amiga.audDat[1][i]);
@ -130,7 +144,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
amiga.audPer[i+1]=amiga.nextOut2[i];
}
}
} else {
} else if (!amiga.useV[i] && !amiga.useP[i]) {
amiga.nextOut[i]=amiga.audDat[amiga.audByte[i]][i];
}
}
@ -154,12 +168,12 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
// output
if (!isMuted[i]) {
if (amiga.audVol[i]>=64) {
if ((amiga.audVol[i]&127)>=64) {
output=amiga.nextOut[i]<<6;
} else if (amiga.audVol[i]<=0) {
} else if ((amiga.audVol[i]&127)==0) {
output=0;
} else {
output=amiga.nextOut[i]*volTable[amiga.audVol[i]][amiga.volPos];
output=amiga.nextOut[i]*volTable[amiga.audVol[i]&63][amiga.volPos];
}
if (i==0 || i==3) {
outL+=(output*sep1)>>7;
@ -168,7 +182,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
outL+=(output*sep2)>>7;
outR+=(output*sep1)>>7;
}
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<1;
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]&127))<<1;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}
@ -190,11 +204,6 @@ void DivPlatformAmiga::irq(int ch) {
if (chan[ch].irLocL==0x400 && chan[ch].irLocH==0 && chan[ch].irLen==1) {
// turn off DMA
rWrite(0x96,1<<ch);
} else {
// write latched loc/len
chWrite(ch,0,chan[ch].irLocH);
chWrite(ch,2,chan[ch].irLocL);
chWrite(ch,4,chan[ch].irLen);
}
// acknowledge interrupt
@ -202,111 +211,96 @@ void DivPlatformAmiga::irq(int ch) {
}
#define UPDATE_DMA(x) \
amiga.dmaLen[x]=amiga.audLen[x]; \
amiga.dmaLoc[x]=amiga.audLoc[x]; \
amiga.audByte[x]=true; \
amiga.audTick[x]=0;
dmaLen[x]=audLen[x]; \
dmaLoc[x]=audLoc[x]; \
audByte[x]=true; \
audTick[x]=0;
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
void DivPlatformAmiga::Amiga::write(unsigned short addr, unsigned short val) {
if (addr&1) return;
//logV("%.3x = %.4x",addr,val);
regPool[addr>>1]=val;
if (!skipRegisterWrites && dumpWrites) {
addWrite(addr,val);
}
switch (addr&0x1fe) {
case 0x96: { // DMACON
if (val&32768) {
if (val&1) amiga.audEn[0]=true;
if (val&2) amiga.audEn[1]=true;
if (val&4) amiga.audEn[2]=true;
if (val&8) amiga.audEn[3]=true;
if (val&512) amiga.dmaEn=true;
if (val&1) audEn[0]=true;
if (val&2) audEn[1]=true;
if (val&4) audEn[2]=true;
if (val&8) audEn[3]=true;
if (val&512) dmaEn=true;
} else {
if (val&1) {
amiga.audEn[0]=false;
UPDATE_DMA(0);
audEn[0]=false;
}
if (val&2) {
amiga.audEn[1]=false;
UPDATE_DMA(1);
audEn[1]=false;
}
if (val&4) {
amiga.audEn[2]=false;
UPDATE_DMA(2);
audEn[2]=false;
}
if (val&8) {
amiga.audEn[3]=false;
UPDATE_DMA(3);
audEn[3]=false;
}
if (val&512) {
amiga.dmaEn=false;
dmaEn=false;
}
}
break;
}
case 0x9a: { // INTENA
if (val&32768) {
if (val&128) amiga.audInt[0]=true;
if (val&256) amiga.audInt[1]=true;
if (val&512) amiga.audInt[2]=true;
if (val&1024) amiga.audInt[3]=true;
if (val&128) audInt[0]=true;
if (val&256) audInt[1]=true;
if (val&512) audInt[2]=true;
if (val&1024) audInt[3]=true;
} else {
if (val&128) amiga.audInt[0]=false;
if (val&256) amiga.audInt[1]=false;
if (val&512) amiga.audInt[2]=false;
if (val&1024) amiga.audInt[3]=false;
if (val&128) audInt[0]=false;
if (val&256) audInt[1]=false;
if (val&512) audInt[2]=false;
if (val&1024) audInt[3]=false;
}
break;
}
case 0x9c: { // INTREQ
if (val&32768) {
if (val&128) {
amiga.audIr[0]=true;
irq(0);
audIr[0]=true;
}
if (val&256) {
amiga.audIr[1]=true;
irq(1);
audIr[1]=true;
}
if (val&512) {
amiga.audIr[2]=true;
irq(2);
audIr[2]=true;
}
if (val&1024) {
amiga.audIr[3]=true;
irq(3);
audIr[3]=true;
}
} else {
if (val&128) amiga.audIr[0]=false;
if (val&256) amiga.audIr[1]=false;
if (val&512) amiga.audIr[2]=false;
if (val&1024) amiga.audIr[3]=false;
if (val&128) audIr[0]=false;
if (val&256) audIr[1]=false;
if (val&512) audIr[2]=false;
if (val&1024) audIr[3]=false;
}
break;
}
case 0x9e: { // ADKCON
if (val&32768) {
if (val&1) amiga.useV[0]=true;
if (val&2) amiga.useV[1]=true;
if (val&4) amiga.useV[2]=true;
if (val&8) amiga.useV[3]=true;
if (val&16) amiga.useP[0]=true;
if (val&32) amiga.useP[1]=true;
if (val&64) amiga.useP[2]=true;
if (val&128) amiga.useP[3]=true;
if (val&1) useV[0]=true;
if (val&2) useV[1]=true;
if (val&4) useV[2]=true;
if (val&8) useV[3]=true;
if (val&16) useP[0]=true;
if (val&32) useP[1]=true;
if (val&64) useP[2]=true;
if (val&128) useP[3]=true;
} else {
if (val&1) amiga.useV[0]=false;
if (val&2) amiga.useV[1]=false;
if (val&4) amiga.useV[2]=false;
if (val&8) amiga.useV[3]=false;
if (val&16) amiga.useP[0]=false;
if (val&32) amiga.useP[1]=false;
if (val&64) amiga.useP[2]=false;
if (val&128) amiga.useP[3]=false;
if (val&1) useV[0]=false;
if (val&2) useV[1]=false;
if (val&4) useV[2]=false;
if (val&8) useV[3]=false;
if (val&16) useP[0]=false;
if (val&32) useP[1]=false;
if (val&64) useP[2]=false;
if (val&128) useP[3]=false;
}
break;
}
@ -316,31 +310,31 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
bool updateDMA=false;
switch (addr&15) {
case 0: // LCH
amiga.audLoc[ch]&=0xffff;
amiga.audLoc[ch]|=val<<16;
audLoc[ch]&=0xffff;
audLoc[ch]|=val<<16;
updateDMA=true;
break;
case 2: // LCL
amiga.audLoc[ch]&=0xffff0000;
amiga.audLoc[ch]|=val&0xfffe;
audLoc[ch]&=0xffff0000;
audLoc[ch]|=val&0xfffe;
updateDMA=true;
break;
case 4: // LEN
amiga.audLen[ch]=val;
audLen[ch]=val;
updateDMA=true;
break;
case 6: // PER
amiga.audPer[ch]=val;
audPer[ch]=val;
break;
case 8: // VOL
amiga.audVol[ch]=val;
audVol[ch]=val;
break;
case 10: // DAT
amiga.audDat[0][ch]=val&0xff;
amiga.audDat[1][ch]=val>>8;
audDat[0][ch]=val&0xff;
audDat[1][ch]=val>>8;
break;
}
if (updateDMA && !amiga.audEn[ch]) {
if (updateDMA && !mustDMA[ch]) {
UPDATE_DMA(ch);
}
}
@ -349,6 +343,20 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
}
}
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
if (addr&1) return;
//logV("%.3x = %.4x",addr,val);
if (!skipRegisterWrites) {
writes.push(QueuedWrite(addr,val));
regPool[addr>>1]=val;
if (dumpWrites) {
addWrite(addr,val);
}
}
}
void DivPlatformAmiga::updateWave(int ch) {
for (int i=0; i<MIN(256,(chan[ch].audLen<<1)); i++) {
sampleMem[(ch<<8)|i]=chan[ch].ws.output[i]^0x80;
@ -417,6 +425,13 @@ void DivPlatformAmiga::tick(bool sysTick) {
if (dmaOff) rWrite(0x96,dmaOff);
for (int i=0; i<4; i++) {
if (chan[i].updateWave) {
chan[i].updateWave=false;
updateWave(i);
}
}
for (int i=0; i<4; i++) {
double off=1.0;
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
@ -509,9 +524,16 @@ void DivPlatformAmiga::tick(bool sysTick) {
if (dmaOn) rWrite(0x96,0x8000|dmaOn);
for (int i=0; i<4; i++) {
if ((dmaOn&(1<<i)) && !chan[i].useWave && dumpWrites) {
if ((dmaOn&(1<<i)) && !chan[i].useWave) {
// write latched loc/len
if (dumpWrites) {
addWrite(0x200+i,(chan[i].irLocH<<16)|chan[i].irLocL);
addWrite(0x204+i,chan[i].irLen);
} else {
chWrite(i,0,chan[i].irLocH);
chWrite(i,2,chan[i].irLocL);
chWrite(i,4,chan[i].irLen);
}
}
}
@ -520,10 +542,6 @@ void DivPlatformAmiga::tick(bool sysTick) {
chan[i].writeVol=false;
chWrite(i,8,chan[i].outVol);
}
if (chan[i].updateWave) {
chan[i].updateWave=false;
updateWave(i);
}
}
if (updateADKCon) {
@ -720,6 +738,7 @@ void DivPlatformAmiga::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].writeVol=true;
/*chan[i].keyOn=false;
chan[i].keyOff=false;
chan[i].sample=-1;*/
@ -738,6 +757,7 @@ DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) {
}
void DivPlatformAmiga::reset() {
writes.clear();
memset(regPool,0,256*sizeof(unsigned short));
for (int i=0; i<4; i++) {
chan[i]=DivPlatformAmiga::Channel();
@ -750,6 +770,7 @@ void DivPlatformAmiga::reset() {
filterOn=false;
filtConst=filterOn?filtConstOn:filtConstOff;
updateADKCon=true;
delay=0;
amiga=Amiga();
// enable DMA

View file

@ -21,6 +21,7 @@
#define _AMIGA_H
#include "../dispatch.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
class DivPlatformAmiga: public DivDispatch {
@ -59,12 +60,14 @@ class DivPlatformAmiga: public DivDispatch {
bool amigaModel;
bool filterOn;
bool updateADKCon;
short delay;
struct Amiga {
// register state
bool audInt[4]; // interrupt on
bool audIr[4]; // interrupt request
bool audEn[4]; // audio DMA on
bool mustDMA[4]; // audio DMA must run
bool useP[4]; // period modulation
bool useV[4]; // volume modulation
@ -91,6 +94,8 @@ class DivPlatformAmiga: public DivDispatch {
unsigned short hPos; // horizontal position of beam
unsigned char state[4]; // current channel state
void write(unsigned short addr, unsigned short val);
Amiga() {
memset(this,0,sizeof(*this));
}
@ -113,6 +118,14 @@ class DivPlatformAmiga: public DivDispatch {
int sep1, sep2;
struct QueuedWrite {
unsigned short addr;
unsigned short val;
QueuedWrite(): addr(0), val(9) {}
QueuedWrite(unsigned short a, unsigned short v): addr(a), val(v) {}
};
FixedQueue<QueuedWrite,512> writes;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
friend class DivExportAmigaValidation;

View file

@ -20,6 +20,7 @@
#include "c64.h"
#include "../engine.h"
#include "sound/c64_fp/siddefs-fp.h"
#include "IconsFontAwesome4.h"
#include <math.h>
#include "../../ta-log.h"
@ -158,21 +159,10 @@ void DivPlatformC64::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
if (ins->c64.volIsCutoff) {
if (ins->c64.filterIsAbs) {
filtCut=MIN(2047,chan[i].std.vol.val);
} else {
filtCut-=((signed char)chan[i].std.vol.val)*7;
if (filtCut>2047) filtCut=2047;
if (filtCut<0) filtCut=0;
}
willUpdateFilter=true;
} else {
vol=MIN(15,chan[i].std.vol.val);
willUpdateFilter=true;
}
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
@ -186,14 +176,18 @@ void DivPlatformC64::tick(bool sysTick) {
if (ins->c64.dutyIsAbs) {
chan[i].duty=chan[i].std.duty.val;
} else {
if (multiplyRel) {
chan[i].duty-=((signed char)chan[i].std.duty.val)*4;
} else {
chan[i].duty-=chan[i].std.duty.val;
}
}
rWrite(i*7+2,chan[i].duty&0xff);
rWrite(i*7+3,chan[i].duty>>8);
}
if (chan[i].std.wave.had) {
chan[i].wave=chan[i].std.wave.val;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active && chan[i].gate));
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
@ -204,6 +198,21 @@ void DivPlatformC64::tick(bool sysTick) {
}
chan[i].freqChanged=true;
}
if (chan[i].std.alg.had) { // new cutoff macro
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
if (ins->c64.filterIsAbs) {
filtCut=MIN(2047,chan[i].std.alg.val);
} else {
if (multiplyRel) {
filtCut+=((signed char)chan[i].std.alg.val)*7;
} else {
filtCut+=chan[i].std.alg.val;
}
if (filtCut>2047) filtCut=2047;
if (filtCut<0) filtCut=0;
}
willUpdateFilter=true;
}
if (chan[i].std.ex1.had) {
filtControl=chan[i].std.ex1.val&15;
willUpdateFilter=true;
@ -212,15 +221,33 @@ void DivPlatformC64::tick(bool sysTick) {
filtRes=chan[i].std.ex2.val&15;
willUpdateFilter=true;
}
if (chan[i].std.ex3.had) {
chan[i].sync=chan[i].std.ex3.val&1;
chan[i].ring=chan[i].std.ex3.val&2;
chan[i].freqChanged=true;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
}
if (chan[i].std.ex4.had) {
chan[i].test=chan[i].std.ex4.val&1;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
chan[i].gate=chan[i].std.ex4.val&1;
chan[i].sync=chan[i].std.ex4.val&2;
chan[i].ring=chan[i].std.ex4.val&4;
chan[i].test=chan[i].std.ex4.val&8;
chan[i].freqChanged=true;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active && chan[i].gate));
}
if (chan[i].std.ex5.had) {
chan[i].attack=chan[i].std.ex5.val&15;
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
}
if (chan[i].std.ex6.had) {
chan[i].decay=chan[i].std.ex6.val&15;
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
}
if (chan[i].std.ex7.had) {
chan[i].sustain=chan[i].std.ex7.val&15;
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
}
if (chan[i].std.ex8.had) {
chan[i].release=chan[i].std.ex8.val&15;
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
}
if (sysTick) {
@ -243,7 +270,7 @@ void DivPlatformC64::tick(bool sysTick) {
if (chan[i].keyOn) {
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(chan[i].gate?1:0));
}
if (chan[i].keyOff) {
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
@ -387,7 +414,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
@ -396,7 +423,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) {
if (parent->song.resetMacroOnPorta || parent->song.preNoteNoEffect) {
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_C64));
chan[c.chan].keyOn=true;
}
@ -481,11 +508,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case 4:
chan[c.chan].ring=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
break;
case 5:
chan[c.chan].sync=c.value;
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
break;
case 6:
filtControl&=7;
@ -568,6 +595,24 @@ DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivChannelModeHints DivPlatformC64::getModeHints(int ch) {
DivChannelModeHints ret;
ret.count=1;
ret.hint[0]=ICON_FA_BELL_SLASH_O;
ret.type[0]=0;
if (ch==2 && (filtControl&8)) {
ret.type[0]=7;
} else if (chan[ch].test && !chan[ch].gate) {
ret.type[0]=5;
} else if (chan[ch].test) {
ret.type[0]=6;
} else if (!chan[ch].gate) {
ret.type[0]=4;
}
return ret;
}
DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -616,7 +661,18 @@ void DivPlatformC64::reset() {
needInitTables=false;
} else if (sidCore==1) {
sid_fp->reset();
sid_fp->clockSilent(16000);
for (int i=0; i<3; i++) {
sid_fp->write(i*7+5,testAD);
sid_fp->write(i*7+6,testSR);
sid_fp->write(i*7+4,8);
}
sid_fp->clockSilent(30000);
for (int i=0; i<3; i++) {
sid_fp->write(i*7+5,testAD);
sid_fp->write(i*7+6,testSR);
sid_fp->write(i*7+4,0);
}
sid_fp->clockSilent(30000);
} else {
sid->reset();
}
@ -627,7 +683,7 @@ void DivPlatformC64::reset() {
filtControl=7;
filtRes=0;
filtCut=2047;
resetTime=1;
resetTime=initResetTime;
vol=15;
chanOrder[0]=0;
@ -675,8 +731,11 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
}
keyPriority=flags.getBool("keyPriority",true);
no1EUpdate=flags.getBool("no1EUpdate",false);
multiplyRel=flags.getBool("multiplyRel",false);
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15);
initResetTime=flags.getInt("initResetTime",2);
if (initResetTime<0) initResetTime=1;
// init fake filter table
// taken from dSID

View file

@ -26,13 +26,17 @@
#include "sound/c64_fp/SID.h"
#include "sound/c64_d/dsid.h"
// TODO:
// - ex3 (special) unify with ex4 (gate/test)
// - ex4 (test) compatibility
class DivPlatformC64: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
int prevFreq, testWhen;
unsigned char sweep, wave, attack, decay, sustain, release;
short duty;
bool sweepChanged, filter;
bool resetMask, resetFilter, resetDuty, ring, sync, test;
bool resetMask, resetFilter, resetDuty, gate, ring, sync, test;
Channel():
SharedChannel<signed char>(15),
prevFreq(65535),
@ -49,6 +53,7 @@ class DivPlatformC64: public DivDispatch {
resetMask(false),
resetFilter(false),
resetDuty(false),
gate(true),
ring(false),
sync(false),
test(false) {}
@ -70,9 +75,9 @@ class DivPlatformC64: public DivDispatch {
unsigned char filtControl, filtRes, vol;
unsigned char writeOscBuf;
unsigned char sidCore;
int filtCut, resetTime;
int filtCut, resetTime, initResetTime;
bool keyPriority, sidIs6581, needInitTables, no1EUpdate;
bool keyPriority, sidIs6581, needInitTables, no1EUpdate, multiplyRel;
unsigned char chanOrder[3];
unsigned char testAD, testSR;
@ -108,6 +113,7 @@ class DivPlatformC64: public DivDispatch {
bool isVolGlobal();
float getPostAmp();
DivMacroInt* getChanMacroInt(int ch);
DivChannelModeHints getModeHints(int chan);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -800,7 +800,7 @@ DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) {
}
unsigned short DivPlatformGenesisExt::getPan(int ch) {
if (ch==csmChan) return 0;
if (ch==4+csmChan) return 0;
if (ch>=4+extChanOffs) return DivPlatformGenesis::getPan(ch-3);
if (ch>=extChanOffs) {
if (extMode) {

View file

@ -19,6 +19,7 @@
#include "pce.h"
#include "../engine.h"
#include "furIcons.h"
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
@ -512,6 +513,18 @@ unsigned short DivPlatformPCE::getPan(int ch) {
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
}
DivChannelModeHints DivPlatformPCE::getModeHints(int ch) {
DivChannelModeHints ret;
if (ch<4) return ret;
ret.count=1;
ret.hint[0]=ICON_FUR_NOISE;
ret.type[0]=0;
if (chan[ch].noise) ret.type[0]=4;
return ret;
}
DivSamplePos DivPlatformPCE::getSamplePos(int ch) {
if (ch>=6) return DivSamplePos();
if (!chan[ch].pcm) return DivSamplePos();

View file

@ -83,6 +83,7 @@ class DivPlatformPCE: public DivDispatch {
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivChannelModeHints getModeHints(int chan);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();

View file

@ -20,6 +20,7 @@
#include "snes.h"
#include "../engine.h"
#include "../../ta-log.h"
#include "furIcons.h"
#include <math.h>
#define CHIP_FREQBASE 131072
@ -710,6 +711,63 @@ DivChannelPair DivPlatformSNES::getPaired(int ch) {
return DivChannelPair();
}
DivChannelModeHints DivPlatformSNES::getModeHints(int ch) {
DivChannelModeHints ret;
ret.count=1;
ret.hint[0]="-";
ret.type[0]=0;
const SPC_DSP::voice_t* v=dsp.get_voice(ch);
if (v!=NULL) {
if (v->regs[5]&128) {
switch (v->env_mode) {
case SPC_DSP::env_attack:
ret.hint[0]=ICON_FUR_ADSR_A;
ret.type[0]=12;
break;
case SPC_DSP::env_decay:
ret.hint[0]=ICON_FUR_ADSR_D;
ret.type[0]=13;
break;
case SPC_DSP::env_sustain:
ret.hint[0]=ICON_FUR_ADSR_S;
ret.type[0]=14;
break;
case SPC_DSP::env_release:
ret.hint[0]=ICON_FUR_ADSR_R;
ret.type[0]=15;
break;
}
} else {
if (v->regs[7]&128) {
switch (v->regs[7]&0x60) {
case 0:
ret.hint[0]=ICON_FUR_DEC_LINEAR;
ret.type[0]=16;
break;
case 32:
ret.hint[0]=ICON_FUR_DEC_EXP;
ret.type[0]=17;
break;
case 64:
ret.hint[0]=ICON_FUR_INC_LINEAR;
ret.type[0]=18;
break;
case 96:
ret.hint[0]=ICON_FUR_INC_BENT;
ret.type[0]=19;
break;
}
} else {
ret.hint[0]=ICON_FUR_VOL_DIRECT;
ret.type[0]=20;
}
}
}
return ret;
}
DivSamplePos DivPlatformSNES::getSamplePos(int ch) {
if (ch>=8) return DivSamplePos();
if (!chan[ch].active) return DivSamplePos();

View file

@ -102,6 +102,7 @@ class DivPlatformSNES: public DivDispatch {
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivChannelPair getPaired(int chan);
DivChannelModeHints getModeHints(int chan);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();

View file

@ -19,6 +19,8 @@
#include "swan.h"
#include "../engine.h"
#include "furIcons.h"
#include "IconsFontAwesome4.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}
@ -480,6 +482,30 @@ unsigned short DivPlatformSwan::getPan(int ch) {
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
}
DivChannelModeHints DivPlatformSwan::getModeHints(int ch) {
DivChannelModeHints ret;
switch (ch) {
case 1: // PCM
ret.count=1;
ret.hint[0]=ICON_FA_VOLUME_UP;
ret.type[0]=pcm?4:0;
break;
case 2: // sweep
ret.count=1;
ret.hint[0]=ICON_FUR_SAW;
ret.type[0]=sweep?2:0;
break;
case 3: // noise
ret.count=1;
ret.hint[0]=ICON_FUR_NOISE;
ret.type[0]=noise?4:0;
break;
}
return ret;
}
DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -63,6 +63,7 @@ class DivPlatformSwan: public DivDispatch {
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivChannelModeHints getModeHints(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -1272,11 +1272,11 @@ void DivEngine::nextRow() {
for (int j=0; j<curPat[i].effectCols; j++) {
if (!song.preNoteNoEffect) {
if (pat->data[curRow][4+(j<<1)]==0x03) {
if (pat->data[curRow][4+(j<<1)]==0x03 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
doPreparePreNote=false;
break;
}
if (pat->data[curRow][4+(j<<1)]==0x06) {
if (pat->data[curRow][4+(j<<1)]==0x06 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
doPreparePreNote=false;
break;
}
@ -1303,11 +1303,11 @@ void DivEngine::nextRow() {
int addition=0;
for (int j=0; j<curPat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==0x03) {
if (pat->data[curRow][4+(j<<1)]==0x03 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
doPrepareCut=false;
break;
}
if (pat->data[curRow][4+(j<<1)]==0x06) {
if (pat->data[curRow][4+(j<<1)]==0x06 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
doPrepareCut=false;
break;
}

View file

@ -597,7 +597,7 @@ void DivEngine::registerSystems() {
{0x1a, {DIV_CMD_C64_RESET_MASK, "1Axx: Disable envelope reset for this channel (1 disables; 0 enables)"}},
{0x1b, {DIV_CMD_C64_FILTER_RESET, "1Bxy: Reset cutoff (x: on new note; y: now)"}},
{0x1c, {DIV_CMD_C64_DUTY_RESET, "1Cxy: Reset pulse width (x: on new note; y: now)"}},
{0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change additional parameters"}},
{0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change other parameters (LEGACY)"}},
{0x20, {DIV_CMD_C64_AD, "20xy: Set attack/decay (x: attack; y: decay)"}},
{0x21, {DIV_CMD_C64_SR, "21xy: Set sustain/release (x: sustain; y: release)"}},
};

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