Merge branch 'master' into doc-general

This commit is contained in:
Electric Keet 2023-08-04 20:39:23 -07:00
commit 211a67e0bb
65 changed files with 3756 additions and 3750 deletions

BIN
demos/gameboy/dtect.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/genesis/mm5_boss.fur Normal file

Binary file not shown.

Binary file not shown.

View file

@ -20,4 +20,10 @@ it also has a flexible windowing system which you may move around and organize.
see [2-interface](../2-interface/README.md) and [3-pattern](../3-pattern/README.md) for more information.
once familiar with the tracker, look to [9-guides](../9-guides/README.md) for useful techniques.
once familiar with the tracker, look to [9-guides](../9-guides/README.md) for useful techniques.
# links
[Furnace Tutorials](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1): video tutorials created by Spinning Square Waves.

View file

@ -13,7 +13,7 @@ primary topics:
- [play/edit controls](play-edit-controls.md)
- [instrument/wavetable/sample list](asset-list.md)
- [song information](song-info.md)
- [effect list window](effect-list.md)
- [effect list window](effect-list-window.md)
- [pattern view](../3-pattern/README.md)
- [instrument editor](../4-instrument/README.md)
- [wavetable editor](../5-wave/README.md)

View file

@ -0,0 +1,10 @@
# effect list window
(for more details about these effects, see [the effects page](../3-pattern/effects.md))
![effect list window](effect-list.png)
this window provides a list of the effects that are available.
- **Chip at cursor**: the currently selected chip. the list only shows available effects for this chip.
- menu button: opens a small list of effect categories. toggle each to change whether they're shown in the list.

View file

