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

This commit is contained in:
YohananDiamond 2023-07-31 14:27:58 -03:00
commit 8b8f90893e
67 changed files with 3746 additions and 3027 deletions

BIN
demos/genesis/mm5_boss.fur Normal file

Binary file not shown.

View file

@ -33,6 +33,6 @@ Everything from the instrument list applies here also, with one major difference
![samples window](samples.png)
Everything from the wavetables list applies here also, with the addition of two buttons:
Everything from the wavetables list applies here also, with the addition of two buttons before the Delete button:
- **Preview**: Plays the selected sample at its default note.
- **Stop preview**: Stops the sample playback.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 294 KiB

View file

@ -1,11 +1,3 @@
# instrument list
![instrument list](list.png)
click on an instrument to select it.
double-click to open the instrument editor.
# instrument editor
every instrument can be renamed and have its type changed.
@ -66,14 +58,16 @@ Some macros are "bitmap" style. They represent a number of "bits" that can be to
The number between the macro type label and the macro type button is the macro length in steps. The `-` and `+` buttons change the length of the macro. Start out by adding at least a few steps.
The values of the macro can be drawn in the "bar graph box". Just beneath the box is shorter bar graph.
- Click to set the start point of a loop; the end point is the last value or release point. Right-click to remove the loop.
- Shift-click to set the release point. When played, the macro will hold here until the note is released. Right-click to remove the release point.
The values of the macro can be drawn in the "bar graph" box.
Just beneath the box is a shorter bar that controls looping.
- Click to set the start point of a loop; the end point is the last value or release point. It appears as half-height bars. Right-click to remove the loop.
- Shift-click to set the release point. When played, the macro will hold here until the note is released. It appears as a full-height bar. Right-click to remove the release point.
Finally, the sequence of values can be directly edited in the text box at the bottom.
- The loop start is entered as a `|`.
- The release point is entered as a `/`.
- In arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). No matter the note played, `@` values will be played at that exact note. This is especially useful for noise instruments with preset periods.
- In arpeggio macros, a value starting with a `@` is an absolute note (instead of a relative shift). No matter the note entered in the pattern, `@` values will be played at that exact note. This is especially useful for noise instruments with preset periods.
### ADSR
@ -100,15 +94,29 @@ Finally, the sequence of values can be directly edited in the text box at the bo
- **Phase** is which part of the waveform the macro will start at, measured in 1/1024 increments.
- **Shape** is the waveform used. Triangle is the default, and Saw and Square are exactly as they say.
# samples
# wavetable
This tab appears for PC Engine, FDS, Namco WSG, and other wavetable-based instruments.
![wavetable tab](wavetable.png)
When **Enable synthesizer** is off, the only option is to select a wavetable entry with the text entry box beneath the **Wave 1** preview.
To use the wavetable synthesizer, refer to the bottom part of [the wavetable documentation](../5-wave/README.md).
# sample
This tab appears for Generic PCM, SNES, Amiga, and other sample-based instruments.
![](sample-map.png)
![sample tab](sample-map.png)
- **Initial Sample**: the sample that the instrument will use.
- **Use wavetable**: instead of samples, use wavetables. this causes the [Wavetables](../5-wave/README.md) tab to appear next to Sample.
- depending on the system and use of the wavetable synthesizer, this may or may not be reproducible on hardware.
- **Use sample map**: assigns a sample to each note.
- samples will be played at their default pitch.
- to set a note's sample, click the list entry in the `#` column then type the number of the sample.
- to set a note's sample, click the list entry in the "#" column then type the number of the sample.
- to set the pitch at which a sample is played, click the list entry in the "note" column and press the key for the new note.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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

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

View file

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

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

View file

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

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

View file

@ -927,12 +927,13 @@ void DivEngine::runExportThread() {
}
}
float* outBuf[2];
float* outBuf[DIV_MAX_OUTPUTS];
memset(outBuf,0,sizeof(void*)*DIV_MAX_OUTPUTS);
outBuf[0]=new float[EXPORT_BUFSIZE];
outBuf[1]=new float[EXPORT_BUFSIZE];
short* sysBuf[DIV_MAX_CHIPS];
for (int i=0; i<song.systemLen; i++) {
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
sysBuf[i]=new short[EXPORT_BUFSIZE*disCont[i].dispatch->getOutputCount()];
}
// take control of audio output
@ -3850,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();
@ -4041,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;
@ -4063,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;
@ -4079,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;
@ -4106,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;
@ -4122,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) {
@ -1253,6 +1219,7 @@ int DivPlatformES5506::init(DivEngine* p, int channels, int sugRate, const DivCo
dumpWrites=false;
skipRegisterWrites=false;
volScale=0;
curPage=0;
for (int i=0; i<32; i++) {
isMuted[i]=false;

View file

@ -109,7 +109,8 @@ class DivPlatformFMBase: public DivDispatch {
if (!skipRegisterWrites && !flushFirst) {
if (writes.empty()) {
writes.push_back(QueuedWrite(a,v));
} else if (writes.size()>16 || writes.front().addrOrVal) {
} else if ((writes.size()>16 && writes.front().addr!=0xf0) || writes.front().addrOrVal) {
// $f0 is used by OPN hard reset
writes.push_back(QueuedWrite(a,v));
} else {
writes.push_front(QueuedWrite(a,v));

View file

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

View file

@ -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);
}
}
@ -576,6 +570,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 +598,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 +631,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 +646,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;
@ -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

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

View file

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

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

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);
@ -491,6 +503,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 +530,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);
@ -513,6 +525,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 +552,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);
@ -509,6 +521,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 +548,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);
@ -509,6 +521,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 +548,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

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

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

@ -84,7 +84,7 @@ const char* aboutLine[]={
"djtuBIG-MaliceX",
"dumbut",
"Eknous-P",
"ElectricKeet",
"Electric Keet",
"EpicTyphlosion",
"FΛDE",
"Forte",
@ -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

@ -304,6 +304,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"
@ -539,6 +540,12 @@ void FurnaceGUI::drawChanOsc() {
text+=fmt::sprintf("%.2X",chanState->volume>>8);
break;
}
case 'n': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL || !(chanState->keyOn)) break;
text+=fmt::sprintf("%s",noteName(short (chanState->note),0));
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

@ -648,9 +648,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

@ -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(
@ -4601,7 +4591,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;
@ -4704,9 +4693,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");
}
@ -4800,11 +4786,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())) {
@ -7317,6 +7298,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,
@ -612,7 +611,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,
@ -1531,6 +1529,7 @@ class FurnaceGUI {
int newSongBehavior;
int memUsageUnit;
int cursorFollowsWheel;
int noDMFCompat;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
@ -1685,6 +1684,7 @@ class FurnaceGUI {
newSongBehavior(0),
memUsageUnit(1),
cursorFollowsWheel(0),
noDMFCompat(0),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),
@ -2038,6 +2038,9 @@ class FurnaceGUI {
bool pianoReadonly;
int pianoOffset, pianoOffsetEdit;
int pianoView, pianoInputPadMode;
//effect sorting
bool effectsShow[10];
// TX81Z
bool hasACED;

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
@ -646,7 +672,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

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