@ -4,12 +4,38 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
# General
## General
- **Workspace layout**
- **Import**: reads a .ini layout file.
- **Export**: writes current layout to a .ini file.
- **Reset**: resets layout to default.
### Program
- **Render backend**
- changing this may help with performace issues.
- **Late render clear**
- **Power-saving mode**
- saves power by lowering the frame rate to 2fps when idle.
- may cause issues under Mesa drivers!
- **Disable threaded input (restart after changing!)**
- threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.
- however, crashes have been reported when threaded input is on. enable this option if that is the case.
- **Enable event delay**
- may cause issues with high-polling-rate mice when previewing notes.
### File
- **Use system file picker**: use native OS file dialog instead of Furnace's.
- **Number of recent files**
- **Compress when saving**
- use zlib to compress saved songs.
- **Save unused patterns**
- **Use new pattern format when saving**
- **Don't apply compatibility flags when loading .dmf**
- **Audio export loop/fade out time:**
- **Set to these values on start-up:**
- **Loops**: number of additional times to play through `0Bxx` song loop.
- **Fade out (seconds)**: length of fade out after final loop.
- **Remember last values**
### Chip
- **Initial system**: the system of chips loaded on starting Furnace.
- **Current system**: sets current chips as default.
@ -18,172 +44,131 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **Reset to defaults**: sets default to "Sega Genesis/Mega Drive".
- **Name**: name for the default system. may be set to any text.
- system configuration: same as in the [chip manager](../8-advanced/chip-manager.md) and [mixer](../8-advanced/mixer.md).
- **When creating new song**:
- **Display system preset selector**
- **Start with initial system**
- **Restart song when changing chip properties**
### Start-up
- **Play intro on start-up:**
- **No**: skips intro entirely.
- **Short**: shows silent title screen briefly.
- **Full (short when loading song)**: shows animated musical intro unless started with a song (command line, double-clicking a .fur file, etc.)
- **Full (always)**: always shows animated musical intro.
- **When creating new song**:
- **Display system preset selector**
- **Start with initial system**
- **Disable fade-in during start-up**
- **About screen party time**
- _warning:_ may cause epileptic seizures.
### Behavior
- **Double-click time (seconds)**: maximum time between mouse clicks to recognize them as a double-click.
- **Toggle channel solo on:** select which interactions with a channel header will toggle solo for that channel.
- **Push value when overwriting instead of clearing it**: in the order list and pattern editors, typing into an already-filled value will shift digits instead of starting fresh.
- if off: moving the cursor onto the value `A5` and typing a "B" results in `0B`.
- if on: with the cursor on the value `A5` and typing a "B" results in `5B`.
- **Move cursor up on backspace-delete**
- **Move cursor by edit step on delete**
- **Change current instrument when changing instrument column (absorb)**
- **Delete effect value when deleting effect**
- **Change order when scrolling outside of pattern bounds**:
- if off, the pattern edit cursor will stay locked within the current order.
- if on, moving the cursor past the edge of the previous or next order will move to that order.
- **Move cursor by edit step on insert (push)**
- **Move cursor to end of clipboard content when pasting**
- **Don't scroll when moving cursor**
- **Double click selects entire column**
- **Allow docking editors**
- **Don't raise pattern editor on click**
- **Focus pattern editor when selecting instrument**
- **Restart song when changing chip properties**
- **Use system file picker**: use native OS file dialog instead of Furnace's.
- **Only allow window movement when clicking on title bar**
- **Enable event delay**
- may cause issues with high-polling-rate mice when previewing notes.
- **Power-saving mode**
- saves power by lowering the frame rate to 2fps when idle.
- may cause issues under Mesa drivers!
- **Disable threaded input (restart after changing!)**
- threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.
- however, crashes have been reported when threaded input is on. enable this option if that is the case.
- **Remember window position**
- remembers the window's last position on start-up.
- **New instruments are blank**
- **Save unused patterns**
- **Compress when saving**
- use zlib to compress saved songs.
- **Cursor follows current order when moving it**
- applies when playback is stopped.
- **Audio export loop/fade out time:**
- **Set to these values on start-up:**
- **Loops**: number of additional times to play through `0Bxx` song loop.
- **Fade out (seconds)**: length of fade out after final loop.
- **Remember last values**
- **Note preview behavior:**
- **Never**
- **When cursor is in Note column**
- **When cursor is in Note column or not in edit mode**
- **Always**
- **Wrap pattern cursor horizontally:**
- **No**
- **Yes**
- **Yes, and move to next/prev row**
- **Wrap pattern cursor vertically:**
- **No**
- **Yes**
- **Yes, and move to next/prev pattern**
- **Cursor movement keys behavior:**
- **Move by one**
- **Move by Edit Step**
- **Effect input cursor behavior:**
- **Move down**
- **Move to effect value (otherwise move down)**
- **Move to effect value/next effect and wrap around**
- **Allow dragging selection:**
- **No**
- **Yes**
- **Yes (while holding Ctrl only)**
# Audio/MIDI
## Audio
### Output
- **Backend**: select SDL or JACK for audio output.
- only appears on Linux, or MacOS compiled with JACK support
- **Driver**
- **Device**: audio device for playback.
- **Sample rate**
- **Outputs**: select number of audio outputs created, up to 16.
- only appears when Backend is JACK.
- **Channels**: number of output channels to use.
- **Buffer size**: size of buffer in both samples and milliseconds.
- **Quality**: selects quality of resampling. low quality reduces CPU load.
- **Metronome volume**
- **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode.
- _warning:_ experimental! may produce glitches. only enable if your buffer size is small (10ms or less).
- **Force mono audio**
- **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.
- **want:** displays requested audio configuration.
- **got:** displays actual audio configuration returned by audio backend.
- **MIDI input**
- **MIDI output**
- **MIDI input settings**
- **Note input**
- **Velocity input**
- **Map MIDI channels to direct channels**
- **Map Yamaha FM voice data to instruments**
- **Program change is instrument selection**
- **Value input style**:
### Mixing
- **Quality**: selects quality of resampling. low quality reduces CPU load.
- **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.
### Metronome
- **Metronome volume**
## MIDI
### MIDI input
- **MIDI input**: input device.
- **Note input**
- **Velocity input**
- **Map MIDI channels to direct channels**
- **Map Yamaha FM voice data to instruments**
- **Program change is instrument selection**
- **Value input style**:
- **Disabled/custom**
- **Two octaves (0 is C-4, F is D#5)**
- **Raw (note number is value)**
- **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**
- **Use dual control change (one for each nibble)**
- **CC of upper nibble**
- **CC of lower nibble**
- **Use 14-bit control change**
- **MSB CC**
- **LSB CC**
- **Use single control change**
- **Control**
- **Per-column control change**
- **Instrument**\
**Volume**\
**Effect `x` type**\
**Effect `x` value**
- **Disabled/custom**
- **Two octaves (0 is C-4, F is D#5)**
- **Raw (note number is value)**
- **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**
- **Use dual control change (one for each nibble)**
- **CC of upper nibble**
- **CC of lower nibble**
- **Use 14-bit control change**
- **MSB CC**
- **LSB CC**
- **Use single control change**
- **Use single control change (imprecise)**
- **Control**
- **Per-column control change**
- **Instrument**\
**Volume**\
**Effect `x` type**\
**Effect `x` value**
- **Disabled/custom**
- **Use dual control change (one for each nibble)**
- **CC of upper nibble**
- **CC of lower nibble**
- **Use 14-bit control change**
- **MSB CC**
- **LSB CC**
- **Use single control change (imprecise)**
- **Control**
- **Volume curve**
- **Actions:**
- **`+`** button: adds a new action.
- window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device.
- each action has the following:
- **Type**
- **Channel**
- **Note/Control**
- **Velocity/Value**
- **Action**
- **Learn**
- **Remove**
- **Volume curve**
- **Actions:**
- **`+`** button: adds a new action.
- window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device.
- each action has the following:
- **Type**
- **Channel**
- **Note/Control**
- **Velocity/Value**
- **Action**
- **Learn**
- **Remove**
- **MIDI output settings**
- **Output mode:**
- **Off (use for TX81Z)**
- **Melodic**
- **Send Program Change**
- **Send MIDI clock**
- **Send MIDI timecode**
- **Timecode frame rate:**
- **Closest to Tick Rate**
- **Film (24fps)**
- **PAL (25fps)**
- **NTSC drop (29.97fps)**
- **NTSC non-drop (30fps)**
### MIDI output
# Emulation
- **MIDI output**: output device.
- **Output mode:**
- **Off (use for TX81Z)**
- **Melodic**
- **Send Program Change**
- **Send MIDI clock**
- **Send MIDI timecode**
- **Timecode frame rate:**
- **Closest to Tick Rate**
- **Film (24fps)**
- **PAL (25fps)**
- **NTSC drop (29.97fps)**
- **NTSC non-drop (30fps)**
## Emulation
### Cores
- **Arcade/YM2151 core**\
**Genesis/YM2612 core**\
**SN76489 core**\
@ -202,16 +187,122 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
# Appearance
## Keyboard
### Keyboard
- **Import**
- **Export**
- **Reset defaults**
- several categories of keybinds...
- click on a keybind then enter a key or key combination to change it
- right-click to clear the keybind
## Interface
### Layout
- **Workspace layout**
- **Import**: reads a .ini layout file.
- **Export**: writes current layout to a .ini file.
- **Reset**: resets layout to default.
- **Allow docking editors**
- **Remember window position**
- remembers the window's last position on start-up.
- **Only allow window movement when clicking on title bar**
- **Play/edit controls layout:**
- **Classic**
- **Compact**
- **Compact (vertical)**
- **Split**
- **Position of buttons in Orders:**
- **Top**
- **Left**
- **Right**
### Mouse
- **Double-click time (seconds)**: maximum time between mouse clicks to recognize them as a double-click.
- **Don't raise pattern editor on click**
- **Focus pattern editor when selecting instrument**
- **Note preview behavior:**
- **Never**
- **When cursor is in Note column**
- **When cursor is in Note column or not in edit mode**
- **Always**
- **Allow dragging selection:**
- **No**
- **Yes**
- **Yes (while holding Ctrl only)**
- **Toggle channel solo on:** select which interactions with a channel header will toggle solo for that channel.
- Right-click or double click
- Right-click
- Double-click
- **Double click selects entire column**
### Cursor behavior
- **Insert pushes entire channel row**
- **Pull delete affects entire channel row**
- **Push value when overwriting instead of clearing it**: in the order list and pattern editors, typing into an already-filled value will shift digits instead of starting fresh.
- if off: moving the cursor onto the value `A5` and typing a "B" results in `0B`.
- if on: with the cursor on the value `A5` and typing a "B" results in `5B`.
- **Effect input behavior:**
- **Move down**
- **Move to effect value (otherwise move down)**
- **Move to effect value/next effect and wrap around**
- **Delete effect value when deleting effect**
- **Change current instrument when changing instrument column (absorb)**
### Cursor movement
- **Wrap horizontally:**
- **No**
- **Yes**
- **Yes, and move to next/prev row**
- **Wrap vertically:**
- **No**
- **Yes**
- **Yes, and move to next/prev pattern**
- **Yes, and move to next/prev pattern (wrap around)**
- **Cursor movement keys behavior:**
- **Move by one**
- **Move by Edit Step**
- **Move cursor by edit step on delete**
- **Move cursor by edit step on insert (push)**
- **Move cursor up on backspace-delete**
- **Move cursor to end of clipboard content when pasting**
### Scrolling
- **Change order when scrolling outside of pattern bounds**:
- **No**: the pattern edit cursor will stay locked within the current order.
- **Yes**: moving the cursor past the edge of the previous or next order will move to that order, but not past the start or end of a song.
- **Yes, and wrap around song**: as above, but will wrap from song end to start.
- **Cursor follows current order when moving it**
- applies when playback is stopped.
- **Don't scroll when moving cursor**
- **Move cursor with scroll wheel**
## Appearance
### Scaling
- **Render driver**
- **Automatic UI scaling factor**: automatically match the OS's UI scaling.
- **UI scaling factor**: only if "Automatic UI scaling factor" is off.
- **Icon size**
### Text
- **Main font**: if "Custom...", a file path selector will appear beneath.
- **Size**
- **Pattern font**: if "Custom...", a file path selector will appear beneath.
- **Size**
- **Icon size**
- **Display Japanese characters**\
**Display Chinese (Simplified) characters**\
**Display Chinese (Traditional) characters**\
@ -219,25 +310,7 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- only toggle these options if you have enough graphics memory.
- these are a temporary solution until dynamic font atlas is implemented in Dear ImGui.
- **Number of recent files**
- **Pattern view labels:**
- **Note off (3-char)**: default is `OFF`
- **Note release (3-char)**: default is `===`.
- **Macro release (3-char)**: default is `REL`.
- **Empty field (3-char)**: default is `...`.
- **Empty field (2-char)**: default is `..`.
- **Orders row number format:**
- **Decimal**
- **Hexadecimal**
- **Pattern row number format:**
- **Decimal**
- **Hexadecimal**
- **FM parameter names:**
- **Friendly**
- **Technical**
- **Technical (alternate)**
### Program
- **Title bar:**
- **Furnace**
@ -251,43 +324,40 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **File path**
- **Cursor details or file path**
- **Nothing**
- **Play/edit controls layout:**
- **Classic**
- **Compact**
- **Compact (vertical)**
- **Split**
- **Position of buttons in Orders:**
- **Top**
- **Left**
- **Right**
- **FM parameter editor layout:**
- **Modern**
- **Compact (2x2, classic)**
- **Compact (1x4)**
- **Compact (4x1)**
- **Alternate (2x2)**
- **Alternate (1x4)**
- **Alternate (4x1)**
- **Position of Sustain in FM editor:**
- **Between Decay and Sustain Rate**
- **After Release Rate**
- **Macro editor layout:**
- **Unified**
- **Mobile**
- **Grid**
- **Single (with list)**
- **Single (combo box)**
- **Namco 163 chip name**
### Orders
- **Highlight channel at cursor in Orders**
- **Orders row number format:**
- **Decimal**
- **Hexadecimal**
### Pattern
- **Center pattern view**: centers pattern horizontally in view.
- **Overflow pattern highlights**
- **Display previous/next pattern**
- **Pattern row number format:**
- **Decimal**
- **Hexadecimal**
- **Pattern view labels:**
- **Note off (3-char)**: default is `OFF`
- **Note release (3-char)**: default is `===`.
- **Macro release (3-char)**: default is `REL`.
- **Empty field (3-char)**: default is `...`.
- **Empty field (2-char)**: default is `..`.
- **Pattern view spacing after:** number of pixels of space between columns.
- **Note**
- **Instrument**
- **Volume**
- **Effect**
- **Effect value**
- **Single-digit effects for 00-0F**
- **Use flats instead of sharps**
- **Use German notation**: display `B` notes as `H`, and `A#` notes as `B`.
### Channel
- **Channel colors:**
- **Single**
- **Channel type**
- **Instrument type**
- **Channel name colors:**
- **Single**
- **Channel type**
- **Instrument type**
- **Channel style:**
- **Classic**
- **Line**
@ -310,60 +380,88 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **Regular**
- **Monospace**
- **Center channel name**
- **Channel colors:**
- **Single**
- **Channel type**
- **Instrument type**
- **Channel name colors:**
- **Single**
- **Channel type**
- **Instrument type**
### Assets
- **Colorize instrument editor using instrument type**
- **Use separate colors for carriers/modulators in FM editor**
- **Unified instrument/wavetable/sample list**
- **Horizontal instrument list**
- **Use standard OPL waveform names**
- **Overflow pattern highlights**
- **Display previous/next pattern**
- **Use German notation**: display `B` notes as `H`, and `A#` notes as `B`.
- **Single-digit effects for 00-0F**
- **Center pattern view**: centers pattern horizontally in view.
- **Unsigned FM detune values**
- **Highlight channel at cursor in Orders**
- **About screen party time**
- _warning:_ may cause epileptic seizures.
- **Colorize instrument editor using instrument type**
### Macro Editor
- **Macro editor layout:**
- **Unified**
- **Mobile**
- **Grid**
- **Single (with list)**
- **Single (combo box)**
- **Use classic macro editor vertical slider**
### Wave Editor
- **Use compact wave editor**
- **Use classic macro editor vertical slider**
### FM Editor
- **FM parameter names:**
- **Friendly**
- **Technical**
- **Technical (alternate)**
- **Use standard OPL waveform names**
- **FM parameter editor layout:**
- **Modern**
- **Compact (2x2, classic)**
- **Compact (1x4)**
- **Compact (4x1)**
- **Alternate (2x2)**
- **Alternate (1x4)**
- **Alternate (4x1)**
- **Position of Sustain in FM editor:**
- **Between Decay and Sustain Rate**
- **After Release Rate**
- **Use separate colors for carriers/modulators in FM editor**
- **Unsigned FM detune values**
### Statistics
- **Chip memory usage unit:**
- **Bytes**
- **Kilobytes**
### Oscilloscope
- **Rounded corners**
- **Border**
- **Fill entire window**
- **Waveform goes out of bounds**
### Windows
- **Rounded window corners**
- **Rounded buttons**
- **Rounded menu corners**
- **Borders around widgets**
- **Disable fade-in during start-up**
- **Oscilloscope settings:**
- **Rounded corners**
- **Fill entire window**
- **Waveform goes out of bounds**
- **Border**
- **Pattern view spacing after:**
- **Note**
- **Instrument**
- **Volume**
- **Effect**
- **Effect value**
- **Color scheme**
- **Import**
- **Export**
- **Reset defaults**
- **General**
- **Color scheme type:**
- **Dark**
- **Light**
- **Frame shading**
- several more categories...
# Keyboard
## Color
### Color scheme
- **Import**
- **Export**
- **Reset defaults**
- several categories of keybinds...
- click on a keybind then enter a key or key combination to change it
- right-click to clear the keybind
- **General**
- **Color scheme type:**
- **Dark**
- **Light**
- **Frame shading**: applies a gradient effect to buttons and input boxes.
- several more categories...

View file

@ -5,11 +5,11 @@ The Namco 163 instrument editor consists of two tabs: "Namco 163" for control of
## Namco 163
- **Load waveform**: if enabled, a waveform will be loaded when this instrument plays.
- if it isn't then only the offset/length change.
- if it isn't then only the position/length change.
- **Waveform**: determines the waveform that will be loaded.
- only appears when Load waveform is enabled.
- **Per-channel wave offset/length**: when enabled, the offset/length settings are split per channel.
- **Offset**: determines the waveform position in RAM.
- **Per-channel wave position/length**: when enabled, the position/length settings are split per channel.
- **Position**: determines the waveform position in RAM.
- **Length**: determines the waveform length in RAM.
## Macros

View file

@ -2,19 +2,19 @@
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape.
Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "synth?" column.
Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "notes" column.
system | width | height | synth?
system | width | height | notes
--------------------|------:|:-------|:------
Bubble System | 32 | 16 |
Game Boy | 32 | 16 |
Game Boy | 32 | 16 | phase reset on waveform change (clicking)
SM8521 | 32 | 16 |
Namco WSG | 32 | 16 | RAM only
WonderSwan | 32 | 16 |
Namco 163 | ≤128 | 16 |
Namco 163 | ≤240 | 16 | limits differ depending on channel count
SNES | ≤256 | 16 |
PC Engine | 32 | 32 |
Virtual Boy | 32 | 64 | no
PC Engine | 32 | 32 | phase reset on waveform change (clicking)
Virtual Boy | 32 | 64 | wavesynth unsupported
Famicom Disk System | 64 | 64 |
Konami SCC | 32 | 256 |
Seta X1-010 | 128 | 256 |

View file

@ -32,7 +32,6 @@ the following sound chips have sample support:
- Ensoniq OTTO/ES5506
- Yamaha PCMD8/YMZ280B
- MMC5 (last channel only)
- SNES/S-DSP
## compatible sample mode

View file

@ -65,11 +65,11 @@ this is the full list of chips that Furnace supports.
- [2A03](nes.md)
- [Amiga](amiga.md)
- [AY-3-8910, AY-3-8914, YM2149(F), 5B](ay8910.md)
- [AY8930](ay8930.md)
- [MOS 6581, MOS 8580](c64.md)
- [ES5506](es5506.md)
- [SCC](scc.md)
- [AY-3-8910/8914/YM2149(F)/Sunsoft 5B](ay8910.md)
- [Microship AY8930](ay8930.md)
- [MOS 6581/8580 (SID)](c64.md)
- [Ensoniq ES5506](es5506.md)
- [Konami SCC](scc.md)
- [FDS](fds.md)
- [Game Boy](game-boy.md)
- [Generic PCM DAC](dac.md)
@ -82,8 +82,8 @@ this is the full list of chips that Furnace supports.
- [MSM5232](msm5232.md)
- [MSM6258](msm6258.md)
- [MSM6295](msm6295.md)
- [N163](n163.md)
- [Namco WSG, C15, C30](namco.md)
- [Namco 163](n163.md)
- [Namco WSG/C15/C30](namco.md)
- [HuC6280](pce.md)
- [PC Speaker](pcspkr.md)
- [PET](pet.md)
@ -91,12 +91,11 @@ this is the full list of chips that Furnace supports.
- [POKEY](pokey.md)
- [PV-1000](pv1000.md)
- [QSound](qsound.md)
- [RF5C68, RF5C164](ricoh.md)
- [RF5C68/RF5C164](ricoh.md)
- [SAA1099](saa1099.md)
- [SCC](scc.md)
- [SegaPCM](segapcm.md)
- [SM8521](sm8521.md)
- [SN76489 / Sega PSG](sms.md)
- [SN76489/Sega PSG](sms.md)
- [SNES](snes.md)
- [tildearrow Sound Unit](soundunit.md)
- [T6W28](t6w28.md)
@ -108,703 +107,16 @@ this is the full list of chips that Furnace supports.
- [VRC6](vrc6.md)
- [WonderSwan](wonderswan.md)
- [X1-010](x1-010.md)
- [(OPL) VRC7, Y8950, YM3526, YM3812, YMF262](opl.md)
- [(OPLL) YM2413](opll.md)
- [(OPZ) YM2414](opz.md)
- [(OPM) YM2151](ym2152.md)
- [(OPN) YM2203](ym2203.md)
- [(OPNA) YM2608](ym2608.md)
- [(OPNB) YM2610](ym2610.md)
- [(OPNB) YM2610B](ym2610b.md)
- [(OPN2) YM2612, YM3438](ym2612.md)
- [VRC7, Y8950, YM3526, YM3812 and YMF262 (OPL)](opl.md)
- [YM2413 (OPLL)](opll.md)
- [YM2414 (OPZ)](opz.md)
- [YM2151 (OPM)](ym2152.md)
- [YM2203 (OPN)](ym2203.md)
- [YM2608 (OPNA)](ym2608.md)
- [YM2610 (OPNB)](ym2610.md)
- [YM2610B (OPNB2)](ym2610b.md)
- [YM2612/YM3438 (OPN2)](ym2612.md)
- [YMZ280B](ymz280b.md)
- [ZXS Beeper](zxbeep.md)
- [ZX Spectrum Beeper](zxbeep.md)
Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but...
<!--
# full system list
## Game consoles
- **Sega Genesis**: [YM3438](ym2612.md), [SN76489](sms.md)
- **Sega Genesis (extended channel 3)**: [YM3438](ym2612.md), [SN76489](sms.md)
- **Sega Genesis (CSM)**: [YM3438](ym2612.md), [SN76489](sms.md)
- **Sega Genesis (DualPCM)**: [YM3438](ym2612.md), [SN76489](sms.md)
- **Sega Genesis (DualPCM, extended channel 3)**: [YM3438](ym2612.md), [SN76489](sms.md)
- **Sega Genesis (with Sega CD)**: [YM3438](ym2612.md), [SN76489](sms.md), [RF5C164](ricoh.md)
- **Sega Genesis (extended channel 3 with Sega CD)**: [YM3438](ym2612.md), [SN76489](sms.md), [RF5C164](ricoh.md)
- **Sega Genesis (CSM with Sega CD)**: [YM3438](ym2612.md), [SN76489](sms.md), [RF5C164](ricoh.md)
- **Sega Master System**: [SN76489](sms.md)
- **Sega Master System (with FM expansion)**: [SN76489](sms.md), [YM2413](opll.md)
- **Sega Master System (with FM expansion in drums mode)**: [SN76489](sms.md), [YM2413](opll.md)
- **Sega Game Gear**: [SN76489](sms.md)
- **Game Boy**: [Game Boy](game-boy.md)
- **Neo Geo Pocket**: [T6W28](t6w28.md), [DAC](dac.md)
- **NEC PC Engine/TurboGrafx-16**: [HuC6280](pce.md)
- **NES**: [2A03](nes.md)
- **Famicom with Konami VRC6**: [2A03](nes.md), [VRC6](vrc6.md)
- **Famicom with Konami VRC7**: [2A03](nes.md), [VRC7](opl.md)
- **Famicom with MMC5**: [2A03](nes.md), [MMC5](mmc5.md)
- **Famicom with Sunsoft 5B**: [2A03](nes.md), [5B](ay8910.md)
- **Famicom with Namco 163**: [2A03](nes.md), [N163](n163.md)
- **Famicom Disk System**: [2A03](nes.md), [FDS](fds.md)
- **SNES**: [SNES](snes.md)
- **Mattel Intellivision**: [AY-3-8914](ay8910.md)
- **Vectrex**: [AY-3-8910](ay8910.md)
- **Neo Geo AES**: [YM2610](ym2610.md)
- **Neo Geo AES (extended channel 2)**: [YM2610](ym2610.md)
- **Atari 2600/7800**: [TIA](tia.md)
- **Atari 7800 + Ballblazer/Commando**: [TIA](tia.md), [POKEY](pokey.md)
- **Atari Lynx**: [Lynx](lynx.md)
- **WonderSwan**: [WonderSwan](wonderswan.md)
- **Virtual Boy**: [Virtual Boy](virtual-boy.md)
- **Gamate**: [AY-3-8910](ay8910.md)
- **Pokémon Mini**: [Pokémon Mini](pokemini.md)
- **Tiger Game.com**: [SM8521](sm8521.md)
- **Casio PV-1000**: [PV-1000](pv1000.md)
## Computers
- **Commodore PET**: [PET](pet.md)
- **Commodore VIC-20**: [VIC](vic20.md)
- **Commodore Plus/4**: [TED](ted.md)
- **Commodore 64 (6581 SID)**: [MOS 6581](c64.md)
- **Commodore 64 (8580 SID)**: [MOS 8580](c64.md)
- **Commodore 64 (6581 SID + Sound Expander)**: [MOS 6581](c64.md), [YM3526](opl.md)
- **Commodore 64 (6581 SID + Sound Expander in drums mode)**: [MOS 6581](c64.md), [YM3526](opl.md)
- **Commodore 64 (8580 SID + Sound Expander)**: [MOS 8580](c64.md), [YM3526](opl.md)
- **Commodore 64 (8580 SID + Sound Expander in drums mode)**: [MOS 8580](c64.md), [YM3526](opl.md)
- **Commodore 64 (6581 SID + FM-YAM)**: [MOS 6581](c64.md), [YM3812](opl.md)
- **Commodore 64 (6581 SID + FM-YAM in drums mode)**: [MOS 6581](c64.md), [YM3812](opl.md)
- **Commodore 64 (8580 SID + FM-YAM)**: [MOS 8580](c64.md), [YM3812](opl.md)
- **Commodore 64 (8580 SID + FM-YAM in drums mode)**: [MOS 8580](c64.md), [YM3812](opl.md)
- **Amiga**: [Amiga](amiga.md)
- **MSX**: [AY-3-8910](ay8910.md)
- **MSX + SFG-01**: [YM2149(F)](ay8910.md), [YM2151](ym2152.md)
- **MSX + MSX-AUDIO**: [YM2149(F)](ay8910.md), [Y8950](opl.md)
- **MSX + MSX-AUDIO (drums mode)**: [YM2149(F)](ay8910.md), [Y8950](opl.md)
- **MSX + MSX-MUSIC**: [YM2149(F)](ay8910.md), [YM2413](opll.md)
- **MSX + MSX-MUSIC (drums mode)**: [YM2149(F)](ay8910.md), [YM2413](opll.md)
- **MSX + Darky**: [YM2149(F)](ay8910.md), [AY8930](ay8930.md) × 2
- **MSX + Playsoniq**: [YM2149(F)](ay8910.md), [SN76489](sms.md), [MOS 8580](c64.md), [SCC+](scc.md)
- **MSX + SCC**: [YM2149(F)](ay8910.md), [SCC](scc.md)
- **MSX + SCC+**: [YM2149(F)](ay8910.md), [SCC+](scc.md)
- **MSX + Neotron**: [YM2149(F)](ay8910.md), [YM2610](ym2610.md)
- **MSX + Neotron (extended channel 2)**: [YM2149(F)](ay8910.md), [YM2610](ym2610.md)
- **MSX + Neotron (with YM2610B)**: [YM2149(F)](ay8910.md), [YM2610B](ym2610b.md)
- **MSX + Neotron (with YM2610B; extended channel 3)**: [YM2149(F)](ay8910.md), [YM2610B](ym2610b.md)
- **MSX + SIMPL**: [YM2149(F)](ay8910.md), [DAC](dac.md)
- **NEC PC-88 (with PC-8801-10)**: [PC Speaker](pcspkr.md), [AY-3-8910](ay8910.md) × 2
- **NEC PC-88 (with PC-8801-11)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md)
- **NEC PC-88 (with PC-8801-11; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md)
- **NEC PC-88 (with PC-8801-23)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md)
- **NEC PC-88 (with PC-8801-23; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md)
- **NEC PC-88 (with HMB-20 HIBIKI-8800)**: [PC Speaker](pcspkr.md), [YM2151](ym2152.md)
- **NEC PC-8801mk2SR (with PC-8801-10)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [AY-3-8910](ay8910.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-10; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [AY-3-8910](ay8910.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-11)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on internal OPN)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on external OPN)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on both OPNs)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md) × 2
- **NEC PC-8801mk2SR (with PC-8801-23)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2608](ym2608.md)
- **NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on internal OPN)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2608](ym2608.md)
- **NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on external OPN)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2608](ym2608.md)
- **NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on both OPNs)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2608](ym2608.md)
- **NEC PC-8801mk2SR (with HMB-20 HIBIKI-8800)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2151](ym2152.md)
- **NEC PC-8801mk2SR (with HMB-20 HIBIKI-8800; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2203](ym2203.md), [YM2151](ym2152.md)
- **NEC PC-8801FA (with PC-8801-10)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [AY-3-8910](ay8910.md) × 2
- **NEC PC-8801FA (with PC-8801-10; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [AY-3-8910](ay8910.md) × 2
- **NEC PC-8801FA (with PC-8801-11)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2203](ym2203.md)
- **NEC PC-8801FA (with PC-8801-11; extended channel 3 on internal OPN)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2203](ym2203.md)
- **NEC PC-8801FA (with PC-8801-11; extended channel 3 on external OPN)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2203](ym2203.md)
- **NEC PC-8801FA (with PC-8801-11; extended channel 3 on both OPNs)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2203](ym2203.md)
- **NEC PC-8801FA (with PC-8801-23)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md) × 2
- **NEC PC-8801FA (with PC-8801-23; extended channel 3 on internal OPN)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md) × 2
- **NEC PC-8801FA (with PC-8801-23; extended channel 3 on external OPN)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md) × 2
- **NEC PC-8801FA (with PC-8801-23; extended channel 3 on both OPNs)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md) × 2
- **NEC PC-8801FA (with HMB-20 HIBIKI-8800)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2151](ym2152.md)
- **NEC PC-8801FA (with HMB-20 HIBIKI-8800; extended channel 3)**: [PC Speaker](pcspkr.md), [YM2608](ym2608.md), [YM2151](ym2152.md)
- **NEC PC-98 (with PC-9801-26/K)**: [YM2203](ym2203.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with PC-9801-26/K; extended channel 3)**: [YM2203](ym2203.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra)**: [YM2203](ym2203.md), [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra; extended channel 3)**: [YM2203](ym2203.md), [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra in drums mode)**: [YM2203](ym2203.md), [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra in drums mode; extended channel 3)**: [YM2203](ym2203.md), [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra V)**: [YM2203](ym2203.md), [Y8950](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra V; extended channel 3)**: [YM2203](ym2203.md), [Y8950](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra V in drums mode)**: [YM2203](ym2203.md), [Y8950](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Orchestra V in drums mode; extended channel 3)**: [YM2203](ym2203.md), [Y8950](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with PC-9801-86)**: [YM2608](ym2608.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with PC-9801-86; extended channel 3)**: [YM2608](ym2608.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with PC-9801-73)**: [YM2608](ym2608.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with PC-9801-73; extended channel 3)**: [YM2608](ym2608.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible)**: [YM2203](ym2203.md), [YMF262](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible; extended channel 3)**: [YM2203](ym2203.md), [YMF262](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode)**: [YM2203](ym2203.md), [YMF262](opl.md), [PC Speaker](pcspkr.md)
- **NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode; extended channel 3)**: [YM2203](ym2203.md), [YMF262](opl.md), [PC Speaker](pcspkr.md)
- **ZX Spectrum (48K, SFX-like engine)**: [ZXS Beeper](zxbeep.md)
- **ZX Spectrum (48K, QuadTone engine)**: [ZXS Beeper](zxbeep.md)
- **ZX Spectrum (128K)**: [AY-3-8910](ay8910.md)
- **ZX Spectrum (128K) with TurboSound FM**: [AY-3-8910](ay8910.md), [YM2203](ym2203.md) × 2
- **ZX Spectrum (128K) with TurboSound FM (extended channel 3 on first OPN)**: [AY-3-8910](ay8910.md), [YM2203](ym2203.md) × 2
- **ZX Spectrum (128K) with TurboSound FM (extended channel 3 on second OPN)**: [AY-3-8910](ay8910.md), [YM2203](ym2203.md) × 2
- **ZX Spectrum (128K) with TurboSound FM (extended channel 3 on both OPNs)**: [AY-3-8910](ay8910.md), [YM2203](ym2203.md) × 2
- **ZX Spectrum (128K) with TurboSound**: [AY-3-8910](ay8910.md) × 3
- **Amstrad CPC**: [AY-3-8910](ay8910.md)
- **Atari 800**: [POKEY](pokey.md)
- **Atari 800 (stereo)**: [POKEY](pokey.md) × 2
- **Atari ST**: [YM2149(F)](ay8910.md)
- **Atari STE**: [YM2149(F)](ay8910.md), [DAC](dac.md)
- **SAM Coupé**: [SAA1099](saa1099.md)
- **BBC Micro**: [SN76489A](sms.md)
- **PC (barebones)**: [PC Speaker](pcspkr.md)
- **IBM PCjr**: [SN76496](sms.md)
- **Tandy 1000**: [SN76496](sms.md), [PC Speaker](pcspkr.md)
- **PC + Covox Sound Master**: [AY8930](ay8930.md), [PC Speaker](pcspkr.md)
- **PC + SSI 2001**: [MOS 6581](c64.md), [PC Speaker](pcspkr.md)
- **PC + Game Blaster**: [SAA1099](saa1099.md) × 2, [PC Speaker](pcspkr.md)
- **PC + AdLib**: [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **PC + AdLib (drums mode)**: [YM3812](opl.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster**: [YM3812](opl.md), [PC Speaker](pcspkr.md), [DAC](dac.md)
- **PC + AdLib/Sound Blaster (drums mode)**: [YM3812](opl.md), [PC Speaker](pcspkr.md), [DAC](dac.md)
- **PC + Sound Blaster w/Game Blaster Compatible**: [YM3812](opl.md), [SAA1099](saa1099.md) × 2, [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster w/Game Blaster Compatible (drums mode)**: [YM3812](opl.md), [SAA1099](saa1099.md) × 2, [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster Pro**: [YM3812](opl.md) × 2, [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster Pro (drums mode)**: [YM3812](opl.md) × 2, [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster Pro 2**: [YMF262](opl.md), [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + Sound Blaster Pro 2 (drums mode)**: [YMF262](opl.md), [DAC](dac.md), [PC Speaker](pcspkr.md)
- **PC + PC-FXGA**: [HuC6280](pce.md), [PC Speaker](pcspkr.md)
- **PC + SAAYM**: [YM2151](ym2152.md), [SAA1099](saa1099.md) × 2, [PC Speaker](pcspkr.md)
- **Sharp X1**: [AY-3-8910](ay8910.md)
- **Sharp X1 + FM Addon**: [AY-3-8910](ay8910.md), [YM2151](ym2152.md)
- **Sharp X68000**: [YM2151](ym2152.md), [MSM6258](msm6258.md)
- **FM Towns**: [YM3438](ym2612.md), [RF5C68](ricoh.md)
- **FM Towns (extended channel 3)**: [YM3438](ym2612.md), [RF5C68](ricoh.md)
- **FM Towns (CSM)**: [YM3438](ym2612.md), [RF5C68](ricoh.md)
- **Commander X16 (VERA only)**: [VERA](vera.md)
- **Commander X16 (with OPM)**: [VERA](vera.md), [YM2151](ym2152.md)
- **Commander X16 (with Twin OPL3)**: [VERA](vera.md), [YMF262](opl.md) × 2
- **TI-99/4A**: [SN94624](sms.md)
## Arcade systems
- **Bally Midway MCR**: [AY-3-8910](ay8910.md) × 2
- **Williams/Midway Y/T unit w/ADPCM sound board**: [YM2151](ym2152.md), [MSM6295](msm6295.md)
- **Konami Gyruss**: [AY-3-8910](ay8910.md) × 5
- **Konami Bubble System**: [AY-3-8910](ay8910.md) × 2, [Konami WSG](bubblesystem.md)
- **Konami MX5000**: [YM2151](ym2152.md), [K007232](k007232.md)
- **Konami Battlantis**: [YM3812](opl.md) × 2
- **Konami Battlantis (drums mode on first OPL2)**: [YM3812](opl.md) × 2
- **Konami Battlantis (drums mode on second OPL2)**: [YM3812](opl.md) × 2
- **Konami Battlantis (drums mode on both OPL2s)**: [YM3812](opl.md) × 2
- **Konami Fast Lane**: [K007232](k007232.md) × 2
- **Konami Chequered Flag**: [YM2151](ym2152.md), [K007232](k007232.md) × 2
- **Konami Haunted Castle**: [YM3812](opl.md), [SCC](ess.md), [K007232](k007232.md)
- **Konami Haunted Castle (drums mode)**: [YM3812](opl.md), [SCC](ess.md), [K007232](k007232.md)
- **Konami Hot Chase**: [K007232](k007232.md) × 3
- **Konami S.P.Y.**: [YM3812](opl.md), [K007232](k007232.md) × 2
- **Konami S.P.Y. (drums mode)**: [YM3812](opl.md), [K007232](k007232.md) × 2
- **Konami Rollergames**: [YM3812](opl.md), [K053260](k056320.md)
- **Konami Rollergames (drums mode)**: [YM3812](opl.md), [K053260](k056320.md)
- **Konami Golfing Greats**: [K053260](k056320.md)
- **Konami Lightning Fighters**: [YM2151](ym2152.md), [K053260](k056320.md)
- **Konami Over Drive**: [YM2151](ym2152.md), [K053260](k056320.md) × 2
- **Konami Asterix**: [YM2151](ym2152.md), [K053260](k056320.md)
- **Konami Hexion**: [SCC](ess.md), [MSM6295](msm6295.md)
- **Sega Kyugo**: [AY-3-8910](ay8910.md) × 2
- **Sega System 1**: [SN76489A](sms.md) × 2
- **Sega System E**: [SN76489](sms.md) × 2
- **Sega System E (with FM expansion)**: [SN76489](sms.md) × 2, [YM2413](opll.md)
- **Sega System E (with FM expansion in drums mode)**: [SN76489](sms.md) × 2, [YM2413](opll.md)
- **Sega Hang-On**: [YM2203](ym2203.md), [SegaPCM](segapcm.md)
- **Sega Hang-On (extended channel 3)**: [YM2203](ym2203.md), [SegaPCM](segapcm.md)
- **Sega OutRun/X Board**: [YM2151](ym2152.md), [SegaPCM](segapcm.md)
- **Sega System 24**: [YM2151](ym2152.md), [DAC](dac.md)
- **Sega System 18**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 18 (extended channel 3 on first OPN2C)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 18 (extended channel 3 on second OPN2C)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 18 (extended channel 3 on both OPN2Cs)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 32**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 32 (extended channel 3 on first OPN2C)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 32 (extended channel 3 on second OPN2C)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Sega System 32 (extended channel 3 on both OPN2Cs)**: [YM3438](ym2612.md) × 2, [RF5C68](ricoh.md)
- **Capcom Exed Eyes**: [AY-3-8910](ay8910.md), [SN76489](sms.md) × 2
- **Capcom Arcade**: [YM2203](ym2203.md) × 2
- **Capcom Arcade (extended channel 3 on first OPN)**: [YM2203](ym2203.md) × 2
- **Capcom Arcade (extended channel 3 on second OPN)**: [YM2203](ym2203.md) × 2
- **Capcom Arcade (extended channel 3 on both OPNs)**: [YM2203](ym2203.md) × 2
- **Capcom CPS-1**: [YM2151](ym2152.md), [MSM6295](msm6295.md)
- **Capcom CPS-2 (QSound)**: [QSound](qsound.md)
- **Jaleco Ginga NinkyouDen**: [YM2149(F)](ay8910.md), [Y8950](opl.md)
- **Jaleco Ginga NinkyouDen (drums mode)**: [YM2149(F)](ay8910.md), [Y8950](opl.md)
- **Jaleco Mega System 1**: [YM2151](ym2152.md), [MSM6295](msm6295.md) × 2
- **NMK 16-bit Arcade**: [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **NMK 16-bit Arcade (extended channel 3)**: [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **Kaneko DJ Boy**: [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **Kaneko DJ Boy (extended channel 3)**: [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **Kaneko Air Buster**: [YM2203](ym2203.md), [MSM6295](msm6295.md)
- **Kaneko Air Buster (extended channel 3)**: [YM2203](ym2203.md), [MSM6295](msm6295.md)
- **Kaneko Toybox System**: [YM2149(F)](ay8910.md) × 2, [MSM6295](msm6295.md)
- **Kaneko Jackie Chan**: [YMZ280B](ymz280b.md)
- **Super Kaneko Nova System**: [YMZ280B](ymz280b.md)
- **Tecmo Ninja Gaiden**: [YM2203](ym2203.md) × 2, [MSM6295](msm6295.md)
- **Tecmo Ninja Gaiden (extended channel 3 on first OPN)**: [YM2203](ym2203.md) × 2, [MSM6295](msm6295.md)
- **Tecmo Ninja Gaiden (extended channel 3 on second OPN)**: [YM2203](ym2203.md) × 2, [MSM6295](msm6295.md)
- **Tecmo Ninja Gaiden (extended channel 3 on both OPNs)**: [YM2203](ym2203.md) × 2, [MSM6295](msm6295.md)
- **Tecmo System**: [YMF262](opl.md), [YMZ280B](ymz280b.md), [MSM6295](msm6295.md)
- **Tecmo System (drums mode)**: [YMF262](opl.md), [YMZ280B](ymz280b.md), [MSM6295](msm6295.md)
- **Seibu Kaihatsu Raiden**: [YM3812](opl.md), [MSM6295](msm6295.md)
- **Seibu Kaihatsu Raiden (drums mode)**: [YM3812](opl.md), [MSM6295](msm6295.md)
- **Sunsoft Shanghai 3**: [YM2149(F)](ay8910.md), [MSM6295](msm6295.md)
- **Sunsoft Arcade**: [YM3438](ym2612.md), [MSM6295](msm6295.md)
- **Sunsoft Arcade (extended channel 3)**: [YM3438](ym2612.md), [MSM6295](msm6295.md)
- **Atari Klax**: [MSM6295](msm6295.md)
- **Atari Rampart**: [YM2413](opll.md), [MSM6295](msm6295.md)
- **Atari Rampart (drums mode)**: [YM2413](opll.md), [MSM6295](msm6295.md)
- **Atari JSA IIIs**: [YM2151](ym2152.md), [MSM6295](msm6295.md) × 2
- **Atari Marble Madness**: [YM2151](ym2152.md), [POKEY](pokey.md)
- **Atari Championship Sprint**: [YM2151](ym2152.md), [POKEY](pokey.md) × 2
- **Atari Tetris**: [POKEY](pokey.md) × 2
- **Atari I, Robot**: [POKEY](pokey.md) × 4
- **Data East Karnov**: [YM2203](ym2203.md), [YM3526](opl.md)
- **Data East Karnov (extended channel 3)**: [YM2203](ym2203.md), [YM3526](opl.md)
- **Data East Karnov (drums mode)**: [YM2203](ym2203.md), [YM3526](opl.md)
- **Data East Karnov (extended channel 3; drums mode)**: [YM2203](ym2203.md), [YM3526](opl.md)
- **Data East Arcade**: [YM2203](ym2203.md), [YM3812](opl.md), [MSM6295](msm6295.md)
- **Data East Arcade (extended channel 3)**: [YM2203](ym2203.md), [YM3812](opl.md), [MSM6295](msm6295.md)
- **Data East Arcade (drums mode)**: [YM2203](ym2203.md), [YM3812](opl.md), [MSM6295](msm6295.md)
- **Data East Arcade (extended channel 3; drums mode)**: [YM2203](ym2203.md), [YM3812](opl.md), [MSM6295](msm6295.md)
- **Data East PCX**: [YM2203](ym2203.md), [HuC6280](pce.md)
- **Data East PCX (extended channel 3)**: [YM2203](ym2203.md), [HuC6280](pce.md)
- **Data East Dark Seal**: [YM2151](ym2152.md), [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **Data East Dark Seal (extended channel 3)**: [YM2151](ym2152.md), [YM2203](ym2203.md), [MSM6295](msm6295.md) × 2
- **Data East Deco 156**: [MSM6295](msm6295.md) × 2
- **Data East MLC**: [YMZ280B](ymz280b.md)
- **SNK Ikari Warriors**: [YM3526](opl.md) × 2
- **SNK Ikari Warriors (drums mode on first OPL)**: [YM3526](opl.md) × 2
- **SNK Ikari Warriors (drums mode on second OPL)**: [YM3526](opl.md) × 2
- **SNK Ikari Warriors (drums mode on both OPLs)**: [YM3526](opl.md) × 2
- **SNK Triple Z80**: [Y8950](opl.md), [YM3526](opl.md)
- **SNK Triple Z80 (drums mode on Y8950)**: [Y8950](opl.md), [YM3526](opl.md)
- **SNK Triple Z80 (drums mode on OPL)**: [Y8950](opl.md), [YM3526](opl.md)
- **SNK Triple Z80 (drums mode on Y8950 and OPL)**: [Y8950](opl.md), [YM3526](opl.md)
- **SNK Chopper I**: [Y8950](opl.md), [YM3812](opl.md)
- **SNK Chopper I (drums mode on Y8950)**: [Y8950](opl.md), [YM3812](opl.md)
- **SNK Chopper I (drums mode on OPL2)**: [Y8950](opl.md), [YM3812](opl.md)
- **SNK Chopper I (drums mode on Y8950 and OPL2)**: [Y8950](opl.md), [YM3812](opl.md)
- **SNK Touchdown Fever**: [YM3526](opl.md), [Y8950](opl.md)
- **SNK Touchdown Fever (drums mode on OPL)**: [YM3526](opl.md), [Y8950](opl.md)
- **SNK Touchdown Fever (drums mode on Y8950)**: [YM3526](opl.md), [Y8950](opl.md)
- **SNK Touchdown Fever (drums mode on OPL and Y8950)**: [YM3526](opl.md), [Y8950](opl.md)
- **Alpha denshi Alpha-68K**: [YM2203](ym2203.md), [YM2413](opll.md), [DAC](dac.md)
- **Alpha denshi Alpha-68K (extended channel 3)**: [YM2203](ym2203.md), [YM2413](opll.md), [DAC](dac.md)
- **Alpha denshi Alpha-68K (drums mode)**: [YM2203](ym2203.md), [YM2413](opll.md), [DAC](dac.md)
- **Alpha denshi Alpha-68K (extended channel 3; drums mode)**: [YM2203](ym2203.md), [YM2413](opll.md), [DAC](dac.md)
- **Alpha denshi Equites**: [MSM5232](msm5232.md), [AY-3-8910](ay8910.md), [DAC](dac.md)
- **Neo Geo MVS**: [YM2610](ym2610.md)
- **Neo Geo MVS (extended channel 2)**: [YM2610](ym2610.md)
- **Nichibutsu Mag Max**: [AY-3-8910](ay8910.md) × 3
- **Namco (3-channel WSG)**: [Namco](namco.md)
- **Namco Mappy**: [C15](namco.md)
- **Namco Pac-Land**: [C30](namco.md)
- **Namco System 86**: [YM2151](ym2152.md), [C30](namco.md)
- **Namco Thunder Ceptor**: [YM2151](ym2152.md), [C30](namco.md), [DAC](dac.md)
- **Namco System 1**: [YM2151](ym2152.md), [C30](namco.md), [DAC](dac.md)
- **Taito Arcade**: [YM2610B](ym2610b.md)
- **Taito Arcade (extended channel 3)**: [YM2610B](ym2610b.md)
- **Taito Metal Soldier Isaac II**: [MSM5232](msm5232.md), [AY-3-8910](ay8910.md) × 2
- **Taito The Fairyland Story**: [MSM5232](msm5232.md), [YM2149(F)](ay8910.md), [DAC](dac.md)
- **Taito Wyvern F-0**: [MSM5232](msm5232.md), [YM2149(F)](ay8910.md) × 2, [DAC](dac.md)
- **Seta 1**: [X1-010](x1-010.md)
- **Seta 1 + FM addon**: [X1-010](x1-010.md), [YM3438](ym2612.md)
- **Seta 1 + FM addon (extended channel 3)**: [X1-010](x1-010.md), [YM3438](ym2612.md)
- **Seta 2**: [X1-010](x1-010.md)
- **Sammy/Seta/Visco SSV**: [ES5506](es5506.md)
- **Cave 68000**: [YMZ280B](ymz280b.md)
- **Coreland Cyber Tank**: [Y8950](opl.md) × 2
- **Coreland Cyber Tank (drums mode)**: [Y8950](opl.md) × 2
- **ICE Skimaxx**: [MSM6295](msm6295.md) × 4
- **Toaplan 1**: [YM3812](opl.md)
- **Toaplan 1 (drums mode)**: [YM3812](opl.md)
- **Dynax/Nakanihon 3rd generation hardware**: [AY-3-8910](ay8910.md), [YM2413](opll.md), [MSM6295](msm6295.md)
- **Dynax/Nakanihon 3rd generation hardware (drums mode)**: [AY-3-8910](ay8910.md), [YM2413](opll.md), [MSM6295](msm6295.md)
- **Dynax/Nakanihon Real Break**: [YM2413](opll.md), [YMZ280B](ymz280b.md)
- **Dynax/Nakanihon Real Break (drums mode)**: [YM2413](opll.md), [YMZ280B](ymz280b.md)
- **Irem M72**: [YM2151](ym2152.md), [DAC](dac.md)
- **Irem M92/M107**: [YM2151](ym2152.md), GA20
## FM
- **Yamaha YM2151 (OPM)**: [YM2151](ym2152.md)
- **Yamaha YM2203 (OPN)**: [YM2203](ym2203.md)
- **Yamaha YM2203 (extended channel 3)**: [YM2203](ym2203.md)
- **Yamaha YM2608 (OPNA)**: [YM2608](ym2608.md)
- **Yamaha YM2608 (extended channel 3)**: [YM2608](ym2608.md)
- **Yamaha YM2610 (OPNB)**: [YM2610](ym2610.md)
- **Yamaha YM2610 (extended channel 2)**: [YM2610](ym2610.md)
- **Yamaha YM2610B (OPNB2)**: [YM2610B](ym2610b.md)
- **Yamaha YM2610B (extended channel 3)**: [YM2610B](ym2610b.md)
- **Yamaha YM2612 (OPN2)**: [YM2612](ym2612.md)
- **Yamaha YM2612 (extended channel 3)**: [YM2612](ym2612.md)
- **Yamaha YM2612 (OPN2) CSM**: [YM2612](ym2612.md)
- **Yamaha YM2612 (OPN2) with DualPCM**: [YM2612](ym2612.md)
- **Yamaha YM2612 (extended channel 3) with DualPCM**: [YM2612](ym2612.md)
- **Yamaha YM2413 (OPLL)**: [YM2413](opll.md)
- **Yamaha YM2413 (drums mode)**: [YM2413](opll.md)
- **Yamaha YM2414 (OPZ)**: [YM2414](opz.md)
- **Yamaha YM3438 (OPN2C)**: [YM3438](ym2612.md)
- **Yamaha YM3438 (extended channel 3)**: [YM3438](ym2612.md)
- **Yamaha YM3438 (OPN2C) CSM**: [YM3438](ym2612.md)
- **Yamaha YM3438 (OPN2C) with DualPCM**: [YM3438](ym2612.md)
- **Yamaha YM3438 (extended channel 3) with DualPCM**: [YM3438](ym2612.md)
- **Yamaha YM3526 (OPL)**: [YM3526](opl.md)
- **Yamaha YM3526 (drums mode)**: [YM3526](opl.md)
- **Yamaha Y8950**: [Y8950](opl.md)
- **Yamaha Y8950 (drums mode)**: [Y8950](opl.md)
- **Yamaha YM3812 (OPL2)**: [YM3812](opl.md)
- **Yamaha YM3812 (drums mode)**: [YM3812](opl.md)
- **Yamaha YMF262 (OPL3)**: [YMF262](opl.md)
- **Yamaha YMF262 (drums mode)**: [YMF262](opl.md)
- **Yamaha YMU759 (MA-2)**: [YMU759](ymu759.md)
## Square
- **TI SN76489**: [SN76489](sms.md)
- **TI SN76489A**: [SN76489A](sms.md)
- **TI SN76496**: [SN76496](sms.md)
- **NCR 8496**: [8496](sms.md)
- **Tandy PSSJ 3-voice sound**: [PSSJ](sms.md)
- **Sega PSG (SN76489-like)**: [SN76489](sms.md)
- **Sega PSG (SN76489-like, Stereo)**: [SN76489](sms.md)
- **TI SN94624**: [SN94624](sms.md)
- **TI SN76494**: [SN76494](sms.md)
- **Toshiba T6W28**: [T6W28](t6w28.md)
- **AY-3-8910**: [AY-3-8910](ay8910.md)
- **AY-3-8914**: [AY-3-8914](ay8910.md)
- **Yamaha YM2149(F)**: [YM2149(F)](ay8910.md)
- **Philips SAA1099**: [SAA1099](saa1099.md)
- **PC Speaker**: [PC Speaker](pcspkr.md)
- **Pokémon Mini**: [Pokémon Mini](pokemini.md)
- **Commodore VIC**: [VIC](vic20.md)
- **OKI MSM5232**: [MSM5232](msm5232.md)
- **Pong**: Pong
- **NEC D65010G031**: [PV-1000](pv1000.md)
- **MOS Technology TED**: [TED](ted.md)
## Sample
- **Amiga**: [Amiga](amiga.md)
- **SegaPCM**: [SegaPCM](segapcm.md)
- **Capcom QSound**: [QSound](qsound.md)
- **Seta/Allumer X1-010**: [X1-010](x1-010.md)
- **Yamaha YMZ280B (PCMD8)**: [YMZ280B](ymz280b.md)
- **Ricoh RF5C68**: [RF5C68](ricoh.md)
- **OKI MSM6258**: [MSM6258](msm6258.md)
- **OKI MSM6295**: [MSM6295](msm6295.md)
- **SNES**: [SNES](snes.md)
- **Konami K007232**: [K007232](k007232.md)
- **Irem GA20**: GA20
- **Generic PCM [DAC](dac.md)**: [DAC](dac.md)
- **Ensoniq ES5506 (OTTO)**: [ES5506](es5506.md)
- **Konami K053260**: [K053260](k056320.md)
## Wavetable
- **PC Engine**: [HuC6280](pce.md)
- **Konami Bubble System WSG**: [Konami WSG](bubblesystem.md)
- **Konami SCC**: [SCC](scc.md)
- **Konami SCC+**: [SCC+](scc.md)
- **Namco WSG**: [Namco WSG](namco.md)
- **Namco C15 (8-channel mono)**: [C15](namco.md)
- **Namco C30 (8-channel stereo)**: [C30](namco.md)
- **Namco 163**: [N163](n163.md)
- **Famicom Disk System (chip)**: [FDS](fds.md)
- **WonderSwan**: [WonderSwan](wonderswan.md)
- **Virtual Boy**: [Virtual Boy](virtual-boy.md)
- **Seta/Allumer X1-010**: [X1-010](x1-010.md)
## Specialized
- **MOS Technology SID (6581)**: [MOS 6581](c64.md)
- **MOS Technology SID (8580)**: [MOS 8580](c64.md)
- **Commodore PET (pseudo-wavetable)**: [PET](pet.md)
- **Konami VRC6**: [VRC6](vrc6.md)
- **MMC5**: [MMC5](mmc5.md)
- **Microchip AY8930**: [AY8930](ay8930.md)
- **Game Boy**: [Game Boy](game-boy.md)
- **Atari Lynx**: [Lynx](lynx.md)
- **POKEY**: [POKEY](pokey.md)
- **Atari TIA**: [TIA](tia.md)
- **NES (Ricoh 2A03)**: [2A03](nes.md)
- **Commander X16 (VERA only)**: [VERA](vera.md)
- **ZX Spectrum (beeper only, SFX-like engine)**: [ZXS Beeper](zxbeep.md)
- **ZX Spectrum (beeper only, QuadTone engine)**: [ZXS Beeper](zxbeep.md)
- **Sharp SM8521**: [SM8521](sm8521.md)
- **Dummy System**: Dummy
- **tildearrow Sound Unit**: [TSU](soundunit.md)
-->

8
doc/7-systems/ga20.md Normal file
View file

@ -0,0 +1,8 @@
# Irem GA20
ga20 is a 4 channel PCM sound source used by Irem in their arcades in late 1980s and eraly 1990s, often paired with [Yamaha YM2151](ym2151.md). Soundchip itself is rather unremarkable, having 8-bit volume and pitch control, no stereo panning
# effects
none

View file

@ -1,6 +1,6 @@
# Konami 056320
the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats.
the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio, 12-bit pitch resolution and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats.

View file

@ -3,4 +3,5 @@
here is a small collection of useful tricks and techniques to really make Furnace sing.
- [using samples with limited playback rates](limited-samples.md)
- [choosing emulation cores](emulation-cores.md)
- [choosing emulation cores](emulation-cores.md)
- [guide on using OPLL patch macro](opllswitching.md)

View file

@ -0,0 +1,19 @@
# using OPLL patch macro
YM2413's biggest flaw (or, rather, cost-saving feature) was that it could use only one user-defined instrument at once. It wasn't monotimbrial; you could use 15 built-in presets and 5 built-in drum tones freely, but for these going off the beaten path, it surely was limiting. However, there is one technique, as amazing as simple: **mid-note preset switching**.
the idea is to use the first patch to put the envelope in an unintended state for the second patch so that it sounds different, with a higher or lower modulation level. the sustain level defines at which "envelope level" the envelope will switch to the sustain state (or release depending on envelope type). if the first patch is used to put the envelope into sustain at a higher or lower envelope state than intended for the second patch, it'll still be in sustain/release but at a higher or lower level than it should be at that point.
therefore, much more variety can be forced out, without using custom instruments. As of July 2023, Furnace is the only tool supporting this feature. It is accessed in 'Macros' tab in OPLL instrument editor.
For example, try putting the first macro value as 14 (acoustic bass preset), followed by 4 (flute preset). This way you will get distortion guitar-like sound this is nothing like other 2413 preset! There are many combination to test out, which is highly recommended (I can only say, 12->1 or 12->4 produces sound similiar to the well-known 4-op FM mallet brass)
## drums using this technique
Using OPLL's drum mode, described is systems/opll.md, you gain access to 5 hardcoded drum tones at the expense of 3 melodic FM channels. Patch switching eliminates that, as using it, it's also possible to construct percussive sounds, some even fuller than their drum mode counterparts!
In short, noise portion of drums (as in hi-hats), can be created of the very high pitched pseudo-distortion guitar, described as above. For kicks, snares, toms and claps, more effort is needed, however using volume and arpeggio macros will help.
## examples
- [Lman-Clubster cover by Mahbod](https://www.youtube.com/watch?v=jfHs7tSyjXI)
- [OPLL Nation by Mahbod](https://www.youtube.com/watch?v=ou6pEfxByeE)

View file

@ -800,138 +800,138 @@ namespace IGFD
// will be called internally
// will not been exposed to IGFD API
bool IGFD::FilterManager::prFillFileStyle(std::shared_ptr<FileInfos> vFileInfos) const
bool IGFD::FilterManager::prFillFileStyle(FileInfos& vFileInfos) const
{
if (vFileInfos.use_count() && !prFilesStyle.empty())
if (!prFilesStyle.empty())
{
for (const auto& _flag : prFilesStyle)
{
for (const auto& _file : _flag.second)
{
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd')
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
{
if (_file.first.empty()) // for all dirs
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
else if (_file.first == vFileInfos->fileNameExt) // for dirs who are equal to style criteria
else if (_file.first == vFileInfos.fileNameExt) // for dirs who are equal to style criteria
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f')
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
{
if (_file.first.empty()) // for all files
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
else if (_file.first == vFileInfos->fileNameExt) // for files who are equal to style criteria
else if (_file.first == vFileInfos.fileNameExt) // for files who are equal to style criteria
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l')
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
{
if (_file.first.empty()) // for all links
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
else if (_file.first == vFileInfos->fileNameExt) // for links who are equal to style criteria
else if (_file.first == vFileInfos.fileNameExt) // for links who are equal to style criteria
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
if (_flag.first & IGFD_FileStyleByExtention)
{
if (_file.first == vFileInfos->fileExt)
if (_file.first == vFileInfos.fileExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
// can make sense for some dirs like the hidden by ex ".git"
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd')
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
{
if (_file.first == vFileInfos->fileExt)
if (_file.first == vFileInfos.fileExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f')
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
{
if (_file.first == vFileInfos->fileExt)
if (_file.first == vFileInfos.fileExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l')
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
{
if (_file.first == vFileInfos->fileExt)
if (_file.first == vFileInfos.fileExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
}
if (_flag.first & IGFD_FileStyleByFullName)
{
if (_file.first == vFileInfos->fileNameExt)
if (_file.first == vFileInfos.fileNameExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd')
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
{
if (_file.first == vFileInfos->fileNameExt)
if (_file.first == vFileInfos.fileNameExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f')
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
{
if (_file.first == vFileInfos->fileNameExt)
if (_file.first == vFileInfos.fileNameExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l')
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
{
if (_file.first == vFileInfos->fileNameExt)
if (_file.first == vFileInfos.fileNameExt)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
}
if (_flag.first & IGFD_FileStyleByContainedInFullName)
{
if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos)
if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType == 'd')
if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
{
if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos)
if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType == 'f')
else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
{
if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos)
if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType == 'l')
else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
{
if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos)
if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
{
vFileInfos->fileStyle = _file.second;
vFileInfos.fileStyle = _file.second;
}
}
}
if (vFileInfos->fileStyle.use_count())
if (vFileInfos.fileStyle.use_count())
return true;
}
}
@ -1142,6 +1142,7 @@ namespace IGFD
for (int i=1; i<4; i++) {
puSortingDirection[i]=false;
}
invalidFile.isValid=false;
}
void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal)
@ -1191,29 +1192,24 @@ namespace IGFD
puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
// this code fail in c:\\Users with the link "All users". got a invalid comparator
/*
// use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
// strict ordering for file/directory types beginning in '.'
// common on Linux platforms
if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.')
if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
return false;
if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.')
if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
return true;
if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.')
if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
{
return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case
return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
}
*/
if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first
return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) < 0); // sort in insensitive case
if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
});
}
else
@ -1222,28 +1218,23 @@ namespace IGFD
puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
// this code fail in c:\\Users with the link "All users". got a invalid comparator
/*
// use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
// strict ordering for file/directory types beginning in '.'
// common on Linux platforms
if (a->fileNameExt[0] == '.' && b->fileNameExt[0] != '.')
if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
return false;
if (a->fileNameExt[0] != '.' && b->fileNameExt[0] == '.')
if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
return true;
if (a->fileNameExt[0] == '.' && b->fileNameExt[0] == '.')
if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
{
return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case
return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
}
*/
return (stricmp(a->fileNameExt.c_str(), b->fileNameExt.c_str()) > 0); // sort in insensitive case
return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
});
}
}
@ -1259,13 +1250,10 @@ namespace IGFD
puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first
return (a->fileExt < b->fileExt); // else
if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
return (a.fileExt < b.fileExt); // else
});
}
else
@ -1274,15 +1262,10 @@ namespace IGFD
puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last
return (a->fileExt > b->fileExt); // else
if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
return (a.fileExt > b.fileExt); // else
});
}
}
@ -1298,15 +1281,10 @@ namespace IGFD
puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first
return (a->fileSize < b->fileSize); // else
if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
return (a.fileSize < b.fileSize); // else
});
}
else
@ -1315,15 +1293,10 @@ namespace IGFD
puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last
return (a->fileSize > b->fileSize); // else
if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
return (a.fileSize > b.fileSize); // else
});
}
}
@ -1339,15 +1312,10 @@ namespace IGFD
puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first
return (a->fileModifDate < b->fileModifDate); // else
if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
return (a.fileModifDate < b.fileModifDate); // else
});
}
else
@ -1356,15 +1324,10 @@ namespace IGFD
puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last
return (a->fileModifDate > b->fileModifDate); // else
if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
return (a.fileModifDate > b.fileModifDate); // else
});
}
}
@ -1384,15 +1347,12 @@ namespace IGFD
puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType == 'd'); // directory in first
if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth)
return (a->thumbnailInfo.textureHeight < b->thumbnailInfo.textureHeight);
return (a->thumbnailInfo.textureWidth < b->thumbnailInfo.textureWidth);
if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
return (a.thumbnailInfo.textureHeight < b.thumbnailInfo.textureHeight);
return (a.thumbnailInfo.textureWidth < b.thumbnailInfo.textureWidth);
});
}
@ -1402,15 +1362,12 @@ namespace IGFD
puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails;
#endif // USE_CUSTOM_SORTING_ICON
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
[](const FileInfos& a, const FileInfos& b) -> bool
{
if (!a.use_count() || !b.use_count())
return false;
if (a->fileType != b->fileType) return (a->fileType != 'd'); // directory in last
if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth)
return (a->thumbnailInfo.textureHeight > b->thumbnailInfo.textureHeight);
return (a->thumbnailInfo.textureWidth > b->thumbnailInfo.textureWidth);
if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
return (a.thumbnailInfo.textureHeight > b.thumbnailInfo.textureHeight);
return (a.thumbnailInfo.textureWidth > b.thumbnailInfo.textureWidth);
});
}
}
@ -1441,38 +1398,38 @@ namespace IGFD
return fileNameExt;
}
void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const char& vFileType, void* ent)
void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, char vFileType, void* ent)
{
auto infos = std::make_shared<FileInfos>();
FileInfos infos;
#ifdef _WIN32
struct dirent* dent=(struct dirent*)ent;
#endif
infos->filePath = vPath;
infos->fileNameExt = vFileName;
infos->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos->fileNameExt);
infos->fileType = vFileType;
infos.filePath = vPath;
infos.fileNameExt = vFileName;
infos.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos.fileNameExt);
infos.fileType = vFileType;
if (infos->fileNameExt.empty() || ((infos->fileNameExt == "." || infos->fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807
if (infos->fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos->fileNameExt[0] == '.') // dont show hidden files
if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos->fileNameExt != ".")) // except "." if in directory mode //-V728
if (infos.fileNameExt.empty() || ((infos.fileNameExt == "." || infos.fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807
if (infos.fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos.fileNameExt[0] == '.') // dont show hidden files
if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos.fileNameExt != ".")) // except "." if in directory mode //-V728
return;
if (infos->fileType == 'f' ||
infos->fileType == 'l') // link can have the same extention of a file
if (infos.fileType == 'f' ||
infos.fileType == 'l') // link can have the same extention of a file
{
size_t lpt = infos->fileNameExt.find_last_of('.');
size_t lpt = infos.fileNameExt.find_last_of('.');
if (lpt != std::string::npos)
{
infos->fileExt = infos->fileNameExt.substr(lpt);
infos.fileExt = infos.fileNameExt.substr(lpt);
}
for (char& i: infos->fileExt) {
for (char& i: infos.fileExt) {
if (i>='A' && i<='Z') i+='a'-'A';
}
if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos->fileExt))
if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos.fileExt))
{
return;
}
@ -1483,16 +1440,16 @@ namespace IGFD
SYSTEMTIME localTime;
char timebuf[100];
infos->fileSize=dent->dwin_size;
infos.fileSize=dent->dwin_size;
if (FileTimeToSystemTime(&dent->dwin_mtime,&systemTime)==TRUE) {
if (SystemTimeToTzSpecificLocalTime(NULL,&systemTime,&localTime)==TRUE) {
snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",localTime.wYear,localTime.wMonth,localTime.wDay,localTime.wHour,localTime.wMinute);
} else {
snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute);
}
infos->fileModifDate=timebuf;
infos.fileModifDate=timebuf;
} else {
infos->fileModifDate="???";
infos.fileModifDate="???";
}
#endif
@ -1534,7 +1491,7 @@ namespace IGFD
{
struct dirent* ent = files[i];
std::string where = path + std::string(PATH_SEP_STR) + std::string(ent->d_name);
char fileType = 0;
char fileType = 'f';
#if defined(HAVE_DIRENT_TYPE) || defined(_WIN32)
if (ent->d_type != DT_UNKNOWN)
{
@ -1544,7 +1501,7 @@ namespace IGFD
fileType = 'f'; break;
case DT_DIR:
fileType = 'd'; break;
case DT_LNK:
case DT_LNK: {
#ifdef _WIN32
fileType = 'f';
#else
@ -1557,7 +1514,8 @@ namespace IGFD
}
else
{
fileType = 'l';
// why does 'l' make it crash?
fileType = 'f';
}
}
else
@ -1568,6 +1526,9 @@ namespace IGFD
#endif
break;
}
default:
fileType = 'f'; break;
}
}
else
#endif // HAVE_DIRENT_TYPE
@ -1622,12 +1583,12 @@ namespace IGFD
ClearFileLists();
for (auto& drive : drives)
{
auto info = std::make_shared<FileInfos>();
info->fileNameExt = drive;
info->fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive);
info->fileType = 'd';
FileInfos info;
info.fileNameExt = drive;
info.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive);
info.fileType = 'd';
if (!info->fileNameExt.empty())
if (!info.fileNameExt.empty())
{
prFileList.push_back(info);
}
@ -1658,11 +1619,11 @@ namespace IGFD
return prFileList.size();
}
std::shared_ptr<FileInfos> IGFD::FileManager::GetFullFileAt(size_t vIdx)
const FileInfos& IGFD::FileManager::GetFullFileAt(size_t vIdx)
{
if (vIdx < prFileList.size())
return prFileList[vIdx];
return nullptr;
return invalidFile;
}
bool IGFD::FileManager::IsFilteredListEmpty()
@ -1675,11 +1636,11 @@ namespace IGFD
return prFilteredFileList.size();
}
std::shared_ptr<FileInfos> IGFD::FileManager::GetFilteredFileAt(size_t vIdx)
const FileInfos& IGFD::FileManager::GetFilteredFileAt(size_t vIdx)
{
if (vIdx < prFilteredFileList.size())
return prFilteredFileList[vIdx];
return nullptr;
return invalidFile;
}
bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName)
@ -1706,14 +1667,12 @@ namespace IGFD
void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal)
{
prFilteredFileList.clear();
for (const auto& file : prFileList)
for (const FileInfos& file : prFileList)
{
if (!file.use_count())
continue;
bool show = true;
if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag
if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) // if search tag
show = false;
if (puDLGDirectoryMode && file->fileType != 'd') // directory mode
if (puDLGDirectoryMode && file.fileType != 'd') // directory mode
show = false;
if (show)
prFilteredFileList.push_back(file);
@ -1750,13 +1709,10 @@ namespace IGFD
return "";
}
void IGFD::FileManager::prCompleteFileInfos(const std::shared_ptr<FileInfos>& vInfos)
void IGFD::FileManager::prCompleteFileInfos(FileInfos& vInfos)
{
if (!vInfos.use_count())
return;
if (vInfos->fileNameExt != "." &&
vInfos->fileNameExt != "..")
if (vInfos.fileNameExt != "." &&
vInfos.fileNameExt != "..")
{
// _stat struct :
//dev_t st_dev; /* ID of device containing file */
@ -1774,25 +1730,25 @@ namespace IGFD
//time_t st_ctime; /* time of last status change - not sure out of ntfs */
#ifdef _WIN32
if (vInfos->fileType != 'd')
if (vInfos.fileType != 'd')
{
vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize);
vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
}
#else
std::string fpn;
if (vInfos->fileType == 'f' || vInfos->fileType == 'l' || vInfos->fileType == 'd') // file
fpn = vInfos->filePath + std::string(1u, PATH_SEP) + vInfos->fileNameExt;
if (vInfos.fileType == 'f' || vInfos.fileType == 'l' || vInfos.fileType == 'd') // file
fpn = vInfos.filePath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;
struct stat statInfos = {};
char timebuf[100];
int result = stat(fpn.c_str(), &statInfos);
if (result!=-1)
{
if (vInfos->fileType != 'd')
if (vInfos.fileType != 'd')
{
vInfos->fileSize = (size_t)statInfos.st_size;
vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize);
vInfos.fileSize = (size_t)statInfos.st_size;
vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
}
size_t len = 0;
@ -1806,12 +1762,12 @@ namespace IGFD
#endif // MSVC
if (len)
{
vInfos->fileModifDate = std::string(timebuf, len);
vInfos.fileModifDate = std::string(timebuf, len);
}
} else {
vInfos->fileSize=0;
vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize);
vInfos->fileModifDate="???";
vInfos.fileSize=0;
vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
vInfos.fileModifDate="???";
}
#endif
}
@ -1994,14 +1950,11 @@ namespace IGFD
IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName);
}
bool IGFD::FileManager::SelectDirectory(const std::shared_ptr<FileInfos>& vInfos)
bool IGFD::FileManager::SelectDirectory(const FileInfos& vInfos)
{
if (!vInfos.use_count())
return false;
bool pathClick = false;
if (vInfos->fileNameExt == "..")
if (vInfos.fileNameExt == "..")
{
pathClick = SetPathOnParentDirectoryIfAny();
}
@ -2011,23 +1964,23 @@ namespace IGFD
if (puShowDrives)
{
newPath = vInfos->fileNameExt + std::string(1u, PATH_SEP);
newPath = vInfos.fileNameExt + std::string(1u, PATH_SEP);
}
else
{
#ifdef __linux__
if (puFsRoot == prCurrentPath)
newPath = prCurrentPath + vInfos->fileNameExt;
newPath = prCurrentPath + vInfos.fileNameExt;
else
#endif // __linux__
newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos->fileNameExt;
newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;
}
if (IGFD::Utils::IsDirectoryExist(newPath))
{
if (puShowDrives)
{
prCurrentPath = vInfos->fileNameExt;
prCurrentPath = vInfos.fileNameExt;
puFsRoot = prCurrentPath;
}
else
@ -2041,35 +1994,32 @@ namespace IGFD
return pathClick;
}
void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos)
void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const FileInfos& vInfos)
{
if (!vInfos.use_count())
return;
if (ImGui::GetIO().KeyCtrl)
{
if (puDLGcountSelectionMax == 0) // infinite selection
{
if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add
if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
{
prAddFileNameInSelection(vInfos->fileNameExt, true);
prAddFileNameInSelection(vInfos.fileNameExt, true);
}
else // found +> remove
{
prRemoveFileNameInSelection(vInfos->fileNameExt);
prRemoveFileNameInSelection(vInfos.fileNameExt);
}
}
else // selection limited by size
{
if (prSelectedFileNames.size() < puDLGcountSelectionMax)
{
if (prSelectedFileNames.find(vInfos->fileNameExt) == prSelectedFileNames.end()) // not found +> add
if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
{
prAddFileNameInSelection(vInfos->fileNameExt, true);
prAddFileNameInSelection(vInfos.fileNameExt, true);
}
else // found +> remove
{
prRemoveFileNameInSelection(vInfos->fileNameExt);
prRemoveFileNameInSelection(vInfos.fileNameExt);
}
}
}
@ -2081,18 +2031,15 @@ namespace IGFD
prSelectedFileNames.clear();
// we will iterate filelist and get the last selection after the start selection
bool startMultiSelection = false;
std::string fileNameToSelect = vInfos->fileNameExt;
std::string fileNameToSelect = vInfos.fileNameExt;
std::string savedLastSelectedFileName; // for invert selection mode
for (const auto& file : prFileList)
for (const FileInfos& file : prFileList)
{
if (!file.use_count())
continue;
bool canTake = true;
if (!file->IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false;
if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false;
if (canTake) // if not filtered, we will take files who are filtered by the dialog
{
if (file->fileNameExt == prLastSelectedFileName)
if (file.fileNameExt == prLastSelectedFileName)
{
startMultiSelection = true;
prAddFileNameInSelection(prLastSelectedFileName, false);
@ -2101,13 +2048,13 @@ namespace IGFD
{
if (puDLGcountSelectionMax == 0) // infinite selection
{
prAddFileNameInSelection(file->fileNameExt, false);
prAddFileNameInSelection(file.fileNameExt, false);
}
else // selection limited by size
{
if (prSelectedFileNames.size() < puDLGcountSelectionMax)
{
prAddFileNameInSelection(file->fileNameExt, false);
prAddFileNameInSelection(file.fileNameExt, false);
}
else
{
@ -2119,7 +2066,7 @@ namespace IGFD
}
}
if (file->fileNameExt == fileNameToSelect)
if (file.fileNameExt == fileNameToSelect)
{
if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse
{
@ -2145,7 +2092,7 @@ namespace IGFD
{
prSelectedFileNames.clear();
IGFD::Utils::ResetBuffer(puFileNameBuffer);
prAddFileNameInSelection(vInfos->fileNameExt, true);
prAddFileNameInSelection(vInfos.fileNameExt, true);
}
}
@ -3922,11 +3869,8 @@ namespace IGFD
}
// returns 0 if not break loop, 1 if break loop, 2 if exit dialog
int IGFD::FileDialog::prSelectableItem(int vidx, std::shared_ptr<FileInfos> vInfos, bool vSelected, const char* vFmt, ...)
int IGFD::FileDialog::prSelectableItem(int vidx, const FileInfos& vInfos, bool vSelected, const char* vFmt, ...)
{
if (!vInfos.use_count())
return 0;
auto& fdi = prFileDialogInternal.puFileManager;
static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick |
@ -3957,7 +3901,7 @@ namespace IGFD
#endif // USE_EXPLORATION_BY_KEYS
if (res)
{
if (vInfos->fileType == 'd')
if (vInfos.fileType == 'd')
{
bool isSelectingDir=false;
// nav system, selectebale cause open directory or select directory
@ -4012,27 +3956,27 @@ namespace IGFD
return 0;
}
void IGFD::FileDialog::prBeginFileColorIconStyle(std::shared_ptr<FileInfos> vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont)
void IGFD::FileDialog::prBeginFileColorIconStyle(const FileInfos& vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont)
{
vOutStr.clear();
vOutShowColor = false;
if (vFileInfos->fileStyle.use_count()) //-V807 //-V522
if (vFileInfos.fileStyle.use_count()) //-V807 //-V522
{
vOutShowColor = true;
*vOutFont = vFileInfos->fileStyle->font;
*vOutFont = vFileInfos.fileStyle->font;
}
if (vOutShowColor && !vFileInfos->fileStyle->icon.empty()) vOutStr = vFileInfos->fileStyle->icon;
else if (vFileInfos->fileType == 'd') vOutStr = dirEntryString;
else if (vFileInfos->fileType == 'l') vOutStr = linkEntryString;
else if (vFileInfos->fileType == 'f') vOutStr = fileEntryString;
if (vOutShowColor && !vFileInfos.fileStyle->icon.empty()) vOutStr = vFileInfos.fileStyle->icon;
else if (vFileInfos.fileType == 'd') vOutStr = dirEntryString;
else if (vFileInfos.fileType == 'l') vOutStr = linkEntryString;
else if (vFileInfos.fileType == 'f') vOutStr = fileEntryString;
vOutStr += " " + vFileInfos->fileNameExt;
vOutStr += " " + vFileInfos.fileNameExt;
if (vOutShowColor)
ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color);
ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos.fileStyle->color);
if (*vOutFont)
ImGui::PushFont(*vOutFont);
}
@ -4126,13 +4070,13 @@ namespace IGFD
{
if (i < 0) continue;
auto infos = fdi.GetFilteredFileAt((size_t)i);
if (!infos.use_count())
const FileInfos& infos = fdi.GetFilteredFileAt((size_t)i);
if (!infos.isValid)
continue;
prBeginFileColorIconStyle(infos, _showColor, _str, &_font);
bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found
bool selected = fdi.IsFileNameSelected(infos.fileNameExt); // found
ImGui::TableNextRow();
@ -4147,13 +4091,13 @@ namespace IGFD
}
if (ImGui::TableNextColumn()) // file type
{
ImGui::Text("%s", infos->fileExt.c_str());
ImGui::Text("%s", infos.fileExt.c_str());
}
if (ImGui::TableNextColumn()) // file size
{
if (infos->fileType != 'd')
if (infos.fileType != 'd')
{
ImGui::Text("%s ", infos->formatedFileSize.c_str());
ImGui::Text("%s ", infos.formatedFileSize.c_str());
}
else
{
@ -4162,7 +4106,7 @@ namespace IGFD
}
if (ImGui::TableNextColumn()) // file date + time
{
ImGui::Text("%s", infos->fileModifDate.c_str());
ImGui::Text("%s", infos.fileModifDate.c_str());
}
prEndFileColorIconStyle(_showColor, _font);

View file

@ -766,7 +766,7 @@ namespace IGFD
void ParseFilters(const char* vFilters); // Parse filter syntax, detect and parse filter collection
void SetSelectedFilterWithExt(const std::string& vFilter); // Select filter
bool prFillFileStyle(std::shared_ptr<FileInfos> vFileInfos) const; // fill with the good style
bool prFillFileStyle(FileInfos& vFileInfos) const; // fill with the good style
void SetFileStyle(
const IGFD_FileStyleFlags& vFlags,
@ -812,6 +812,7 @@ namespace IGFD
#ifdef USE_THUMBNAILS
IGFD_Thumbnail_Info thumbnailInfo; // structre for the display for image file tetxure
#endif // USE_THUMBNAILS
bool isValid = true;
public:
bool IsTagFound(const std::string& vTag) const;
@ -824,6 +825,7 @@ namespace IGFD
class FileManager
{
public: // types
FileInfos invalidFile;
enum class SortingFieldEnum // sorting for filetering of the file lsit
{
FIELD_NONE = 0, // no sorting preference, result indetermined haha..
@ -839,8 +841,8 @@ namespace IGFD
private:
std::string prCurrentPath; // current path (to be decomposed in prCurrentPathDecomposition
std::vector<std::string> prCurrentPathDecomposition; // part words
std::vector<std::shared_ptr<FileInfos>> prFileList; // base container
std::vector<std::shared_ptr<FileInfos>> prFilteredFileList; // filtered container (search, sorting, etc..)
std::vector<FileInfos> prFileList; // base container
std::vector<FileInfos> prFilteredFileList; // filtered container (search, sorting, etc..)
std::string prLastSelectedFileName; // for shift multi selection
std::set<std::string> prSelectedFileNames; // the user selection of FilePathNames
bool prCreateDirectoryMode = false; // for create directory widget
@ -879,11 +881,11 @@ namespace IGFD
static std::string prRoundNumber(double vvalue, int n); // custom rounding number
static std::string prFormatFileSize(size_t vByteSize); // format file size field
static std::string prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt); // turn all text in lower case for search facilitie
static void prCompleteFileInfos(const std::shared_ptr<FileInfos>& FileInfos); // set time and date infos of a file (detail view mode)
static void prCompleteFileInfos(FileInfos& FileInfos); // set time and date infos of a file (detail view mode)
void prRemoveFileNameInSelection(const std::string& vFileName); // selection : remove a file name
void prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName); // selection : add a file name
void AddFile(const FileDialogInternal& vFileDialogInternal,
const std::string& vPath, const std::string& vFileName, const char& vFileType, void* ent); // add file called by scandir
const std::string& vPath, const std::string& vFileName, char vFileType, void* ent); // add file called by scandir
public:
FileManager();
@ -892,9 +894,9 @@ namespace IGFD
bool IsFileListEmpty();
bool IsFilteredListEmpty();
size_t GetFullFileListSize();
std::shared_ptr<FileInfos> GetFullFileAt(size_t vIdx);
const FileInfos& GetFullFileAt(size_t vIdx);
size_t GetFilteredListSize();
std::shared_ptr<FileInfos> GetFilteredFileAt(size_t vIdx);
const FileInfos& GetFilteredFileAt(size_t vIdx);
bool IsFileNameSelected(const std::string& vFileName);
std::string GetBack();
void ClearComposer();
@ -912,9 +914,9 @@ namespace IGFD
void SetCurrentPath(const std::string& vCurrentPath); // set the current path
static bool IsFileExist(const std::string& vFile);
void SetDefaultFileName(const std::string& vFileName);
bool SelectDirectory(const std::shared_ptr<FileInfos>& vInfos); // enter directory
bool SelectDirectory(const FileInfos& vInfos); // enter directory
void SelectFileName(const FileDialogInternal& vFileDialogInternal,
const std::shared_ptr<FileInfos>& vInfos); // select filename
const FileInfos& vInfos); // select filename
//depend of dirent.h
void SetCurrentDir(const std::string& vPath); // define current directory for scan
@ -1311,7 +1313,7 @@ namespace IGFD
// widgets components
virtual void prDrawSidePane(float vHeight); // draw side pane
virtual int prSelectableItem(int vidx,
std::shared_ptr<FileInfos> vInfos,
const FileInfos& vInfos,
bool vSelected, const char* vFmt, ...); // draw a custom selectable behavior item
virtual bool prDrawFileListView(ImVec2 vSize); // draw file list view (default mode)
@ -1325,7 +1327,7 @@ namespace IGFD
// - prDrawThumbnailsListView
// - prDrawThumbnailsGridView
void prBeginFileColorIconStyle(
std::shared_ptr<FileInfos> vFileInfos,
const FileInfos& vFileInfos,
bool& vOutShowColor,
std::string& vOutStr,
ImFont** vOutFont); // begin style apply of filter with color an icon if any

View file

@ -226,7 +226,7 @@ void k053260_core::voice_t::write(u8 address, u8 data)
m_start = (m_start & ~0x00ff00) | (u32(data) << 8);
break;
case 6: // start address bit 16-20
m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 16, 5)) << 16);
m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 0, 5)) << 16);
break;
case 7: // volume
m_volume = bitfield(data, 0, 7);

View file

@ -3851,6 +3851,23 @@ void DivEngine::delSample(int index) {
song.sampleLen=song.sample.size();
removeAsset(song.sampleDir,index);
checkAssetDir(song.sampleDir,song.sample.size());
// compensate
for (DivInstrument* i: song.ins) {
if (i->amiga.initSample==index) {
i->amiga.initSample=-1;
} else if (i->amiga.initSample>index) {
i->amiga.initSample--;
}
for (int j=0; j<120; j++) {
if (i->amiga.noteMap[j].map==index) {
i->amiga.noteMap[j].map=-1;
} else if (i->amiga.noteMap[j].map>index) {
i->amiga.noteMap[j].map--;
}
}
}
renderSamples();
}
saveLock.unlock();
@ -4042,6 +4059,27 @@ void DivEngine::exchangeIns(int one, int two) {
}
}
void DivEngine::exchangeWave(int one, int two) {
// TODO
}
void DivEngine::exchangeSample(int one, int two) {
for (DivInstrument* i: song.ins) {
if (i->amiga.initSample==one) {
i->amiga.initSample=two;
} else if (i->amiga.initSample==two) {
i->amiga.initSample=one;
}
for (int j=0; j<120; j++) {
if (i->amiga.noteMap[j].map==one) {
i->amiga.noteMap[j].map=two;
} else if (i->amiga.noteMap[j].map==two) {
i->amiga.noteMap[j].map=one;
}
}
}
}
bool DivEngine::moveInsUp(int which) {
if (which<1 || which>=(int)song.ins.size()) return false;
BUSY_BEGIN;
@ -4064,6 +4102,7 @@ bool DivEngine::moveWaveUp(int which) {
song.wave[which]=song.wave[which-1];
song.wave[which-1]=prev;
moveAsset(song.waveDir,which,which-1);
exchangeWave(which,which-1);
saveLock.unlock();
BUSY_END;
return true;
@ -4080,6 +4119,7 @@ bool DivEngine::moveSampleUp(int which) {
song.sample[which]=song.sample[which-1];
song.sample[which-1]=prev;
moveAsset(song.sampleDir,which,which-1);
exchangeSample(which,which-1);
saveLock.unlock();
renderSamples();
BUSY_END;
@ -4107,6 +4147,7 @@ bool DivEngine::moveWaveDown(int which) {
saveLock.lock();
song.wave[which]=song.wave[which+1];
song.wave[which+1]=prev;
exchangeWave(which,which+1);
moveAsset(song.waveDir,which,which+1);
saveLock.unlock();
BUSY_END;
@ -4123,6 +4164,7 @@ bool DivEngine::moveSampleDown(int which) {
saveLock.lock();
song.sample[which]=song.sample[which+1];
song.sample[which+1]=prev;
exchangeSample(which,which+1);
moveAsset(song.sampleDir,which,which+1);
saveLock.unlock();
renderSamples();

View file

@ -531,6 +531,9 @@ class DivEngine {
void initSongWithDesc(const char* description, bool inBase64=true, bool oldVol=false);
void exchangeIns(int one, int two);
void exchangeWave(int one, int two);
void exchangeSample(int one, int two);
void swapChannels(int src, int dest);
void stompChannel(int ch);

View file

@ -142,76 +142,78 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
// compatibility flags
ds.limitSlides=true;
ds.linearPitch=1;
ds.loopModality=0;
ds.properNoiseLayout=false;
ds.waveDutyIsVol=false;
// TODO: WHAT?! geodude.dmf fails when this is true
// but isn't that how Defle behaves???
ds.resetMacroOnPorta=false;
ds.legacyVolumeSlides=true;
ds.compatibleArpeggio=true;
ds.noteOffResetsSlides=true;
ds.targetResetsSlides=true;
ds.arpNonPorta=false;
ds.algMacroBehavior=false;
ds.brokenShortcutSlides=false;
ds.ignoreDuplicateSlides=true;
ds.brokenDACMode=true;
ds.oneTickCut=false;
ds.newInsTriggersInPorta=true;
ds.arp0Reset=true;
ds.brokenSpeedSel=true;
ds.noSlidesOnFirstTick=false;
ds.rowResetsArpPos=false;
ds.ignoreJumpAtEnd=true;
ds.buggyPortaAfterSlide=true;
ds.gbInsAffectsEnvelope=true;
ds.ignoreDACModeOutsideIntendedChannel=false;
ds.e1e2AlsoTakePriority=true;
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true;
ds.brokenOutVol2=true;
ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false;
ds.snNoLowPeriods=true;
ds.disableSampleMacro=true;
ds.delayBehavior=0;
ds.jumpTreatment=2;
if (!getConfInt("noDMFCompat",0)) {
ds.limitSlides=true;
ds.linearPitch=1;
ds.loopModality=0;
ds.properNoiseLayout=false;
ds.waveDutyIsVol=false;
// TODO: WHAT?! geodude.dmf fails when this is true
// but isn't that how Defle behaves???
ds.resetMacroOnPorta=false;
ds.legacyVolumeSlides=true;
ds.compatibleArpeggio=true;
ds.noteOffResetsSlides=true;
ds.targetResetsSlides=true;
ds.arpNonPorta=false;
ds.algMacroBehavior=false;
ds.brokenShortcutSlides=false;
ds.ignoreDuplicateSlides=true;
ds.brokenDACMode=true;
ds.oneTickCut=false;
ds.newInsTriggersInPorta=true;
ds.arp0Reset=true;
ds.brokenSpeedSel=true;
ds.noSlidesOnFirstTick=false;
ds.rowResetsArpPos=false;
ds.ignoreJumpAtEnd=true;
ds.buggyPortaAfterSlide=true;
ds.gbInsAffectsEnvelope=true;
ds.ignoreDACModeOutsideIntendedChannel=false;
ds.e1e2AlsoTakePriority=true;
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true;
ds.brokenOutVol2=true;
ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false;
ds.snNoLowPeriods=true;
ds.disableSampleMacro=true;
ds.delayBehavior=0;
ds.jumpTreatment=2;
// 1.1 compat flags
if (ds.version>24) {
ds.waveDutyIsVol=true;
ds.legacyVolumeSlides=false;
}
// 1.1 compat flags
if (ds.version>24) {
ds.waveDutyIsVol=true;
ds.legacyVolumeSlides=false;
}
// Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
/*
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
ds.tuning=443.23;
}
*/
// Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
/*
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
ds.tuning=443.23;
}
*/
// Genesis detuned on Defle v10 and earlier
/*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) {
ds.tuning=443.23;
}*/
// C64 detuned on Defle v11 and earlier
/*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) {
ds.tuning=433.2;
}*/
// Genesis detuned on Defle v10 and earlier
/*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) {
ds.tuning=443.23;
}*/
// C64 detuned on Defle v11 and earlier
/*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) {
ds.tuning=433.2;
}*/
// Game Boy arp+soundLen screwery
if (ds.system[0]==DIV_SYSTEM_GB) {
ds.systemFlags[0].set("enoughAlready",true);
// Game Boy arp+soundLen screwery
if (ds.system[0]==DIV_SYSTEM_GB) {
ds.systemFlags[0].set("enoughAlready",true);
}
}
logI("reading module data...");
@ -869,7 +871,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (ds.version>0x15) {
sample->depth=(DivSampleDepth)reader.readC();
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
logW("%d: sample depth is wrong! (%d)",i,sample->depth);
logW("%d: sample depth is wrong! (%d)",i,(int)sample->depth);
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
}
} else {
@ -1937,8 +1939,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
ds.system[i]=systemFromFileFur(sysID);
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
if (sysID!=0 && systemToFileFur(ds.system[i])==0) {
logE("unrecognized system ID %.2x",ds.system[i]);
lastError=fmt::sprintf("unrecognized system ID %.2x!",ds.system[i]);
logE("unrecognized system ID %.2x",sysID);
lastError=fmt::sprintf("unrecognized system ID %.2x!",sysID);
delete[] file;
return false;
}

View file

@ -360,26 +360,6 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
}
if (chan[i].pcm.isNoteMap) {
// note map macros
if (chan[i].std.wave.had) {
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<120) {
if (chan[i].pcm.next!=chan[i].std.wave.val) {
chan[i].pcm.next=chan[i].std.wave.val;
chan[i].pcmChanged.index=1;
}
}
}
} else if (!chan[i].pcm.isNoteMap) {
if (chan[i].std.wave.had) {
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<parent->song.sampleLen) {
if (chan[i].pcm.next!=chan[i].std.wave.val) {
chan[i].pcm.next=chan[i].std.wave.val;
chan[i].pcmChanged.index=1;
}
}
}
}
// update registers
if (chan[i].volChanged.changed) {
// calculate volume (16 bit)
@ -432,7 +412,7 @@ void DivPlatformES5506::tick(bool sysTick) {
off=(double)center/8363.0;
}
if (ins->amiga.useNoteMap) {
chan[i].pcm.note=next;
//chan[i].pcm.note=next;
}
// get loop mode
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
@ -748,13 +728,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
int sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
if (sample>=0 && sample<parent->song.sampleLen) {
sampleValid=true;
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
chan[c.chan].pcm.note=c.value;
chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample;
c.value=ins->amiga.getFreq(c.value);
chan[c.chan].pcm.note=c.value;
chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope;
}
@ -870,20 +850,6 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
// sample commands
case DIV_CMD_WAVE:
if (!chan[c.chan].useWave) {
if (chan[c.chan].active) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap) && (c.value>=0 && c.value<parent->song.sampleLen))) {
chan[c.chan].pcm.next=c.value;
chan[c.chan].pcmChanged.index=1;
}
}
}
// reserved for useWave
break;
// Filter commands
case DIV_CMD_ES5506_FILTER_MODE:
if (!chan[c.chan].active) {

View file

@ -105,15 +105,17 @@ class DivPlatformFMBase: public DivDispatch {
}
}
}
// only used by OPN2 for DAC writes
inline void urgentWrite(unsigned short a, unsigned char v) {
if (!skipRegisterWrites && !flushFirst) {
if (writes.empty()) {
writes.push_back(QueuedWrite(a,v));
} else if (writes.size()>16 || writes.front().addrOrVal) {
writes.push_back(QueuedWrite(a,v));
} else {
writes.push_front(QueuedWrite(a,v));
if (!writes.empty()) {
// check for hard reset
if (writes.front().addr==0xf0) {
// replace hard reset with DAC write
writes.pop_front();
}
}
writes.push_front(QueuedWrite(a,v));
if (dumpWrites) {
addWrite(a,v);
}

View file

@ -81,7 +81,6 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
}
void DivPlatformGB::updateWave() {
logV("WAVE UPDATE");
rWrite(0x1a,0);
for (int i=0; i<16; i++) {
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];

View file

@ -96,32 +96,22 @@ void DivPlatformGenesis::processDAC(int iRate) {
//sample>>=1;
if (sample<-128) sample=-128;
if (sample>127) sample=127;
urgentWrite(0x2a,(unsigned char)sample+0x80);
dacWrite=(unsigned char)(sample+0x80);
}
} else {
if (!chan[5].dacReady) {
chan[5].dacDelay+=32000;
if (chan[5].dacDelay>=iRate) {
chan[5].dacDelay-=iRate;
chan[5].dacReady=true;
}
}
if (chan[5].dacMode && chan[5].dacSample!=-1) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=iRate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0 && chan[5].dacPos<s->samples) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
dacWrite=(unsigned char)(sample+0x80);
}
chan[5].dacPos++;
if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->loopEnd)) {
@ -151,24 +141,34 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
os[0]=0; os[1]=0;
for (int i=0; i<6; i++) {
if (!writes.empty()) {
if (--delay<0) {
delay=0;
QueuedWrite& w=writes.front();
if (w.addrOrVal) {
//logV("%.3x = %.2x",w.addr,w.val);
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
lastBusy=0;
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
} else {
lastBusy++;
if (fm.write_busy==0) {
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
w.addrOrVal=true;
QueuedWrite& w=writes.front();
if (w.addrOrVal) {
//logV("%.3x = %.2x",w.addr,w.val);
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
if (dacWrite>=0) {
if (!canWriteDAC) {
canWriteDAC=true;
} else {
urgentWrite(0x2a,dacWrite);
dacWrite=-1;
canWriteDAC=writes.empty();
}
}
} else {
if (fm.write_busy==0) {
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
w.addrOrVal=true;
}
}
} else {
canWriteDAC=true;
if (dacWrite>=0) {
urgentWrite(0x2a,dacWrite);
dacWrite=-1;
}
flushFirst=false;
}
@ -227,8 +227,22 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
lastBusy=1;
if (dacWrite>=0) {
if (!canWriteDAC) {
canWriteDAC=true;
} else {
urgentWrite(0x2a,dacWrite);
dacWrite=-1;
canWriteDAC=writes.empty();
}
}
} else {
canWriteDAC=true;
if (dacWrite>=0) {
urgentWrite(0x2a,dacWrite);
dacWrite=-1;
}
flushFirst=false;
}
@ -288,6 +302,11 @@ void DivPlatformGenesis::fillStream(std::vector<DivDelayedWrite>& stream, int sR
for (size_t i=0; i<len; i++) {
processDAC(sRate);
if (dacWrite>=0) {
urgentWrite(0x2a,dacWrite);
dacWrite=-1;
}
while (!writes.empty()) {
QueuedWrite& w=writes.front();
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
@ -1314,11 +1333,12 @@ void DivPlatformGenesis::reset() {
pendingWrites[i]=-1;
}
lastBusy=60;
lfoValue=8;
softPCMTimer=0;
extMode=false;
flushFirst=false;
dacWrite=-1;
canWriteDAC=true;
if (softPCM) {
chan[5].dacMode=true;
@ -1330,8 +1350,6 @@ void DivPlatformGenesis::reset() {
// LFO
immWrite(0x22,lfoValue);
delay=0;
}
int DivPlatformGenesis::getOutputCount() {

View file

@ -55,7 +55,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
bool setPos;
unsigned char sampleBank;
@ -69,7 +68,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
dacPos(0),
dacSample(-1),
dacDelay(0),
dacReady(true),
dacDirection(false),
setPos(false),
sampleBank(0),
@ -86,8 +84,9 @@ class DivPlatformGenesis: public DivPlatformOPN {
int softPCMTimer;
bool extMode, softPCM, noExtMacros, useYMFM;
bool extMode, softPCM, noExtMacros, useYMFM, canWriteDAC;
unsigned char chipType;
short dacWrite;
unsigned char dacVolTable[128];

View file

@ -19,6 +19,7 @@
#include "genesisext.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define CHIP_FREQBASE fmFreqBase
@ -476,13 +477,6 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
/*printf(
"Mask: %c %c %c %c\n",
(writeMask&0x10)?'1':'-',
(writeMask&0x20)?'2':'-',
(writeMask&0x40)?'3':'-',
(writeMask&0x80)?'4':'-'
);*/
immWrite(0x28,writeMask);
}
}
@ -518,6 +512,39 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
opChan[i].freqChanged=true;
}
// channel macros
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
}
}
}
if (i==0 || fbAllOps) {
if (opChan[i].std.fb.had) {
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
}
}
if (opChan[i].std.fms.had) {
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ams.had) {
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ex3.had) {
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
// param macros
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
@ -576,6 +603,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
bool writeNoteOn=false;
unsigned char writeMask=2;
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -603,8 +631,13 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
writeNoteOn=true;
if (opChan[i].mask) {
writeMask|=1<<(4+i);
if (opChan[i].hardReset) {
hardResetMask|=1<<(4+i);
}
}
if (!opChan[i].hardReset) {
opChan[i].keyOn=false;
}
opChan[i].keyOn=false;
}
}
@ -631,14 +664,9 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
/*printf(
"Mask: %c %c %c %c\n",
(writeMask&0x10)?'1':'-',
(writeMask&0x20)?'2':'-',
(writeMask&0x40)?'3':'-',
(writeMask&0x80)?'4':'-'
);*/
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
// hard reset handling
if (mustHardReset) {
@ -651,6 +679,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
opChan[i].keyOn=false;
}
}
immWrite(0x28,writeMask);

View file

@ -145,15 +145,15 @@ void DivPlatformK053260::tick(bool sysTick) {
off=8363.0/s->centerRate;
}
}
DivSample* s=parent->getSample(chan[i].sample);
DivSample* s=parent->getSample(sample);
chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int start=0;
unsigned int length=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
start=sampleOffK053260[chan[i].sample];
if (sample>=0 && sample<parent->song.sampleLen) {
start=sampleOffK053260[sample];
length=s->length8;
if (chan[i].reverse) {
start+=length;
@ -163,8 +163,7 @@ void DivPlatformK053260::tick(bool sysTick) {
if (chan[i].audPos>0) {
if (chan[i].reverse) {
start=start-MIN(chan[i].audPos,s->length8);
}
else {
} else {
start=start+MIN(chan[i].audPos,s->length8);
}
length=MAX(1,length-chan[i].audPos);

View file

@ -386,6 +386,9 @@ int DivPlatformMSM6258::init(DivEngine* p, int channels, int sugRate, const DivC
oscBuf[i]=new DivDispatchOscBuffer;
}
msm=new okim6258_device(4000000);
msm->set_start_div(okim6258_device::FOSC_DIV_BY_1024);
msm->set_type(okim6258_device::TYPE_4BITS);
msm->set_outbits(okim6258_device::OUTPUT_12BITS);
msm->device_start();
setFlags(flags);
reset();

View file

@ -47,7 +47,6 @@ class DivPlatformMSM6258: public DivDispatch {
};
FixedQueue<QueuedWrite,256> writes;
okim6258_device* msm;
unsigned char lastBusy;
unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel;
signed char msmDividerCount, msmClockCount;

View file

@ -571,7 +571,7 @@ void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) {
chipClock=3072000;
CHECK_CUSTOM_CLOCK;
rate=chipClock/32;
namco->device_clock_changed(rate);
namco->device_clock_changed(96000);
for (int i=0; i<chans; i++) {
oscBuf[i]->rate=rate;
}

View file

@ -130,9 +130,9 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
for (size_t i=0; i<len; i++) {
doPCM;
nes1_NP->Tick(1);
nes2_NP->TickFrameSequence(1);
nes2_NP->Tick(1);
nes1_NP->Tick(8);
nes2_NP->TickFrameSequence(8);
nes2_NP->Tick(8);
nes1_NP->Render(out1);
nes2_NP->Render(out2);
@ -140,7 +140,7 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
if (sample>32767) sample=32767;
if (sample<-32768) sample=-32768;
buf[0][i]=sample;
if (++writeOscBuf>=32) {
if (++writeOscBuf>=4) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
@ -332,7 +332,7 @@ void DivPlatformNES::tick(bool sysTick) {
if (chan[4].keyOn) {
if (dpcmMode && !skipRegisterWrites && dacSample>=0 && dacSample<parent->song.sampleLen) {
unsigned int dpcmAddr=sampleOffDPCM[dacSample];
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
unsigned int dpcmLen=parent->getSample(dacSample)->lengthDPCM>>4;
if (dpcmLen>255) dpcmLen=255;
goingToLoop=parent->getSample(dacSample)->isLoopable();
// write DPCM
@ -749,8 +749,11 @@ void DivPlatformNES::setFlags(const DivConfig& flags) {
}
CHECK_CUSTOM_CLOCK;
rate=chipClock;
if (useNP) {
rate/=8;
}
for (int i=0; i<5; i++) {
oscBuf[i]->rate=rate/32;
oscBuf[i]->rate=rate/(useNP?4:32);
}
dpcmModeDefault=flags.getBool("dpcmMode",true);

View file

@ -392,6 +392,15 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) {
}
}
void DivPlatformOPLL::switchMode(bool mode) {
if (mode==properDrums) return;
if (mode) {
} else {
}
}
int DivPlatformOPLL::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
@ -773,18 +782,10 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
if ((int)properDrums==c.value) break;
if (c.value) {
properDrums=true;
immWrite(0x0e,0x20);
drumState=0;
} else {
properDrums=false;
immWrite(0x0e,0x00);
drumState=0;
}
chan[6].freqChanged=true;
chan[7].freqChanged=true;
chan[8].freqChanged=true;
chan[9].freqChanged=true;
chan[10].freqChanged=true;
switchMode(properDrums);
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);

View file

@ -75,6 +75,7 @@ class DivPlatformOPLL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
void switchMode(bool mode);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);

View file

@ -17,9 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _USE_MATH_DEFINES
#include "pcspkr.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#ifdef __linux__
@ -190,9 +192,6 @@ const char** DivPlatformPCSpeaker::getRegisterSheet() {
return regCheatSheetPCSpeaker;
}
const float cut=0.05;
const float reso=0.06;
void DivPlatformPCSpeaker::acquire_unfilt(short** buf, size_t len) {
int out=0;
for (size_t i=0; i<len; i++) {
@ -229,8 +228,8 @@ void DivPlatformPCSpeaker::acquire_cone(short** buf, size_t len) {
}
}
float next=(pos>((freq+16)>>1) && !isMuted[0])?1:0;
low+=0.04*band;
band+=0.04*(next-low-band);
low+=cut*band;
band+=cut*(next-low-band);
float out=(low+band)*0.75;
if (out>1.0) out=1.0;
if (out<-1.0) out=-1.0;
@ -612,6 +611,17 @@ void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) {
rate=chipClock/PCSPKR_DIVIDER;
speakerType=flags.getInt("speakerType",0)&3;
oscBuf->rate=rate;
switch (speakerType) {
case 1:
cut=2.0*sin(M_PI*1900.0/rate);
reso=0.0;
break;
default:
cut=2.0*sin(M_PI*2375.0/rate);
reso=0.06;
break;
}
}
void DivPlatformPCSpeaker::notifyInsDeletion(void* ins) {

View file

@ -57,6 +57,9 @@ class DivPlatformPCSpeaker: public DivDispatch {
float low, band;
float low2, high2, band2;
float low3, band3;
float cut;
float reso;
unsigned short freq, lastFreq;
unsigned char regPool[2];

View file

@ -296,6 +296,10 @@ void DivPlatformSMS::tick(bool sysTick) {
rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
chan[i].writeVol=false;
}
if (chan[i].keyOff) {
rWrite(0,0x9f|i<<5);
chan[i].keyOff=false;
}
}
}
@ -309,6 +313,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
chan[c.chan].actualNote=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOff=false;
//if (!parent->song.brokenOutVol2) {
chan[c.chan].writeVol=true;
chan[c.chan].outVol=chan[c.chan].vol;
@ -321,7 +326,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
rWrite(0,0x9f|c.chan<<5);
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:

View file

@ -733,6 +733,7 @@ int DivPlatformSNES::getRegisterPoolSize() {
void DivPlatformSNES::initEcho() {
unsigned char esa=0xf8-(echoDelay<<3);
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
if (echoOn) {
rWrite(0x6d,esa);
rWrite(0x7d,echoDelay);
@ -742,13 +743,14 @@ void DivPlatformSNES::initEcho() {
for (int i=0; i<8; i++) {
rWrite(0x0f+(i<<4),echoFIR[i]);
}
rWrite(0x6c,control);
} else {
rWrite(0x6d,0);
rWrite(0x7d,0);
rWrite(0x2c,0);
rWrite(0x3c,0);
rWrite(0x6c,control);
rWrite(0x7d,0);
rWrite(0x6d,0xff);
}
writeControl=true;
}
void DivPlatformSNES::reset() {

View file

@ -356,12 +356,12 @@ void DivPlatformTIA::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformTIA::setFlags(const DivConfig& flags) {
if (flags.getInt("clockSel",0)) {
rate=COLOR_PAL*4.0/5.0;
chipClock=COLOR_PAL*4.0/5.0;
} else {
rate=COLOR_NTSC;
chipClock=COLOR_NTSC;
}
CHECK_CUSTOM_CLOCK;
chipClock=rate;
rate=chipClock;
mixingType=flags.getInt("mixingType",0)&3;
for (int i=0; i<2; i++) {
oscBuf[i]->rate=rate/114;

View file

@ -355,6 +355,9 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_FM_HARD_RESET:
opChan[ch].hardReset=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 127;
break;
@ -385,6 +388,9 @@ static int opChanOffsH[4]={
};
void DivPlatformYM2203Ext::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -395,6 +401,12 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
if (opChan[i].hardReset && opChan[i].keyOn) {
mustHardReset=true;
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
if (writeSomething) {
immWrite(0x28,writeMask);
@ -432,6 +444,27 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
opChan[i].freqChanged=true;
}
// channel macros
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
}
}
}
if (i==0 || fbAllOps) {
if (opChan[i].std.fb.had) {
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
}
}
// param macros
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
@ -491,6 +524,7 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
bool writeNoteOn=false;
unsigned char writeMask=2;
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -517,12 +551,36 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
writeNoteOn=true;
if (opChan[i].mask) {
writeMask|=1<<(4+i);
if (opChan[i].hardReset) {
hardResetMask|=1<<(4+i);
}
}
if (!opChan[i].hardReset) {
opChan[i].keyOn=false;
}
opChan[i].keyOn=false;
}
}
if (writeNoteOn) {
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<4; i++) {
if (opChan[i].keyOn && opChan[i].hardReset) {
// restore SL/RR
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
opChan[i].keyOn=false;
}
}
immWrite(0x28,writeMask);
}
}
}

View file

@ -378,6 +378,9 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_FM_HARD_RESET:
opChan[ch].hardReset=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 127;
break;
@ -408,6 +411,9 @@ static int opChanOffsH[4]={
};
void DivPlatformYM2608Ext::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -418,6 +424,12 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
if (opChan[i].hardReset && opChan[i].keyOn) {
mustHardReset=true;
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
if (writeSomething) {
immWrite(0x28,writeMask);
@ -455,6 +467,40 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
opChan[i].freqChanged=true;
}
// channel macros
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
}
}
}
if (i==0 || fbAllOps) {
if (opChan[i].std.fb.had) {
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
}
}
if (opChan[i].std.fms.had) {
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ams.had) {
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ex3.had) {
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
// param macros
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
@ -513,6 +559,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
bool writeNoteOn=false;
unsigned char writeMask=2;
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -539,12 +586,36 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
writeNoteOn=true;
if (opChan[i].mask) {
writeMask|=1<<(4+i);
if (opChan[i].hardReset) {
hardResetMask|=1<<(4+i);
}
}
if (!opChan[i].hardReset) {
opChan[i].keyOn=false;
}
opChan[i].keyOn=false;
}
}
if (writeNoteOn) {
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<4; i++) {
if (opChan[i].keyOn && opChan[i].hardReset) {
// restore SL/RR
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
opChan[i].keyOn=false;
}
}
immWrite(0x28,writeMask);
}
}
}

View file

@ -374,6 +374,9 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_FM_HARD_RESET:
opChan[ch].hardReset=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 127;
break;
@ -404,6 +407,9 @@ static int opChanOffsH[4]={
};
void DivPlatformYM2610BExt::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -414,6 +420,12 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
if (opChan[i].hardReset && opChan[i].keyOn) {
mustHardReset=true;
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
if (writeSomething) {
immWrite(0x28,writeMask);
@ -451,6 +463,39 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
opChan[i].freqChanged=true;
}
// channel macros
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
}
}
}
if (i==0 || fbAllOps) {
if (opChan[i].std.fb.had) {
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
}
}
if (opChan[i].std.fms.had) {
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ams.had) {
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ex3.had) {
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
// param macros
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
@ -509,6 +554,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
bool writeNoteOn=false;
unsigned char writeMask=2;
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -535,12 +581,36 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
writeNoteOn=true;
if (opChan[i].mask) {
writeMask|=1<<(4+i);
if (opChan[i].hardReset) {
hardResetMask|=1<<(4+i);
}
}
if (!opChan[i].hardReset) {
opChan[i].keyOn=false;
}
opChan[i].keyOn=false;
}
}
if (writeNoteOn) {
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<4; i++) {
if (opChan[i].keyOn && opChan[i].hardReset) {
// restore SL/RR
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
opChan[i].keyOn=false;
}
}
immWrite(0x28,writeMask);
}
}
}

View file

@ -374,6 +374,9 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_FM_HARD_RESET:
opChan[ch].hardReset=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 127;
break;
@ -404,6 +407,9 @@ static int opChanOffsH[4]={
};
void DivPlatformYM2610Ext::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -414,6 +420,12 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
if (opChan[i].hardReset && opChan[i].keyOn) {
mustHardReset=true;
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
if (writeSomething) {
immWrite(0x28,writeMask);
@ -451,6 +463,39 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
opChan[i].freqChanged=true;
}
// channel macros
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
}
}
}
if (i==0 || fbAllOps) {
if (opChan[i].std.fb.had) {
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
}
}
if (opChan[i].std.fms.had) {
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ams.had) {
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
}
if (opChan[i].std.ex3.had) {
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
// param macros
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
@ -509,6 +554,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
bool writeNoteOn=false;
unsigned char writeMask=2;
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -535,12 +581,36 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
writeNoteOn=true;
if (opChan[i].mask) {
writeMask|=1<<(4+i);
if (opChan[i].hardReset) {
hardResetMask|=1<<(4+i);
}
}
if (!opChan[i].hardReset) {
opChan[i].keyOn=false;
}
opChan[i].keyOn=false;
}
}
if (writeNoteOn) {
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<4; i++) {
if (opChan[i].keyOn && opChan[i].hardReset) {
// restore SL/RR
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
opChan[i].keyOn=false;
}
}
immWrite(0x28,writeMask);
}
}
}

View file

@ -1967,8 +1967,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
if (cycles<=0) {
// we have to tick
if (nextTick()) {
totalTicks=0;
totalSeconds=0;
/*totalTicks=0;
totalSeconds=0;*/
lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC);
logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
totalLoops++;

View file

@ -207,7 +207,7 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
// render data
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
logW("sample depth is wrong! (%d)",depth);
logW("sample depth is wrong! (%d)",(int)depth);
depth=DIV_SAMPLE_DEPTH_16BIT;
}
samples=(double)samples/samplePitchesSD[pitch];
@ -480,6 +480,7 @@ bool DivSample::saveRaw(const char* path) {
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
bool DivSample::initInternal(DivSampleDepth d, int count) {
logV("initInternal(%d,%d)",(int)d,count);
switch (d) {
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
if (data1!=NULL) delete[] data1;
@ -489,7 +490,7 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
break;
case DIV_SAMPLE_DEPTH_1BIT_DPCM: // DPCM
if (dataDPCM!=NULL) delete[] dataDPCM;
lengthDPCM=1+((((count+7)/8)+15)&(~15));
lengthDPCM=1+((((count-1)/8)+15)&(~15));
dataDPCM=new unsigned char[lengthDPCM];
memset(dataDPCM,0xaa,lengthDPCM);
break;
@ -748,7 +749,11 @@ void DivSample::convert(DivSampleDepth newDepth) {
setSampleCount((samples+7)&(~7));
break;
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
setSampleCount((1+((((samples+7)/8)+15)&(~15)))<<3);
if (samples) {
setSampleCount((1+((((samples-1)/8)+15)&(~15)))<<3);
} else {
setSampleCount(8);
}
break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
setSampleCount(((lengthZ+3)&(~0x03))*2);
@ -1168,7 +1173,7 @@ void DivSample::render(unsigned int formatMask) {
if (!initInternal(DIV_SAMPLE_DEPTH_1BIT_DPCM,samples)) return;
int accum=63;
int next=63;
for (unsigned int i=0; i<samples; i++) {
for (unsigned int i=0; (i<samples && (i>>3)<lengthDPCM); i++) {
next=((unsigned short)(data16[i]^0x8000))>>9;
if (next>accum) {
dataDPCM[i>>3]|=1<<(i&7);

View file

@ -1542,7 +1542,6 @@ void DivEngine::registerSystems() {
);
EffectHandlerMap es5506PreEffectHandlerMap={
{0x10, {DIV_CMD_WAVE, "10xx: Change waveform (00 to FF)",effectVal}},
{0x11, {DIV_CMD_ES5506_FILTER_MODE, "11xx: Set filter mode (00 to 03)",effectValAnd<3>}},
{0x14, {DIV_CMD_ES5506_FILTER_K1, "14xx: Set filter coefficient K1 low byte (00 to FF)",effectValShift<0>,constVal<0x00ff>}},
{0x15, {DIV_CMD_ES5506_FILTER_K1, "15xx: Set filter coefficient K1 high byte (00 to FF)",effectValShift<8>,constVal<0xff00>}},

View file

@ -52,7 +52,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
break;
default:
IGNORED++;
logD("Ignoring chip %d systemID %d",i,song.system[i]);
logD("Ignoring chip %d systemID %d",i,(int)song.system[i]);
break;
}
}
@ -94,6 +94,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
playSub(false);
//size_t tickCount=0;
bool done=false;
bool loopNow=false;
int loopPos=-1;
int fracWait=0; // accumulates fractional ticks
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(true);
@ -109,9 +110,17 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
while (!done) {
if (loopPos==-1) {
if (loopOrder==curOrder && loopRow==curRow && ticks==1 && loop) {
loopPos=zsm.getoffset();
zsm.setLoopPoint();
if (loopOrder==curOrder && loopRow==curRow && loop)
loopNow=true;
if (loopNow) {
// If Virtual Tempo is in use, our exact loop point
// might be skipped due to quantization error.
// If this happens, the tick immediately following is our loop point.
if (ticks==1 || !(loopOrder==curOrder && loopRow==curRow)) {
loopPos=zsm.getoffset();
zsm.setLoopPoint();
loopNow=false;
}
}
}
if (nextTick() || !playing) {

View file

@ -121,6 +121,7 @@ const char* aboutLine[]={
"SwapXFO",
"TakuikaNinja",
"TCORPStudios",
"Teuthida",
"The Blender Fiddler",
"TheDuccinator",
"theloredev",
@ -137,6 +138,7 @@ const char* aboutLine[]={
"ZoomTen (Zumi)",
"",
"-- additional feedback/fixes --",
"Electric Keet",
"fd",
"GENATARi",
"host12prog",

View file

@ -41,6 +41,13 @@ const char* chanOscRefs[]={
"Note Trigger"
};
const char* autoColsTypes[]={
"Off",
"Mode 1",
"Mode 2",
"Mode 3"
};
float FurnaceGUI::computeGradPos(int type, int chan) {
switch (type) {
case GUI_OSCREF_NONE:
@ -146,6 +153,19 @@ void FurnaceGUI::drawChanOsc() {
centerSettingReset=true;
}
ImGui::TableNextColumn();
ImGui::Text("Automatic columns");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
const char* previewColType=autoColsTypes[chanOscAutoColsType&3];
if (ImGui::BeginCombo("##AutoCols",previewColType)) {
for (int j=0; j<4; j++) {
const bool isSelected=(chanOscAutoColsType==j);
if (ImGui::Selectable(autoColsTypes[j],isSelected)) chanOscAutoColsType=j;
if (isSelected) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::EndTable();
}
@ -304,6 +324,7 @@ void FurnaceGUI::drawChanOsc() {
"- %C: channel short name\n"
"- %d: channel number (starting from 0)\n"
"- %D: channel number (starting from 1)\n"
"- %n: channel note\n"
"- %i: instrument name\n"
"- %I: instrument number (decimal)\n"
"- %x: instrument number (hex)\n"
@ -345,6 +366,25 @@ void FurnaceGUI::drawChanOsc() {
oscChans.push_back(i);
}
}
// 0: none
// 1: sqrt(chans)
// 2: sqrt(chans+1)
// 3: sqrt(chans)+1
switch (chanOscAutoColsType) {
case 1:
chanOscCols=sqrt(oscChans.size());
break;
case 2:
chanOscCols=sqrt(oscChans.size()+1);
break;
case 3:
chanOscCols=sqrt(oscChans.size())+1;
break;
}
if (chanOscCols<1) chanOscCols=1;
if (chanOscCols>64) chanOscCols=64;
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
for (size_t i=0; i<oscBufs.size(); i++) {
@ -539,6 +579,15 @@ void FurnaceGUI::drawChanOsc() {
text+=fmt::sprintf("%.2X",chanState->volume>>8);
break;
}
case 'n': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL || !(chanState->keyOn)) break;
short tempNote=chanState->note; //all of this conversion is necessary because notes 100-102 are special chars
short noteMod=tempNote%12+12; //also note 0 is a BUG, hence +12 on the note and -1 on the octave
short oct=tempNote/12-1;
text+=fmt::sprintf("%s",noteName(noteMod,oct));
break;
}
case '%':
text+='%';
break;

View file

@ -69,6 +69,7 @@ void FurnaceGUI::drawChannels() {
if (dragItem->IsDataType("FUR_CHAN")) {
if (chanToMove!=i && chanToMove>=0) {
e->swapChannelsP(chanToMove,i);
MARK_MODIFIED;
}
chanToMove=-1;
}

View file

@ -330,9 +330,6 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
if (ImGui::MenuItem("save")) {
doAction(GUI_ACTION_INS_LIST_SAVE);
}
if (ImGui::MenuItem("save (legacy .fui)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save (.dmp)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}

View file

@ -62,7 +62,6 @@
#define FM_CHIP_DEBUG \
COMMON_CHIP_DEBUG; \
ImGui::Text("- lastBusy: %d",ch->lastBusy); \
ImGui::Text("- delay: %d",ch->delay);
#define FM_OPN_CHIP_DEBUG \
@ -167,7 +166,6 @@
ImGui::TextColored(ch->hardReset?colorOn:colorOff,">> hardReset"); \
ImGui::TextColored(ch->opMaskChanged?colorOn:colorOff,">> opMaskChanged"); \
ImGui::TextColored(ch->dacMode?colorOn:colorOff,">> DACMode"); \
ImGui::TextColored(ch->dacReady?colorOn:colorOff,">> DACReady"); \
ImGui::TextColored(ch->dacDirection?colorOn:colorOff,">> DACDirection");
#define GENESIS_OPCHAN_DEBUG \
@ -381,7 +379,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- pcmR: %d",ch->pcmR);
ImGui::Text("- pcmCycles: %d",ch->pcmCycles);
ImGui::Text("- sampleBank: %d",ch->sampleBank);
ImGui::Text("- lastBusy: %d",ch->lastBusy);
COMMON_CHIP_DEBUG_BOOL;
break;
}
@ -389,7 +386,6 @@ void putDispatchChip(void* data, int type) {
DivPlatformAY8910* ch=(DivPlatformAY8910*)data;
ImGui::Text("> AY-3-8910");
COMMON_CHIP_DEBUG;
ImGui::Text("- lastBusy: %d",ch->lastBusy);
ImGui::Text("- sampleBank: %d",ch->sampleBank);
ImGui::Text("- stereoSep: %d",ch->stereoSep);
ImGui::Text("- delay: %d",ch->delay);

View file

@ -642,9 +642,6 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_INS_LIST_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE);
break;
case GUI_ACTION_INS_LIST_SAVE_OLD:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD);
break;
case GUI_ACTION_INS_LIST_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP);
break;

View file

@ -1,6 +1,7 @@
#include "gui.h"
#include "guiConst.h"
#include <imgui.h>
#include "IconsFontAwesome4.h"
void FurnaceGUI::drawEffectList() {
if (nextWindow==GUI_WINDOW_EFFECT_LIST) {
@ -11,7 +12,28 @@ void FurnaceGUI::drawEffectList() {
if (!effectListOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(60.0f*dpiScale,20.0f*dpiScale),ImVec2(canvasW,canvasH));
if (ImGui::Begin("Effect List",&effectListOpen,globalWinFlags)) {
ImGui::Text("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
float availB=ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing();
if (availB>0) {
ImGui::PushTextWrapPos(availB);
ImGui::TextWrapped("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
ImGui::PopTextWrapPos();
ImGui::SameLine();
}
ImGui::Button(ICON_FA_BARS "##SortEffects");
if (ImGui::BeginPopupContextItem("effectSort",ImGuiPopupFlags_MouseButtonLeft)) {
for (int i=0; i<9; i++) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColorsSort[i]]);
ImGui::Checkbox(fxColorsNames[i],&effectsShow[i]);
ImGui::PopStyleColor();
}
if (ImGui::Button("All")) memset(effectsShow,1,sizeof(bool)*10);
ImGui::SameLine();
if (ImGui::Button("None")) memset(effectsShow,0,sizeof(bool)*10);
ImGui::EndPopup();
}
if (ImGui::BeginTable("effectList",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
@ -25,11 +47,24 @@ void FurnaceGUI::drawEffectList() {
const char* prevName=NULL;
for (int i=0; i<256; i++) {
const char* name=e->getEffectDesc(i,cursor.xCoarse);
bool effectShow = true;
if (name==prevName) {
continue;
}
prevName=name;
if (name!=NULL) {
switch (fxColors[i]) {
case GUI_COLOR_PATTERN_EFFECT_MISC: effectShow = effectsShow[8]; break;
case GUI_COLOR_PATTERN_EFFECT_SONG: effectShow = effectsShow[1]; break;
case GUI_COLOR_PATTERN_EFFECT_SPEED: effectShow = effectsShow[3]; break;
case GUI_COLOR_PATTERN_EFFECT_TIME: effectShow = effectsShow[2]; break;
case GUI_COLOR_PATTERN_EFFECT_PITCH: effectShow = effectsShow[0]; break;
case GUI_COLOR_PATTERN_EFFECT_PANNING: effectShow = effectsShow[4]; break;
case GUI_COLOR_PATTERN_EFFECT_VOLUME: effectShow = effectsShow[5]; break;
case GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY: effectShow = effectsShow[6]; break;
case GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY: effectShow = effectsShow[7]; break;
default: effectShow = true; break;
}
if (name!=NULL && effectShow) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushFont(patFont);

View file

@ -1438,8 +1438,8 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;
curNibble=!curNibble;
if (orderEditMode==2 || orderEditMode==3) {
curNibble=!curNibble;
if (!curNibble) {
if (orderEditMode==2) {
orderCursor++;
@ -1653,16 +1653,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale
);
break;
case GUI_FILE_INS_SAVE_OLD:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Instrument",
{"Furnace instrument", "*.fui"},
"Furnace instrument{.fui}",
workingDirIns,
dpiScale
);
break;
case GUI_FILE_INS_SAVE_DMP:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
@ -1845,6 +1835,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale
);
break;
case GUI_FILE_LOAD_HEAD_FONT:
if (!dirExists(workingDirFont)) workingDirFont=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Font",
{"compatible files", "*.ttf *.otf *.ttc"},
"compatible files{.ttf,.otf,.ttc}",
workingDirFont,
dpiScale
);
break;
case GUI_FILE_LOAD_PAT_FONT:
if (!dirExists(workingDirFont)) workingDirFont=getHomeDir();
hasOpened=fileDialog->openLoad(
@ -3869,6 +3869,7 @@ bool FurnaceGUI::loop() {
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
bigFont=mainFont;
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
@ -4159,7 +4160,7 @@ bool FurnaceGUI::loop() {
exitDisabledTimer=1;
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true,true);
ImGui::TreePop();
}
}
@ -4486,17 +4487,6 @@ bool FurnaceGUI::loop() {
}
MEASURE(songInfo,drawSongInfo());
MEASURE(orders,drawOrders());
if (introMonOpen) {
int totalTicks=e->getTotalTicks();
int totalSeconds=e->getTotalSeconds();
double newMonitorPos=totalSeconds+((double)totalTicks/1000000.0);
if (fabs(newMonitorPos-monitorPos)>0.08) monitorPos=newMonitorPos;
drawIntro(monitorPos,true);
if (e->isPlaying()) monitorPos+=ImGui::GetIO().DeltaTime;
}
MEASURE(sampleList,drawSampleList());
MEASURE(sampleEdit,drawSampleEdit());
MEASURE(waveList,drawWaveList());
@ -4531,6 +4521,11 @@ bool FurnaceGUI::loop() {
MEASURE(effectList,drawEffectList());
}
for (int i=0; i<e->getTotalChannelCount(); i++) {
keyHit1[i]-=0.2f;
if (keyHit1[i]<0.0f) keyHit1[i]=0.0f;
}
activateTutorial(GUI_TUTORIAL_OVERVIEW);
if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen);
@ -4592,7 +4587,6 @@ bool FurnaceGUI::loop() {
case GUI_FILE_INS_OPEN:
case GUI_FILE_INS_OPEN_REPLACE:
case GUI_FILE_INS_SAVE:
case GUI_FILE_INS_SAVE_OLD:
case GUI_FILE_INS_SAVE_DMP:
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
@ -4628,6 +4622,7 @@ bool FurnaceGUI::loop() {
workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_LOAD_MAIN_FONT:
case GUI_FILE_LOAD_HEAD_FONT:
case GUI_FILE_LOAD_PAT_FONT:
workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
@ -4695,9 +4690,6 @@ bool FurnaceGUI::loop() {
if (curFileDialog==GUI_FILE_INS_SAVE) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_OLD) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_DMP) {
checkExtension(".dmp");
}
@ -4791,11 +4783,6 @@ bool FurnaceGUI::loop() {
e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song);
}
break;
case GUI_FILE_INS_SAVE_OLD:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
e->song.ins[curIns]->save(copyOfName.c_str(),true);
}
break;
case GUI_FILE_INS_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) {
@ -5097,6 +5084,9 @@ bool FurnaceGUI::loop() {
case GUI_FILE_LOAD_MAIN_FONT:
settings.mainFontPath=copyOfName;
break;
case GUI_FILE_LOAD_HEAD_FONT:
settings.headFontPath=copyOfName;
break;
case GUI_FILE_LOAD_PAT_FONT:
settings.patFontPath=copyOfName;
break;
@ -6025,6 +6015,7 @@ bool FurnaceGUI::loop() {
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
bigFont=mainFont;
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
@ -6093,7 +6084,6 @@ bool FurnaceGUI::init() {
clockOpen=e->getConfBool("clockOpen",false);
speedOpen=e->getConfBool("speedOpen",true);
groovesOpen=e->getConfBool("groovesOpen",false);
introMonOpen=e->getConfBool("introMonOpen",false);
regViewOpen=e->getConfBool("regViewOpen",false);
logOpen=e->getConfBool("logOpen",false);
effectListOpen=e->getConfBool("effectListOpen",true);
@ -6152,6 +6142,7 @@ bool FurnaceGUI::init() {
pianoInputPadMode=e->getConfInt("pianoInputPadMode",pianoInputPadMode);
chanOscCols=e->getConfInt("chanOscCols",3);
chanOscAutoColsType=e->getConfInt("chanOscAutoColsType",0);
chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER);
chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER);
chanOscTextX=e->getConfFloat("chanOscTextX",0.0f);
@ -6480,6 +6471,7 @@ bool FurnaceGUI::init() {
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
bigFont=mainFont;
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
@ -6611,7 +6603,6 @@ void FurnaceGUI::commitState() {
e->setConf("clockOpen",clockOpen);
e->setConf("speedOpen",speedOpen);
e->setConf("groovesOpen",groovesOpen);
e->setConf("introMonOpen",introMonOpen);
e->setConf("regViewOpen",regViewOpen);
e->setConf("logOpen",logOpen);
e->setConf("effectListOpen",effectListOpen);
@ -6670,6 +6661,7 @@ void FurnaceGUI::commitState() {
// commit per-chan osc state
e->setConf("chanOscCols",chanOscCols);
e->setConf("chanOscAutoColsType",chanOscAutoColsType);
e->setConf("chanOscColorX",chanOscColorX);
e->setConf("chanOscColorY",chanOscColorY);
e->setConf("chanOscTextX",chanOscTextX);
@ -6826,6 +6818,7 @@ FurnaceGUI::FurnaceGUI():
iconFont(NULL),
patFont(NULL),
bigFont(NULL),
headFont(NULL),
fontRange(NULL),
prevInsData(NULL),
curIns(0),
@ -6895,7 +6888,6 @@ FurnaceGUI::FurnaceGUI():
clockOpen(false),
speedOpen(true),
groovesOpen(false),
introMonOpen(false),
basicMode(true),
shortIntro(false),
insListDir(false),
@ -7114,6 +7106,7 @@ FurnaceGUI::FurnaceGUI():
oscWindowSize(20.0f),
oscZoomSlider(false),
chanOscCols(3),
chanOscAutoColsType(0),
chanOscColorX(GUI_OSCREF_CENTER),
chanOscColorY(GUI_OSCREF_CENTER),
chanOscWindowSize(20.0f),
@ -7288,6 +7281,8 @@ FurnaceGUI::FurnaceGUI():
memset(macroRelLabel,0,32);
memset(emptyLabel,0,32);
memset(emptyLabel2,0,32);
//effect sorting
memset(effectsShow,1,sizeof(bool)*10);
strncpy(noteOffLabel,"OFF",32);
strncpy(noteRelLabel,"===",32);

View file

@ -402,7 +402,6 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_INS_OPEN,
GUI_FILE_INS_OPEN_REPLACE,
GUI_FILE_INS_SAVE,
GUI_FILE_INS_SAVE_OLD,
GUI_FILE_INS_SAVE_DMP,
GUI_FILE_WAVE_OPEN,
GUI_FILE_WAVE_OPEN_REPLACE,
@ -424,6 +423,7 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_EXPORT_CMDSTREAM_BINARY,
GUI_FILE_EXPORT_ROM,
GUI_FILE_LOAD_MAIN_FONT,
GUI_FILE_LOAD_HEAD_FONT,
GUI_FILE_LOAD_PAT_FONT,
GUI_FILE_IMPORT_COLORS,
GUI_FILE_IMPORT_KEYBINDS,
@ -607,7 +607,6 @@ enum FurnaceGUIActions {
GUI_ACTION_INS_LIST_OPEN,
GUI_ACTION_INS_LIST_OPEN_REPLACE,
GUI_ACTION_INS_LIST_SAVE,
GUI_ACTION_INS_LIST_SAVE_OLD,
GUI_ACTION_INS_LIST_SAVE_DMP,
GUI_ACTION_INS_LIST_MOVE_UP,
GUI_ACTION_INS_LIST_MOVE_DOWN,
@ -1371,6 +1370,7 @@ class FurnaceGUI {
ImFont* iconFont;
ImFont* patFont;
ImFont* bigFont;
ImFont* headFont;
ImWchar* fontRange;
ImVec4 uiColors[GUI_COLOR_MAX];
ImVec4 volColors[128];
@ -1389,7 +1389,7 @@ class FurnaceGUI {
char emptyLabel2[32];
struct Settings {
int mainFontSize, patFontSize, iconSize;
int mainFontSize, patFontSize, headFontSize, iconSize;
int audioEngine;
int audioQuality;
int audioChans;
@ -1406,6 +1406,7 @@ class FurnaceGUI {
String tg100Path;
String mu5Path;
int mainFont;
int headFont;
int patFont;
int audioRate;
int audioBufSize;
@ -1525,8 +1526,10 @@ class FurnaceGUI {
int newSongBehavior;
int memUsageUnit;
int cursorFollowsWheel;
int noDMFCompat;
unsigned int maxUndoSteps;
String mainFontPath;
String headFontPath;
String patFontPath;
String audioDevice;
String midiInDevice;
@ -1545,6 +1548,7 @@ class FurnaceGUI {
Settings():
mainFontSize(18),
patFontSize(18),
headFontSize(27),
iconSize(16),
audioEngine(DIV_AUDIO_SDL),
audioQuality(0),
@ -1679,8 +1683,10 @@ class FurnaceGUI {
newSongBehavior(0),
memUsageUnit(1),
cursorFollowsWheel(0),
noDMFCompat(0),
maxUndoSteps(100),
mainFontPath(""),
headFontPath(""),
patFontPath(""),
audioDevice(""),
midiInDevice(""),
@ -1727,7 +1733,7 @@ class FurnaceGUI {
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen;
bool groovesOpen, introMonOpen;
bool groovesOpen;
bool basicMode, shortIntro;
bool insListDir, waveListDir, sampleListDir;
@ -1970,7 +1976,7 @@ class FurnaceGUI {
bool oscZoomSlider;
// per-channel oscilloscope
int chanOscCols, chanOscColorX, chanOscColorY;
int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY;
float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify;
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize;
String chanOscTextFormat;
@ -2031,6 +2037,9 @@ class FurnaceGUI {
bool pianoReadonly;
int pianoOffset, pianoOffsetEdit;
int pianoView, pianoInputPadMode;
//effect sorting
bool effectsShow[10];
// TX81Z
bool hasACED;
@ -2074,7 +2083,7 @@ class FurnaceGUI {
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, float maxRr, const ImVec2& size, unsigned short instType);
void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size);
bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange);
bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false);
void kvsConfig(DivInstrument* ins);
void drawFMPreview(const ImVec2& size);
void renderFMPreview(const DivInstrumentFM& params, int pos=0);

View file

@ -208,6 +208,32 @@ const char* resampleStrats[]={
"best possible"
};
const FurnaceGUIColors fxColorsSort[]={//used for sorting
GUI_COLOR_PATTERN_EFFECT_PITCH,
GUI_COLOR_PATTERN_EFFECT_SONG,
GUI_COLOR_PATTERN_EFFECT_TIME,
GUI_COLOR_PATTERN_EFFECT_SPEED,
GUI_COLOR_PATTERN_EFFECT_PANNING,
GUI_COLOR_PATTERN_EFFECT_VOLUME,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_INVALID
};
const char* fxColorsNames[]={
"Pitch",
"Song",
"Time",
"Speed",
"Panning",
"Volume",
"System Primary",
"System Secondary",
"Miscellaneous",
"Invalid"
};
const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // 00
GUI_COLOR_PATTERN_EFFECT_PITCH, // 01
@ -641,7 +667,6 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("INS_LIST_OPEN", "Open", 0),
D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("INS_LIST_SAVE", "Save", 0),
D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0),
D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0),
D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),

View file

@ -57,4 +57,6 @@ extern const FurnaceGUIActionDef guiActions[];
extern const FurnaceGUIColorDef guiColors[];
extern const int altValues[24];
extern const int vgmVersions[7];
extern const FurnaceGUIColors fxColors[256];
extern const FurnaceGUIColors fxColors[256];
extern const FurnaceGUIColors fxColorsSort[10];
extern const char* fxColorsNames[10];

View file

@ -2288,9 +2288,6 @@ void FurnaceGUI::drawInsEdit() {
ImGui::SetTooltip("Save");
}
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
if (ImGui::MenuItem("save in legacy format...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save as .dmp...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}
@ -4688,7 +4685,7 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Separator();
P(ImGui::Checkbox("Per-channel wave offset/length",&ins->n163.perChanPos));
P(ImGui::Checkbox("Per-channel wave position/length",&ins->n163.perChanPos));
if (ins->n163.perChanPos) {
if (ImGui::BeginTable("N1PerChPos",3)) {
@ -4700,7 +4697,7 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn();
ImGui::Text("Ch");
ImGui::TableNextColumn();
ImGui::Text("Offset");
ImGui::Text("Position");
ImGui::TableNextColumn();
ImGui::Text("Length");
@ -4732,7 +4729,7 @@ void FurnaceGUI::drawInsEdit() {
ImGui::EndTable();
}
} else {
if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER
if (ImGui::InputInt("Position##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER
if (ins->n163.wavePos<0) ins->n163.wavePos=0;
if (ins->n163.wavePos>255) ins->n163.wavePos=255;
}
@ -5563,6 +5560,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6295) waveMax=0;
if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
if (ins->type==DIV_INS_K007232) waveMax=0;
if (ins->type==DIV_INS_ES5506) waveMax=0;
if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_K053260) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;

View file

@ -104,10 +104,7 @@ void FurnaceGUI::endIntroTune() {
void FurnaceGUI::drawIntro(double introTime, bool monitor) {
if (monitor) {
if (introTime<0.0) introTime=0.0;
if (introTime>11.0) introTime=11.0;
if (!introMonOpen) return;
if (introPos<(shortIntro?1.0:11.0)) return;
return;
}
if (introPos<(shortIntro?1.0:11.0) || monitor) {
if (!monitor) {
@ -117,7 +114,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
ImGui::SetNextWindowSize(ImVec2(canvasW,canvasH));
if (introPos<0.1) ImGui::SetNextWindowFocus();
}
if (ImGui::Begin(monitor?"IntroMon X":"Intro",monitor?(&introMonOpen):NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) {
if (ImGui::Begin(monitor?"IntroMon X":"Intro",NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) {
if (monitor) {
if (ImGui::Button("Preview")) {
introPos=0;

View file

@ -829,8 +829,6 @@ void FurnaceGUI::drawPattern() {
ImGui::GetColorU32(chanHeadBase)
);
}
keyHit1[i]-=0.2f;
if (keyHit1[i]<0.0f) keyHit1[i]=0.0f;
}
}

View file

@ -991,7 +991,7 @@ void FurnaceGUI::initSystemPresets() {
}
);
ENTRY(
"PC + AdLib/Sound Blaster (drums mode)", {
"PC + Sound Blaster (drums mode)", {
CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, ""),
CH(DIV_SYSTEM_PCM_DAC, 1.0f, 0,

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@
#include "misc/cpp/imgui_stdlib.h"
#include <imgui.h>
bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) {
bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu) {
bool altered=false;
bool restart=settings.restartOnFlagChange && modifyOnChange;
bool supportsCustomRate=true;
@ -1564,7 +1564,13 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
}
altered=true;
}
if (i<7) ImGui::SameLine();
if (i<7) {
if (fromMenu) {
ImGui::SameLine();
} else {
sameLineMaybe();
}
}
}
if (CWSliderInt("Delay##EchoDelay",&echoDelay,0,15)) {