Merge branch 'tildearrow:master' into master

This commit is contained in:
DevEd 2023-09-17 13:43:47 -04:00 committed by GitHub
commit 16aed41e89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
103 changed files with 1358 additions and 268 deletions

1
.gitignore vendored
View file

@ -32,3 +32,4 @@ res/binary_to_compressed_c.exe
res/docpdf/manual.html
res/docpdf/manual.pdf
res/docpdf/.venv
res/furnace.appdata.xml

View file

@ -25,6 +25,15 @@ set(SYSTEM_SDL2_DEFAULT OFF)
include(CheckIncludeFile)
include(TestBigEndian)
execute_process(COMMAND git status RESULT_VARIABLE DONT_HAVE_GIT WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
if (NOT DONT_HAVE_GIT)
message(STATUS "Git is available")
else()
message(WARNING "either Git is not available, or this Git repository has not been initialized.
if you have used the \"Source code\" option in the GitHub release page, you are doing it wrong! unless you manually initialize submodules, the build is guaranteed to FAIL spectacularly!
read the \"developer info\" section of README.md for more information.")
endif()
if (ANDROID)
set(USE_RTMIDI_DEFAULT OFF)
set(WITH_PORTAUDIO_DEFAULT OFF)
@ -357,14 +366,16 @@ endif()
if (WITH_JACK)
find_package(PkgConfig REQUIRED)
pkg_check_modules(JACK REQUIRED jack)
list(APPEND AUDIO_SOURCES extern/weakjack/weak_libjack.c)
list(APPEND AUDIO_SOURCES src/audio/jack.cpp)
list(APPEND DEPENDENCIES_INCLUDE_DIRS ${JACK_INCLUDE_DIRS})
list(APPEND DEPENDENCIES_DEFINES HAVE_JACK)
list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${JACK_CFLAGS_OTHER})
list(APPEND DEPENDENCIES_LIBRARIES ${JACK_LIBRARIES})
list(APPEND DEPENDENCIES_LIBRARY_DIRS ${JACK_LIBRARY_DIRS})
list(APPEND DEPENDENCIES_LINK_OPTIONS ${JACK_LDFLAGS_OTHER})
list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${JACK_LDFLAGS})
list(APPEND DEPENDENCIES_DEFINES USE_WEAK_JACK)
#list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${JACK_CFLAGS_OTHER})
#list(APPEND DEPENDENCIES_LIBRARIES ${JACK_LIBRARIES})
#list(APPEND DEPENDENCIES_LIBRARY_DIRS ${JACK_LIBRARY_DIRS})
#list(APPEND DEPENDENCIES_LINK_OPTIONS ${JACK_LDFLAGS_OTHER})
#list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${JACK_LDFLAGS})
message(STATUS "Building with JACK support")
else()
message(STATUS "Building without JACK support")
@ -920,6 +931,15 @@ if (WARNINGS_ARE_ERRORS)
)
endif()
if (NOT ANDROID OR TERMUX)
if (NOT WIN32 AND NOT APPLE)
if (NOT DONT_HAVE_GIT)
add_custom_command(OUTPUT furnace.appdata.xml COMMAND res/make-appdata.sh ARGS ${CMAKE_SOURCE_DIR}/res/furnace.appdata.xml.in ${CMAKE_BINARY_DIR}/furnace.appdata.xml DEPENDS res/furnace.appdata.xml.in WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
list(APPEND USED_SOURCES furnace.appdata.xml)
endif()
endif()
endif()
if(ANDROID AND NOT TERMUX)
add_library(furnace SHARED ${USED_SOURCES})
elseif(WIN32)
@ -950,7 +970,9 @@ if (NOT ANDROID OR TERMUX)
include(GNUInstallDirs)
install(TARGETS furnace RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES res/furnace.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES res/furnace.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
if (NOT DONT_HAVE_GIT)
install(FILES ${CMAKE_BINARY_DIR}/furnace.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
endif()
install(DIRECTORY doc DESTINATION ${CMAKE_INSTALL_DOCDIR})
install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR}/other)
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace)

View file

@ -15,8 +15,8 @@ android {
}
minSdkVersion 21
targetSdkVersion 26
versionCode 175
versionName "0.6pre14"
versionCode 178
versionName "0.6pre16"
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DWARNINGS_ARE_ERRORS=ON"

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.tildearrow.furnace"
android:versionCode="175"
android:versionName="0.6pre14"
android:versionCode="178"
android:versionName="0.6pre16"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->

View file

@ -3,29 +3,34 @@
Furnace comes with a "basic mode" that can be toggled through the "settings" menu. it disables certain features in Furnace that may look intimidating or confusing for newcomers. if you find that a certain feature of Furnace is missing, see if this setting is enabled or not.
among the features that cannot be accessed in this mode are:
- edit menu:
- paste special
- operation masking
- input latch
- find and replace
- speed window:
- virtual tempo
- divider
- song length
- song info window:
- manual system naming
- tuning options
- right-clicking on the pattern window:
- gradient/fade
- scale
- randomize
- invert values
- flip selection
- collapse
- expand
- these windows:
- mixer
- grooves
- channels
- pattern manager
- compatibility flags
### edit menu
- paste special
- operation masking
- input latch
- find and replace
### speed window
- virtual tempo
- divider
- song length
### song info window
- manual system naming
- tuning options
### pattern right-click menu
- gradient/fade
- scale
- randomize
- invert values
- flip selection
- collapse
- expand
### other windows
- mixer
- grooves
- channels
- pattern manager
- compatibility flags

View file

@ -32,8 +32,10 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- **Loops**: number of additional times to play through `0Bxx` song loop.
- **Fade out (seconds)**: length of fade out after final loop.
- **Remember last values**
- **Store instrument name in .fui**: when enabled, saving an instrument will store its name. this may increase file size.
- **Load instrument name from .fui**: when enabled, loading an instrument will use the stored name (if present). otherwise, it will use the file name.
### Chip
### New Song
- **Initial system**: the system of chips loaded on starting Furnace.
- **Current system**: sets current chips as default.
@ -105,7 +107,11 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
### Metronome
- **Metronome volume**: sets volume of metronome.
- **Volume**: sets volume of metronome.
### Sample preview
- **Volume**: sets volume of sample preview.
## MIDI

View file

@ -8,7 +8,7 @@ the OPL FM editor is divided into 7 tabs:
- **Macros (OP2)**: for macros controlling FM parameters of operator 2.
- **Macros (OP3)**: for macros controlling FM parameters of operator 3 (only when 4-op flag is set and only on OPL3!).
- **Macros (OP4)**: for macros controlling FM parameters of operator 4 (only when 4-op flag is set and only on OPL3!).
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and OPL3 panning.
- **Macros**: for other macros (volume/arp/pitch/pan).
## FM
@ -40,13 +40,14 @@ these apply to each operator:
![FM ADSR chart](FM-ADSRchart.png)
- **Key Scale Rate (KSR)**: also known as "Rate Scale". determines the degree to which the envelope execution speed increases according to the pitch.
- **Frequency Multiplier (MULT)**: determines the operator frequency in relation to the pitch (0-15 range but be noted that 11, 13 and 14 have no effect!).
- **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on.
- note that values 11, 13 and 14 behave as 10, 12 and 12 respectively.
- **Waveform Select (WS)**: changes the waveform of the operator (OPL2 and OPL3 only, 0-3 range on OPL2 and 0-7 on OPL3).
- **Vibrato (VIB)**: makes the operator affected by LFO vibrato.
## macros
macros define the sequence of values passed to the given parameter. via macro, along with the previously mentioned parameters, the following can be controlled:
these macros allow you to control several parameters of FM per tick.
## FM Macros

View file

@ -6,7 +6,7 @@ the OPLL FM editor is divided into 5 tabs:
- **Macros (FM)**: for macros controlling algorithm, waveform and feedback.
- **Macros (OP1)**: for macros controlling FM parameters of operator 1.
- **Macros (OP2)**: for macros controlling FM parameters of operator 2.
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and preset.
- **Macros**: for other macros (volume/arp/pitch/patch).
## FM
@ -43,12 +43,13 @@ these apply to each operator:
![FM ADSR chart](FM-ADSRchart.png)
- **Envelope Scale (KSR)**: also known as "Key Scale". determines the degree to which the envelope execution speed increases according to the pitch.
- **Frequency Multiplier (MULT)**: determines the operator frequency in relation to the pitch. (0-10, 12, 15 range)
- **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on.
- note that values 11, 13 and 14 behave as 10, 12 and 12 respectively.
- **Vibrato (VIB)**: makes the operator affected by LFO vibrato.
## macros
macros define the sequence of values passed to the given parameter. via macro, along with the previously mentioned parameters, the following can be controlled:
these macros allow you to control several parameters of FM per tick.
## FM Macros

View file

@ -8,7 +8,7 @@ the FM editor is divided into 7 tabs:
- **Macros (OP2)**: for macros controlling FM parameters of operator 2.
- **Macros (OP3)**: for macros controlling FM parameters of operator 3.
- **Macros (OP4)**: for macros controlling FM parameters of operator 4.
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and noise generator.
- **Macros**: for other macros (volume/arp/pitch/noise).
## FM
@ -41,14 +41,14 @@ these apply to each operator:
![FM ADSR chart](FM-ADSRchart.png)
- **Envelope Scale (RS/KS)**: also known as "Key Scale" or "Rate Scale". determines the degree to which the envelope execution speed increases according to the pitch (0 to 3).
- **Frequency Multiplier (MULT)**: determines the operator frequency in relation to the pitch (0 to 15).
- **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on.
- **Fine Detune (DT)**: shifts the pitch a little (0 to 7).
- **Coarse Detune (DT2)**: shifts the pitch by tens of cents (0 to 3).
## macros
macros define the sequence of values passed to the given parameter. via macro, along with the previously mentioned parameters, the following can be controlled:
these macros allow you to control several parameters of FM per tick.
## FM Macros

View file

@ -8,7 +8,7 @@ the FM editor is divided into 7 tabs:
- **Macros (OP2)**: for macros controlling FM parameters of operator 2.
- **Macros (OP3)**: for macros controlling FM parameters of operator 3.
- **Macros (OP4)**: for macros controlling FM parameters of operator 4.
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and pitch.
- **Macros**: for other macros (volume/arp/pitch).
## FM
@ -44,13 +44,13 @@ these apply to each operator:
![FM ADSR chart](FM-ADSRchart.png)
- **Envelope Scale (RS/KS)**: also known as "Key Scale" or "Rate Scale". determines the degree to which the envelope execution speed increases according to the pitch (0 to 3).
- **Frequency Multiplier (MULT)**: determines the operator frequency in relation to the pitch (0 to 15).
- **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on.
- **Fine Detune (DT)**: shifts the pitch a little (0 to 7).
## macros
macros define the sequence of values passed to the given parameter. via macro, along with the previously mentioned parameters, the following can be controlled:
these macros allow you to control several parameters of FM per tick.
## FM Macros

View file

@ -1,4 +1,4 @@
# FM (OPM) instrument editor
# FM (OPZ) instrument editor
the FM editor is divided into 7 tabs:
@ -8,7 +8,7 @@ the FM editor is divided into 7 tabs:
- **Macros (OP2)**: for macros controlling FM parameters of operator 2
- **Macros (OP3)**: for macros controlling FM parameters of operator 3
- **Macros (OP4)**: for macros controlling FM parameters of operator 4
- **Macros**: for miscellaneous macros controlling volume, arpeggio, and YM2151 noise generator.
- **Macros**: for other macros (volume/arp/pitch/noise).
## FM
@ -43,7 +43,7 @@ these apply to each operator:
![FM ADSR chart](FM-ADSRchart.png)
- **Envelope Scale (RS/KS)**: also known as "Rate Scale" or "Key Scale". determines the degree to which the envelope execution speed increases according to the pitch (0 to 3).
- **Frequency Multiplier (MULT)**: determines the operator frequency in relation to the pitch (0 to 15).
- **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on.
- **Fine Frequency Multiplier (Fine)**: a fine control for MULT.
- **Envelope Generator Shift (EGS)**: adds a "handicap" to the envelope. in other words, the minimum volume of the operator.
- 0: no change
@ -69,7 +69,7 @@ each operator has a Fixed Frequency mode. once enabled, the operator runs at the
## macros
macros define the sequence of values passed to the given parameter. via macro, along with the previously mentioned parameters, the following can be controlled:
these macros allow you to control several parameters of FM per tick.
## FM Macros

View file

@ -69,8 +69,6 @@ furthermore, many of these chips have a limited amount of sample memory. check m
you can edit your samples in Furnace's sample editor, which can be accessed by clicking on `window` (at the top of the screen) then clicking on `sample editor`, or by double-clicking a sample in the sample list.
the changes you make will be applied as soon as you've committed them to your sample, but they can be undone and redone, just like text.
in there, you can modify certain data pertaining to your sample, such as the:
- volume of the sample in percentage, where 100% is the current level of the sample (note that you can distort it if you put it too high)
- the sample rate.

View file

@ -3,7 +3,7 @@
a backwards-compatible successor to the AY-3-8910, with increased volume resolution, duty cycle control, three envelopes and highly configurable noise generator.
sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since.
it is best known for being used in the Covox Sound Master, which didn't sell well either. it also observed very minimal success in Merit's CRT-250 machines, but only as a replacement for the AY-3-8910.
it is known for being used in the Covox Sound Master, which didn't sell well either.
emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted.

View file

@ -16,7 +16,7 @@ this console is powered by two sound chips: the [Yamaha YM2612](ym2612.md) and [
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `17xx`: **enable PCM channel.**
- this only works on channel 6.
- _this effect is here for compatibility reasons!_ it is otherwise recommended to use Sample type instruments (which automatically enable PCM mode when used).

View file

@ -1,4 +1,4 @@
# Namco 163 (also called N163, Namco C163, Namco 106 [_sic_], Namco 160 or Namco 129)
# Namco 163 (also called N163, Namco C163, Namco 106 [sic], Namco 160 or Namco 129)
this is one of Namco's NES mappers, with up to 8 wavetable channels.

View file

@ -29,7 +29,7 @@ afterwards everyone moved to Windows and software mixed PCM streaming...
- only in 4-op mode (OPL3).
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4; last 2 operators only in 4-op mode).
- `y` is the multiplier.
- `y` is the new MULT value..
- `17xx`: **set vibrato depth.**
- `0`: normal
- `1`: double

View file

@ -32,7 +32,7 @@ the YM2413 is equipped with the following features:
- `13xx`: **set operator 2 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator, either 1 or 2.
- `y` is the multiplier.
- `y` is the new MULT value..
- `18xx`: **toggle drums mode.**
- `0` disables it and `1` enables it.
- only in drums mode.

View file

@ -33,7 +33,7 @@ no plans have been made for TX81Z MIDI passthrough, because:
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `17xx`: **set LFO speed.**
- `18xx`: **set LFO waveform.** `xx` may be one of the following:
- `00`: saw

View file

@ -2,7 +2,7 @@
the SM8521 is the CPU and sound chip of the Game.com, a handheld console released in 1997 as a competitor to the infamous Nintendo Virtual Boy.
ultimately, most of the games for the Game.com ended up being failures in the eyes of reviewers, thus giving the Game.com a pretty bad reputation. this was one of the reasons that the Game.com only ended up selling at least 300,000 units. for these reasons and more, the Game.com ended up being discontinued in 2000.
sadly, the Game.com ended up being a failure as well, mostly due to poor quality games. the Game.com only lasted 3 years before being discontinued.
however, for its time, it was a pretty competitively priced system. the Game Boy Color was to be released in a year for $79.95, while the Game.com was released for $69.99; its later model, the Pocket Pro, was released in mid-1999 for $29.99 due to the Game.com's apparent significant decrease in value.

View file

@ -4,7 +4,7 @@ a relatively simple sound chip made by Texas Instruments. a derivative of it is
nominal mode of SN76489 has 3 square wave channels, with noise channel having only 3 preset frequencies to use (absurdly low, very low, low). to use more pitches, one can enable a mode which "steals" the frequency from square wave channel 3. by doing that, SN76489 becomes effectively a 3 channel sound chip. in addition, periodic noise mode can be enabled, with same caveats.
the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624, could only produce tones as low as 100Hz, and was clocked at 447 KHz. all later versions (such as the one in the Master System and Genesis) had a clock divider but ran on a faster clock... except for the SN76494, which can play notes as low as 13670 Hz (A -1). consequently, its pitch accuracy for higher notes is compromised.
the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624, could only produce tones as low as 100Hz, and was clocked at 447 KHz. all later versions (such as the one in the Master System and Genesis) had a clock divider but ran on a faster clock... except for the SN76494, which can play notes as low as 13670 Hz (A -1). as a result, its pitch accuracy for higher notes is compromised.
## SN7 versions

View file

@ -1,27 +1,17 @@
# Seta/Allumer X1-010
a sound chip designed by Seta, mainly used in their own arcade hardware from the late 80s to the early 2000s.
it has 2 output channels, but there is no known hardware taking advantage of stereo sound capabilities.
later hardware paired this with external bankswitching logic, but this isn't emulated yet.
Allumer rebadged it for their own arcade hardware.
the X1-010 is a chip used by Seta (and Allumer) in the Seta 1 and 2 arcade boards.
it has 16 channels, which can all be switched between PCM sample or wavetable playback mode.
wavetable playback needs to paired with envelope, similar to AY PSG, but shapes are stored in RAM and as such are user-definable.
it has 16 channels of wavetable sound with some support for 8-bit samples up to 128KB in length.
the sample frequency resolution is pretty bad in the low end though...
in Furnace, this chip can be configured for original arcade mono output or stereo output - it simulates early 'incorrect' emulation on some mono hardware, but it is also based on the assumption that each channel is connected to each output.
even though this chip has stereo output, no board (as far as we know) uses the two outputs that it has... instead, only one output is connected, effectively being used as a mono chip.
## waveform types
the chip also has some (complicated) hardware volume envelope capabilities, with half of its memory being usable for that purpose. the shape of a volume envelope is defined by user-provided 128×16 waveforms.
this chip supports 2 types of waveforms, needs to be paired to external 8 KB RAM to access these features:
the chip can store up to 32 sound waveforms and envelope waveforms at once.
one is a signed 8 bit mono waveform, operated like other wavetable based sound systems.
these are stored at the lower half of RAM at common case.
the other one ("Envelope") is a 4 bit stereo waveform, multiplied with the above and calculates final output, each nibble is used for each output channel.
these are stored at the upper half of RAM at common case.
both waveforms are 128 bytes (fixed size), freely allocated at each half of RAM except the channel register area: each half can store total 32/31 waveforms at once.
in Furnace, you can enable the envelope shape split mode. when it is set, its waveform will be split to the left and right halves for each output. each max size is 128 bytes, total 256 bytes.
this chip was the inspiration for Organya/PxTone (the former being used in a well-known game called Cave Story).
## effects
@ -34,8 +24,8 @@ in Furnace, you can enable the envelope shape split mode. when it is set, its wa
- range is 1.95KHz to 498KHz if the chip clock is 16MHz.
- `22xx`: **set envelope mode.**
- bit 0 sets whether envelope will affect this channel.
- bit 1 toggles the envelope one-shot mode. when it is set, channel is halted after envelope cycle is finished.
- bit 2 toggles the envelope shape split mode. when it is set, envelope shape will be split to left half and right half.
- bit 1 sets whether envelope will run once instead of looping.
- bit 2 sets whether split mode is used. I don't know what it does.
- bit 3/5 sets whether the right/left shape will mirror the original one.
- bit 4/6 sets whether the right/left output will mirror the original one.
- `23xx`: **set envelope period.**

View file

@ -16,7 +16,7 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `17xx`: **set LFO speed.**
- `18xx`: **set LFO waveform.**
- `00`: saw

View file

@ -18,7 +18,7 @@ several variants of this chip were released as well, with more features.
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator from 1 to 4.
- `y` is the multiplier.
- `y` is the new MULT value..
- `18xx`: **toggle extended channel 3 mode.**
- `0` disables it and `1` enables it.
- only in extended channel 3 chip.

View file

@ -18,7 +18,7 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `18xx`: **toggle extended channel 3 mode.**
- `0` disables it and `1` enables it.
- only in extended channel 3 chip.

View file

@ -16,7 +16,7 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and [2 differen
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `18xx`: **toggle extended channel 2 mode.**
- 0 disables it and 1 enables it.
- only in extended channel 2 chip.

View file

@ -15,7 +15,7 @@ it is backward compatible with the original chip.
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `18xx`: **toggle extended channel 3 mode.**
- 0 disables it and 1 enables it.
- only in extended channel 3 chip.

View file

@ -30,7 +30,7 @@ thanks to the Z80 sound CPU, DualPCM can play two samples at once! this mode spl
- `15xx`: **set operator 4 level.**
- `16xy`: **set multiplier of operator.**
- `x` is the operator (1-4).
- `y` is the multiplier.
- `y` is the new MULT value..
- `17xx`: **toggle LEGACY sample mode.**
- this only works on channel 6.
- **this effect exists only for compatibility reasons! its use is NOT recommented. use Sample type instruments instead.**

View file

@ -22,6 +22,15 @@ the index follows.
- freq-mod
- host12prog
- nicco1690
- polluks
- tildearrow
- WindowxDeveloper
## information
copyright © 2023 tildearrow and other authors.
this documentation is under the [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/) license.
you may reproduce, modify and/or distribute this documentation provided this copyright notice (including license and attribution) is present and any necessary disclaimers whether modifications have been made.
this documentation is provided as-is and without warranty of any kind.

1
extern/weakjack/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.swp

79
extern/weakjack/README.md vendored Normal file
View file

@ -0,0 +1,79 @@
Weak-JACK
=========
This small library abstracts the [JACK](http://jackaudio.org) Application Binary Interface.
Background and Motivation
-------------------------
The jack shared library needs to be installed system-wide (for all jack applications
to share), it can not be part of an application itself.
JACK developers take great care to not break binary compatibility of libjack. An
application compiled with one version of jack will work with all future versions
of jack. However, this only works well on GNU/Linux, BSD and to some extend on OSX.
weak-jack linking is useful (at least) in the following cases:
* the resulting application should not be directly linked to libjack.[so|dll|dylib]
IOW: the application should start even if libjack is not installed.
* the ABI of libjack is not stable. Note, this is only relevant for the Windows .dll
(e.g. applications compiled and linked with jack-1.9.9 will crash with jack-1.9.10
on windows. -- MSVC has a workaround: link by function-name, not ordinal, mingw
does not offer this)
* Reference new API functions (e.g. meta-data API or new latency compensation)
in the application, which is not available on older versions of jack.
Usage
-----
1. Copy the source files into your application's source (or use a git submodule)
2. replace all `#include<jack/*>` in your sources with `#include "weak-jack.h"`
3. add `weak_libjack.c` to the build-source of your project
(in case your build-system does not detect `#include` dependencies automatically,
also reference the header and .def file).
4. Define `USE_WEAK_JACK` for all platforms where you want to use weak-linking. Usually
`CFLAGS+=-DUSE_WEAK_JACK CXXFLAGS+=-DUSE_WEAK_JACK`
5. Do not link your application to libjack (`-ljack`) when `USE_WEAK_JACK` is defined.
Note the jack-headers still need to be present when compiling the application.
The application code itself does not need to be changed.
The first call to `jack_client_open()` will try to find and load libjack, if it cannot be
found, it will fail (return `NULL` and set `jack_status_t` if provided to `JackFailure`.)
It is possible to explicitly initialize and query availability of libjack using
`have_libjack();` it returns 0 if libjack is available and can be used. (see the header
file for non-zero error codes).
Caveats
-------
If libjack is not available, all `jack_*` API calls are turned into no-operation functions.
This is not a problem in general, as jack-applications will not use any part of the jack API if
jack_client_open fails. The only exception here may be `jack_ringbuffer`. Note that the ringbuffer
implementation is also part of libjack and will not be available.
The dummy implementation for the ringbuffer API is safe (read, writes are ignored and return failure
or zero-bytes length), but if your application depends on it to work, you're out of luck :)
The function wrappers in `weak_libjack.def` were collected pragmatically it's quite possible that
some JACK API calls have been missed. If you application fails to link (without -ljack), please report
at https://github.com/x42/weakjack/issues
License
-------
GNU General Public License version 2 (or later).
Alternatives
------------
An alternative, more liberally licensed, implementation that abstracts and wraps jack completely
(incl headers) can be found at
https://github.com/falkTX/Carla/tree/master/source/jackbridge (C++ only),
and a jack2 specific version at https://github.com/sletz/jack2/blob/master/common/JackWeakAPI.c
A variant for python bindings is also provided by falkTX:
https://github.com/falkTX/Cadence/blob/master/src/jacklib.py

291
extern/weakjack/weak_libjack.c vendored Normal file
View file

@ -0,0 +1,291 @@
/* runtime/weak dynamic JACK linking
*
* (C) 2014 Robin Gareus <robin@gareus.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "weak_libjack.h"
#define NDEBUG
#ifndef USE_WEAK_JACK
int have_libjack (void) {
return 0;
}
#else
#include <stdio.h>
#include <string.h>
#include <assert.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
static void* lib_open(const char* const so) {
#ifdef _WIN32
return (void*) LoadLibraryA(so);
#else
return dlopen(so, RTLD_NOW|RTLD_LOCAL);
#endif
}
static void* lib_symbol(void* const lib, const char* const sym) {
#ifdef _WIN32
return (void*) GetProcAddress((HMODULE)lib, sym);
#else
return dlsym(lib, sym);
#endif
}
#if defined _MSC_VER && ! defined __INTEL_COMPILER
typedef void * pvoid_t;
#define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \
if (!_j._ ## SYM) err |= FAIL;
#elif defined NDEBUG
typedef void * __attribute__ ((__may_alias__)) pvoid_t;
#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
if (!_j._ ## SYM) err |= FAIL;
#else
typedef void * __attribute__ ((__may_alias__)) pvoid_t;
#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
if (!_j._ ## SYM) { \
if (FAIL) { \
fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \
} \
err |= FAIL; \
}
#endif
typedef void (* func_t) (void);
/* function pointers to the real jack API */
static struct WeakJack {
func_t _client_open; // special case due to varargs
#define JCFUN(ERR, RTYPE, NAME, RVAL) func_t _ ## NAME ;
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) func_t _ ## NAME ;
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) func_t _ ## NAME ;
#define JVFUN(ERR, NAME, DEF, ARGS, CODE) func_t _ ## NAME ;
#include "weak_libjack.def"
#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN
} _j;
static int _status = -1;
#if !defined _MSC_VER || defined __INTEL_COMPILER
__attribute__((constructor))
#endif
static void init_weak_jack(void)
{
void* lib;
int err = 0;
#ifndef NDEBUG
fprintf(stderr, "*** WEAK-JACK: initializing\n");
#endif
memset(&_j, 0, sizeof(_j));
#ifdef __APPLE__
lib = lib_open("libjack.dylib");
if (!lib) {
lib = lib_open("/usr/local/lib/libjack.dylib");
}
if (!lib) {
/* New Homebrew location */
lib = lib_open("/opt/homebrew/lib/libjack.dylib");
if (lib) {
fprintf(stderr, "*** WEAK-JACK: using Homebrew\n");
}
}
if (!lib) {
/* MacPorts location */
lib = lib_open("/opt/local/lib/libjack.dylib");
if (lib) {
fprintf(stderr, "*** WEAK-JACK: using MacPorts\n");
}
}
#elif (defined _WIN32)
# if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
lib = lib_open("libjack64.dll");
# else
lib = lib_open("libjack.dll");
# endif
#else
lib = lib_open("libjack.so.0");
#endif
if (!lib) {
#ifndef NDEBUG
fprintf(stderr, "*** WEAK-JACK: libjack was not found\n");
#endif
_status = -2;
return;
}
/* found library, now lookup functions */
MAPSYM(client_open, 2)
#define JCFUN(ERR, RTYPE, NAME, RVAL) MAPSYM(NAME, ERR)
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) MAPSYM(NAME, ERR)
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR)
#define JVFUN(ERR, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR)
#include "weak_libjack.def"
#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN
/* if a required symbol is not found, disable JACK completly */
if (err) {
_j._client_open = NULL;
}
_status = err;
#ifndef NDEBUG
fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status);
#endif
}
int have_libjack (void) {
if (_status == -1) {
init_weak_jack();
}
return _status;
}
/*******************************************************************************
* helper macros
*/
#if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG)
#define likely(expr) (__builtin_expect (!!(expr), 1))
#else
#define likely(expr) (expr)
#endif
#ifndef NDEBUG
# define WJACK_WARNING(NAME) \
fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME);
#else
# define WJACK_WARNING(NAME) ;
#endif
/******************************************************************************
* JACK API wrapper functions.
*
* if a function pointer is set in the static struct WeakJack _j,
* the function is called directly.
* Otherwise a dummy NOOP implementation is provided.
* The latter is mainly for compile-time warnings.
*
* If libjack is not found, jack_client_open() will fail.
* In that case the application should not call any other libjack
* functions. Hence a real implementation is not needed.
* (jack ringbuffer may be an exception for some apps)
*/
/* dedicated support for jack_client_open(,..) variable arg function macro */
func_t WJACK_get_client_open(void) {
if (_status == -1) {
init_weak_jack();
}
return _j._client_open;
}
/* callback to set status */
jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) {
WJACK_WARNING(client_open);
if (status) { *status = JackFailure; }
return NULL;
}
/*******************************************************************************
* Macros to wrap jack API
*/
/* abstraction for jack_client functions
* rtype jack_function_name (jack_client_t *client) { return rval; }
*/
#define JCFUN(ERR, RTYPE, NAME, RVAL) \
RTYPE WJACK_ ## NAME (jack_client_t *client) { \
if likely(_j._ ## NAME) { \
return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \
} else { \
WJACK_WARNING(NAME) \
return RVAL; \
} \
}
/* abstraction for NOOP functions with return value
* rtype jack_function_name (ARGS) { return rval; }
*/
#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \
RTYPE WJACK_ ## NAME DEF { \
if likely(_j._ ## NAME) { \
return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
} else { \
WJACK_WARNING(NAME) \
return RVAL; \
} \
}
/* abstraction for functions that need custom code.
* e.g. functions with return-value-pointer args,
* use CODE to initialize value
*
* rtype jack_function_name (ARGS) { CODE }
*/
#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \
RTYPE WJACK_ ## NAME DEF { \
if likely(_j._ ## NAME) { \
return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
} else { \
WJACK_WARNING(NAME) \
CODE \
} \
}
/* abstraction for void functions with return-value-pointer args
* void jack_function_name (ARGS) { CODE }
*/
#define JVFUN(ERR, NAME, DEF, ARGS, CODE) \
void WJACK_ ## NAME DEF { \
if likely(_j._ ## NAME) { \
((void (*)DEF) _j._ ## NAME) ARGS; \
} else { \
WJACK_WARNING(NAME) \
CODE \
} \
}
#include "weak_libjack.def"
#undef JCFUN
#undef JPFUN
#undef JXFUN
#undef JVFUN
#endif // end USE_WEAK_JACK

179
extern/weakjack/weak_libjack.def vendored Normal file
View file

@ -0,0 +1,179 @@
/* macro-absraction of the JACK API
*
* see weak_libjack.c for details, in general arguments are:
*
* [required], [return type], [name], [arguments], [code or return value]
*
* This file is included multiple times with different macro definitions
* do not add header guards.
* see https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros
*/
#ifdef USE_WEAK_JACK
/* <jack/jack.h> */
JCFUN(1, int, client_close, 0)
JCFUN(1, char*, get_client_name, NULL)
JVFUN(0, on_shutdown, (jack_client_t *c, JackShutdownCallback s, void *a), (c,s,a),)
JVFUN(0, on_info_shutdown, (jack_client_t *c, JackInfoShutdownCallback s, void *a), (c,s,a),)
JPFUN(1, int, set_process_callback, (jack_client_t *c, JackProcessCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_freewheel_callback, (jack_client_t *c, JackFreewheelCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_buffer_size_callback, (jack_client_t *c, JackBufferSizeCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_sample_rate_callback, (jack_client_t *c, JackSampleRateCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_port_registration_callback, (jack_client_t *c, JackPortRegistrationCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_port_connect_callback, (jack_client_t *c, JackPortConnectCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_graph_order_callback, (jack_client_t *c, JackGraphOrderCallback g, void *a), (c,g,a), -1)
JPFUN(1, int, set_xrun_callback, (jack_client_t *c, JackXRunCallback g, void *a), (c,g,a), -1)
JPFUN(1, int, set_latency_callback, (jack_client_t *c, JackLatencyCallback g, void *a), (c,g,a), -1)
JVFUN(1, set_error_function, (void (*f)(const char *)), (f),)
JVFUN(1, set_info_function, (void (*f)(const char *)), (f),)
JCFUN(1, int, activate, -1)
JCFUN(1, int, deactivate, -1)
JPFUN(1, int, client_name_size, (), (), 32)
JCFUN(1, jack_nframes_t, get_sample_rate, 0)
JCFUN(1, jack_nframes_t, get_buffer_size, 0)
JPFUN(1, jack_nframes_t, frames_since_cycle_start, (const jack_client_t *c), (c), 0)
JPFUN(1, jack_nframes_t, frame_time, (const jack_client_t *c), (c), 0)
JPFUN(1, jack_nframes_t, last_frame_time, (const jack_client_t *c), (c), 0)
JPFUN(1, jack_time_t, get_time, (void), (), 0)
JCFUN(1, float, cpu_load, 0)
JCFUN(1, int, is_realtime, 0)
JPFUN(1, int, set_freewheel, (jack_client_t *c, int o), (c,o), 0)
JPFUN(1, int, set_buffer_size, (jack_client_t *c, jack_nframes_t b), (c,b), 0)
JCFUN(0, int, recompute_total_latencies, 0)
JPFUN(0, jack_nframes_t, port_get_total_latency, (jack_client_t *c, jack_port_t *p), (c,p), 0)
JVFUN(0, port_get_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r), if (r) {r->min = r->max = 0;})
JVFUN(0, port_set_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r),)
JPFUN(1, void*, port_get_buffer, (jack_port_t *p, jack_nframes_t n), (p,n), NULL)
JPFUN(1, int, port_request_monitor, (jack_port_t *p, int o), (p,o), 0)
JPFUN(1, int, port_ensure_monitor, (jack_port_t *p, int o), (p,o), 0)
JPFUN(1, int, port_monitoring_input, (jack_port_t *p), (p), 0)
JPFUN(1, const char*, port_name, (const jack_port_t *p), (p), NULL)
JPFUN(1, const char*, port_short_name, (const jack_port_t *p), (p), NULL)
JPFUN(1, int, port_flags, (const jack_port_t *p), (p), 0)
JPFUN(1, int, port_is_mine, (const jack_client_t *c, const jack_port_t *p), (c,p), 0)
JPFUN(1, int, port_connected, (const jack_port_t *p), (p), 0)
JPFUN(1, const char**, get_ports,(jack_client_t *c, const char *p, const char *t, unsigned long f), (c,p,t,f), NULL)
JPFUN(1, int, port_name_size, (void), (), 0)
JPFUN(1, int, port_type_size, (void), (), 0)
JPFUN(1, size_t, port_type_get_buffer_size, (jack_client_t *c, const char *t), (c,t), 0)
JPFUN(1, jack_port_t*, port_by_name, (jack_client_t *c, const char *n), (c,n), NULL)
JPFUN(1, jack_port_t*, port_by_id, (jack_client_t *c, jack_port_id_t i), (c,i), NULL)
JPFUN(1, jack_port_t*, port_register, (jack_client_t *c, const char *n, const char *t, unsigned long f, unsigned long b), (c,n,t,f,b), NULL)
JPFUN(1, int, port_unregister, (jack_client_t *c, jack_port_t *p), (c,p), 0)
JPFUN(1, const char *, port_type, (const jack_port_t *p), (p), 0)
JPFUN(1, const char **, port_get_connections, (const jack_port_t *p), (p), 0)
JPFUN(1, const char **, port_get_all_connections, (const jack_client_t *c, const jack_port_t *p), (c,p), 0)
JPFUN(1, int, port_set_name, (jack_port_t *p, const char *n), (p,n), -1)
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
JXFUN(0, int, port_rename, (jack_client_t *c, jack_port_t *p, const char *n), (c,p,n), return jack_port_set_name (p,n);)
#if defined(__clang__)
#pragma clang diagnostic pop
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic pop
#endif
JPFUN(1, int, port_get_aliases, (const jack_port_t *port, char* const aliases[2]), (port,aliases), 0)
JPFUN(1, int, port_disconnect, (jack_client_t *c, jack_port_t *p), (c,p), 0)
JPFUN(1, int, connect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1)
JPFUN(1, int, disconnect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1)
JVFUN(0, free, (void *p), (p), free(p);)
JCFUN(1, jack_nframes_t, cycle_wait, 0)
JVFUN(1, cycle_signal, (jack_client_t *c, int s), (c,s),)
JPFUN(1, int, set_process_thread, (jack_client_t *c, JackThreadCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_thread_init_callback, (jack_client_t *c, JackThreadInitCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, transport_locate, (jack_client_t *c, jack_nframes_t f), (c,f), 0)
JVFUN(1, transport_start, (jack_client_t *c), (c),)
JVFUN(1, transport_stop, (jack_client_t *c), (c),)
JPFUN(1, jack_nframes_t, get_current_transport_frame, (const jack_client_t *c), (c), 0)
JXFUN(1, jack_transport_state_t, transport_query, (const jack_client_t *c, jack_position_t *p), (c,p), memset(p, 0, sizeof(jack_position_t)); return JackTransportStopped;)
JPFUN(1, int, set_sync_callback, (jack_client_t *c, JackSyncCallback p, void *a), (c,p,a), -1)
JPFUN(1, int, set_timebase_callback, (jack_client_t *c, int l, JackTimebaseCallback p, void *a), (c,l,p,a), -1)
JPFUN(1, int, set_sync_timeout, (jack_client_t *c, jack_time_t t), (c,t), -1)
JCFUN(1, int, release_timebase, 0)
/* <jack/midiport.h> */
JPFUN(1, uint32_t, midi_get_event_count, (void* p), (p), 0)
JPFUN(1, int, midi_event_get, (jack_midi_event_t *e, void *p, uint32_t i), (e,p,i), -1)
JPFUN(1, int, midi_event_write, (void *b, jack_nframes_t t, const jack_midi_data_t *d, size_t s), (b,t,d,s), -1)
JVFUN(1, midi_clear_buffer, (void *b), (b),)
/* <jack/session.h> */
JPFUN(0, int, set_session_callback, (jack_client_t *c, JackSessionCallback s, void *a), (c,s,a), -1)
JPFUN(0, int, session_reply, (jack_client_t *c, jack_session_event_t *e), (c,e), -1)
JVFUN(0, session_event_free, (jack_session_event_t *e), (e), )
/* <jack/ringbuffer.h> */
JPFUN(1, jack_ringbuffer_t *, ringbuffer_create, (size_t s), (s), NULL)
JVFUN(1, ringbuffer_free, (jack_ringbuffer_t *rb), (rb), )
JVFUN(1, ringbuffer_reset, (jack_ringbuffer_t *rb), (rb), )
JVFUN(1, ringbuffer_read_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), )
JVFUN(1, ringbuffer_write_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), )
JPFUN(1, size_t, ringbuffer_read_space, (const jack_ringbuffer_t *rb), (rb), 0)
JPFUN(1, size_t, ringbuffer_write_space, (const jack_ringbuffer_t *rb), (rb), 0)
JPFUN(1, size_t, ringbuffer_read, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0)
JPFUN(1, size_t, ringbuffer_write, (jack_ringbuffer_t *rb, const char *s, size_t c), (rb,s,c), 0)
JPFUN(0, int, ringbuffer_mlock, (jack_ringbuffer_t *rb), (rb), 0)
JVFUN(0, ringbuffer_get_read_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} )
JVFUN(0, ringbuffer_get_write_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} )
JPFUN(0, size_t, ringbuffer_peek, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0)
/* <jack/thread.h> */
JCFUN(0, int, client_real_time_priority, 0)
JCFUN(0, int, client_max_real_time_priority, 0)
JPFUN(0, int, acquire_real_time_scheduling, (jack_native_thread_t t, int p), (t,p), 0)
JPFUN(0, int, drop_real_time_scheduling, (jack_native_thread_t t), (t), 0)
#if (!defined _WIN32 && (defined _REENTRANT || defined __linux__))
/* on POSIX systems, call pthread_join() when libjack does not provide jack_client_stop_thread */
JXFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), if (t) { pthread_join(t, NULL); return 0; } else { return -1;})
#else
JPFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0)
#endif
JPFUN(0, int, client_kill_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0)
#ifndef _WIN32
JVFUN(0, set_thread_creator, (jack_thread_creator_t c), (c),)
#endif
JPFUN(1, int, client_create_thread, \
(jack_client_t* c, jack_native_thread_t *t, int p, int r, void *(*f)(void*), void *a), (c,t,p,r,f,a), 0)
#ifndef NO_JACK_METADATA
/* <jack/uuid.h> - TODO*/
/* <jack/jack.h> */
JPFUN(0, char *, get_uuid_for_client_name, (jack_client_t* c, const char* n), (c,n), NULL)
JPFUN(0, char *, get_client_name_by_uuid, (jack_client_t* c, const char* u), (c,u), NULL)
JPFUN(0, jack_uuid_t, port_uuid, (const jack_port_t *p), (p), 0)
/* <jack/metadata.h> */
JPFUN(0, int, set_property, (jack_client_t* c, jack_uuid_t s, const char* k, const char* v, const char* t), (c,s,k,v,t), -1)
JXFUN(0, int, get_property, (jack_uuid_t s, const char* k, char** v, char** t), (s,k,v,t), if (v) *v=NULL; if (t) *t=NULL; return -1;)
JVFUN(0, free_description, (jack_description_t* d, int f), (d,f),)
JXFUN(0, int, get_properties, (jack_uuid_t s, jack_description_t* d), (s,d), if (d) {d->properties = NULL; d->property_cnt = 0;} return -1;)
JXFUN(0, int, get_all_properties, (jack_description_t** d), (d), if (d) *d=NULL; return -1;)
JPFUN(0, int, remove_property, (jack_client_t* c, jack_uuid_t s, const char* k), (c,s,k), -1)
JPFUN(0, int, remove_properties, (jack_client_t* c, jack_uuid_t s), (c,s), -1)
JPFUN(0, int, remove_all_properties, (jack_client_t* c), (c), -1)
JPFUN(0, int, set_property_change_callback, (jack_client_t *c, JackPropertyChangeCallback s, void *a), (c,s,a), -1)
#endif
/* <jack/statistics.h> */
JCFUN(1, float, get_max_delayed_usecs, 0.0)
JCFUN(1, float, get_xrun_delayed_usecs, 0.0)
JVFUN(0, reset_max_delayed_usecs, (jack_client_t *c), (c),)
#endif // end USE_WEAK_JACK

230
extern/weakjack/weak_libjack.h vendored Normal file
View file

@ -0,0 +1,230 @@
/* runtime/weak dynamic JACK linking
*
* (C) 2014 Robin Gareus <robin@gareus.org>
*
* The wrapped jack API itself is
* (C) 2001 Paul Davis
* (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _WEAK_JACK_H
#define _WEAK_JACK_H
#ifdef __cplusplus
extern "C"
{
#endif
/** check if libjack is available
*
* return 0 if libjack is dynamically linked of was
* successfully dl-opened. Otherwise:
*
* -1: library was not initialized
* -2: libjack was not found
* > 0 bitwise flags:
* 1: a required function was not found in libjack
* 2: jack_client_open was not found in libjack
*/
int have_libjack(void);
#ifdef __cplusplus
}
#endif
#ifdef USE_WEAK_JACK
/* <jack/jack.h> */
#define jack_client_close WJACK_client_close
#define jack_get_client_name WJACK_get_client_name
#define jack_get_sample_rate WJACK_get_sample_rate
#define jack_get_buffer_size WJACK_get_buffer_size
#define jack_frames_since_cycle_start WJACK_frames_since_cycle_start
#define jack_frame_time WJACK_frame_time
#define jack_last_frame_time WJACK_last_frame_time
#define jack_get_time WJACK_get_time
#define jack_cpu_load WJACK_cpu_load
#define jack_is_realtime WJACK_is_realtime
#define jack_client_name_size WJACK_client_name_size
#define jack_set_freewheel WJACK_set_freewheel
#define jack_set_buffer_size WJACK_set_buffer_size
#define jack_on_shutdown WJACK_on_shutdown
#define jack_on_info_shutdown WJACK_on_info_shutdown
#define jack_set_process_callback WJACK_set_process_callback
#define jack_set_freewheel_callback WJACK_set_freewheel_callback
#define jack_set_buffer_size_callback WJACK_set_buffer_size_callback
#define jack_set_sample_rate_callback WJACK_set_sample_rate_callback
#define jack_set_port_registration_callback WJACK_set_port_registration_callback
#define jack_set_port_connect_callback WJACK_set_port_connect_callback
#define jack_set_graph_order_callback WJACK_set_graph_order_callback
#define jack_set_xrun_callback WJACK_set_xrun_callback
#define jack_set_latency_callback WJACK_set_latency_callback
#define jack_set_error_function WJACK_set_error_function
#define jack_set_info_function WJACK_set_info_function
#define jack_activate WJACK_activate
#define jack_deactivate WJACK_deactivate
#define jack_recompute_total_latencies WJACK_recompute_total_latencies
#define jack_port_get_total_latency WJACK_port_get_total_latency
#define jack_port_get_latency_range WJACK_port_get_latency_range
#define jack_port_set_latency_range WJACK_port_set_latency_range
#define jack_port_get_buffer WJACK_port_get_buffer
#define jack_port_request_monitor WJACK_port_request_monitor
#define jack_port_ensure_monitor WJACK_port_ensure_monitor
#define jack_port_monitoring_input WJACK_port_monitoring_input
#define jack_port_name WJACK_port_name
#define jack_port_short_name WJACK_port_short_name
#define jack_port_flags WJACK_port_flags
#define jack_port_is_mine WJACK_port_is_mine
#define jack_port_connected WJACK_port_connected
#define jack_get_ports WJACK_get_ports
#define jack_port_name_size WJACK_port_name_size
#define jack_port_type_size WJACK_port_type_size
#define jack_port_type_get_buffer_size WJACK_port_type_get_buffer_size
#define jack_port_by_name WJACK_port_by_name
#define jack_port_by_id WJACK_port_by_id
#define jack_port_set_name WJACK_port_set_name
#define jack_port_get_aliases WJACK_port_get_aliases
#define jack_port_rename WJACK_port_rename
#define jack_port_disconnect WJACK_port_disconnect
#define jack_port_register WJACK_port_register
#define jack_port_unregister WJACK_port_unregister
#define jack_port_type WJACK_port_type
#define jack_port_get_connections WJACK_port_get_connections
#define jack_port_get_all_connections WJACK_port_get_all_connections
#define jack_connect WJACK_connect
#define jack_disconnect WJACK_disconnect
#define jack_free WJACK_free
#define jack_cycle_wait WJACK_cycle_wait
#define jack_cycle_signal WJACK_cycle_signal
#define jack_set_process_thread WJACK_set_process_thread
#define jack_set_thread_init_callback WJACK_set_thread_init_callback
/* <jack/transport.h> */
#define jack_get_current_transport_frame WJACK_get_current_transport_frame
#define jack_transport_locate WJACK_transport_locate
#define jack_transport_start WJACK_transport_start
#define jack_transport_stop WJACK_transport_stop
#define jack_transport_query WJACK_transport_query
#define jack_set_sync_callback WJACK_set_sync_callback
#define jack_set_timebase_callback WJACK_set_timebase_callback
#define jack_release_timebase WJACK_release_timebase
/* <jack/midiport.h> */
#define jack_midi_get_event_count WJACK_midi_get_event_count
#define jack_midi_event_get WJACK_midi_event_get
#define jack_midi_event_write WJACK_midi_event_write
#define jack_midi_clear_buffer WJACK_midi_clear_buffer
/* <jack/session.h> */
#define jack_set_session_callback WJACK_set_session_callback
#define jack_session_reply WJACK_session_reply
#define jack_session_event_free WJACK_session_event_free
/* <jack/ringbuffer.h> */
#define jack_ringbuffer_create WJACK_ringbuffer_create
#define jack_ringbuffer_free WJACK_ringbuffer_free
#define jack_ringbuffer_reset WJACK_ringbuffer_reset
#define jack_ringbuffer_read_advance WJACK_ringbuffer_read_advance
#define jack_ringbuffer_write_advance WJACK_ringbuffer_write_advance
#define jack_ringbuffer_read_space WJACK_ringbuffer_read_space
#define jack_ringbuffer_write_space WJACK_ringbuffer_write_space
#define jack_ringbuffer_read WJACK_ringbuffer_read
#define jack_ringbuffer_write WJACK_ringbuffer_write
#define jack_ringbuffer_mlock WJACK_ringbuffer_mlock
#define jack_ringbuffer_get_read_vector WJACK_ringbuffer_get_read_vector
#define jack_ringbuffer_get_write_vector WJACK_ringbuffer_get_write_vector
#define jack_ringbuffer_peek WJACK_ringbuffer_peek
/* <jack/thread.h> */
#define jack_client_real_time_priority WJACK_client_real_time_priority
#define jack_client_max_real_time_priority WJACK_client_max_real_time_priority
#define jack_acquire_real_time_scheduling WJACK_acquire_real_time_scheduling
#define jack_client_create_thread WJACK_client_create_thread
#define jack_drop_real_time_scheduling WJACK_drop_real_time_scheduling
#define jack_client_stop_thread WJACK_client_stop_thread
#define jack_client_kill_thread WJACK_client_kill_thread
#define jack_set_thread_creator WJACK_set_thread_creator
#define jack_client_open WJACK_client_client_openXXX
#ifndef NO_JACK_METADATA
/* <jack/metadata.h> */
#define jack_get_uuid_for_client_name WJACK_get_uuid_for_client_name
#define jack_get_client_name_by_uuid WJACK_get_client_name_by_uuid
#define jack_port_uuid WJACK_port_uuid
#define jack_set_property WJACK_set_property
#define jack_get_property WJACK_get_property
#define jack_free_description WJACK_free_description
#define jack_get_properties WJACK_get_properties
#define jack_get_all_properties WJACK_get_all_properties
#define jack_remove_property WJACK_remove_property
#define jack_remove_properties WJACK_remove_properties
#define jack_remove_all_properties WJACK_remove_all_properties
#define jack_set_property_change_callback WJACK_set_property_change_callback
#endif
/* <jack/statistics.h> */
#define jack_get_max_delayed_usecs WJACK_get_max_delayed_usecs
#define jack_get_xrun_delayed_usecs WJACK_get_xrun_delayed_usecs
#define jack_reset_max_delayed_usecs WJACK_reset_max_delayed_usecs
#endif // end USE_WEAK_JACK
#include <jack/jack.h>
#include <jack/transport.h>
#include <jack/ringbuffer.h>
#include <jack/midiport.h>
#include <jack/session.h>
#include <jack/thread.h>
#include <jack/statistics.h>
#ifndef NO_JACK_METADATA
#include <jack/metadata.h>
#endif
#ifdef USE_WEAK_JACK
#undef jack_client_open
/* var-args hack */
#ifdef __cplusplus
extern "C" {
#endif
void (* WJACK_get_client_open (void)) (void);
jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...);
#ifdef __cplusplus
}
#endif
#define jack_client_open(...) \
( \
(WJACK_get_client_open() != NULL) \
? ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))(WJACK_get_client_open()))(__VA_ARGS__) \
: WJACK_no_client_open(__VA_ARGS__) \
)
#endif // end USE_WEAK_JACK
#endif // _WEAK_JACK_H

View file

@ -6,7 +6,7 @@ when copying pattern data from Furnace, it's stored in the clipboard as plain te
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.6pre14 is `175`.
this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6pre16 is `178`.
the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from.
- `0`: note.

View file

@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
- 178: Furnace 0.6pre16
- 177: Furnace 0.6pre15
- 175: Furnace 0.6pre14
- 174: Furnace 0.6pre13
- 173: Furnace 0.6pre12

View file

@ -15,17 +15,17 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>0.6pre14</string>
<string>0.6pre16</string>
<key>CFBundleName</key>
<string>Furnace</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.6pre14</string>
<string>0.6pre16</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.6pre14</string>
<string>0.6pre16</string>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>NSHighResolutionCapable</key>

View file

@ -73,16 +73,41 @@ if __name__ == "__main__":
for file_ in filter(lambda x: x.lower().endswith('.md'), files):
file_list.append(os.path.join(base_dir, file_))
#-- then, create the index --#
index = '<h2>contents</h2><ol>'
#-- then, create the document --#
html = ''
# perform sort
file_list.sort(key=sort_func)
first = True
for my_file in file_list:
pageLink = my_file.replace(os.path.sep, "__")
if pageLink.endswith("__doc__README.md"):
continue
with open(my_file, 'r') as md:
LOGGER.info("processing file %s" % my_file)
data = md.read()
# retrieve title
pageTitle = data.partition('\n')[0].replace("# ","")
if pageLink.endswith("__README.md"):
if first:
first = False
else:
index += '</li></ol>'
index += '<li><a href="#%s" class="indexItemPre">%s</a><a href="#%s" class="indexItem"></a>' % ( pageLink, pageTitle, pageLink )
if pageLink.endswith("__README.md"):
index += '<ol>'
else:
index += '</li>'
# perform link fixing
data = re.sub(r'\[(.+?)\]\((.+?)\)', fix_links, data)
@ -94,6 +119,9 @@ if __name__ == "__main__":
markdown.markdown(data, extensions=['nl2br', 'mdx_breakless_lists', GithubFlavoredMarkdownExtension()])
)
# finish index
index += '</ol></li></ol>'
# build html
final_html = ('''
<!DOCTYPE html>
@ -136,7 +164,7 @@ if __name__ == "__main__":
margin-right: 4pt;
list-style-type: none;
}
li:before {
ul > li:before {
content: '-';
padding-right: 3pt;
}
@ -193,6 +221,24 @@ if __name__ == "__main__":
font-weight: normal;
color: #555;
}
a.indexItemPre {
color: #000;
text-decoration: none;
letter-spacing: .01em;
}
a.indexItemPre[href^='#']:after {
content: ' ' leader('.') ' ';
font-size: 1em;
}
a.indexItem {
float: right;
overflow: hidden;
}
a.indexItem[href^='#']:after {
content: target-counter(attr(href),page);
color: #000;
font-size: 11pt;
}
#cover {
height: 100%%;
text-align: center;
@ -282,11 +328,35 @@ if __name__ == "__main__":
<i>for version 0.6</i>
</div>
</section>
<section id="authors">
<div>
</div>
<div>
<h3>authors</h3>
<ul>
<li>cam900</li>
<li>DeMOSic</li>
<li>Electric Keet</li>
<li>freq-mod</li>
<li>host12prog</li>
<li>nicco1690</li>
<li>tildearrow</li>
</ul>
<p>special thanks to ZoomTen for providing tools which assisted in the production of this document!</p>
<p>copyright © 2023 tildearrow and other authors.</p>
<p>this documentation is under the <a href="https://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported</a> license.</p>
<p>you may reproduce, modify and/or distribute this documentation provided this copyright notice (including license and attribution) is present and any necessary disclaimers whether modifications have been made.</p>
<p>this documentation is provided as-is and without warranty of any kind.</p>
<p>this manual is written for version 0.6 of Furnace.<br/>it may not necessarily apply to previous or future versions.</p>
</section>
<section id="index">
%s
</section>
%s
</body>
</html>
''' % (
html
index, html
)
)

View file

@ -4,13 +4,14 @@
<name>Furnace</name>
<summary>Open-source chiptune tracker</summary>
<url type="homepage">https://github.com/tildearrow/furnace</url>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0-or-later</project_license>
<description>
<p>
the biggest chiptune tracker ever made!
Furnace - the biggest chiptune tracker ever made!
</p>
<p>
it allows you to create songs using a music tracker interface for several computer/game console/arcade sound chips.
@ -19,7 +20,7 @@
it also offers DefleMask compatibility, allowing you to import your songs and even export them back for interoperability.
</p>
<p>
<b>rationale for intense profanity:</b> the tracker itself is clean, but a few demo songs and instruments contain a small amount of strong language.
rationale for intense profanity: the tracker itself is clean, but a few demo songs and instruments contain a small amount of strong language.
</p>
</description>
@ -34,4 +35,4 @@
<image>https://tildearrow.org/storage/images/furnace.png</image>
</screenshot>
</screenshots>
</component>

34
res/make-appdata.sh Executable file
View file

@ -0,0 +1,34 @@
#!/bin/sh
if [ $# -lt 2 ]; then
echo "usage: $0 input output"
exit 1
fi
#echo "generating $2..."
cat "$1" > "$2"
echo " <releases>" >> "$2"
for i in `git log --tags='v*' --no-walk --format="%as/%(describe:tags)"`; do
releaseDate=${i%/*}
releaseVer=${i#*/}
releaseVerProper=$releaseVer
case $releaseVer in
*pre*)
releaseType=development
releaseVerProper="${releaseVer%pre*}~pre${releaseVer#*pre}"
;;
*) releaseType=stable;;
esac
echo " <release version=\"${releaseVerProper}\" date=\"$releaseDate\" type=\"$releaseType\">" >> "$2"
echo " <url>https://github.com/tildearrow/furnace/releases/tag/$releaseVer</url>" >> "$2"
echo " </release>" >> "$2"
done
echo " </releases>" >> "$2"
echo "</component>" >> "$2"
#echo "done."

View file

@ -30,8 +30,8 @@ cp -v ../../../res/logo.png furnace.png || exit 1
ln -s furnace.png .DirIcon || exit 1
cp -v ../../../res/furnace.desktop . || exit 1
#mkdir -p usr/share/metainfo || exit 1
cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
rm usr/share/metainfo/furnace.appdata.xml || exit 1
#cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
#rm usr/share/metainfo/furnace.appdata.xml || exit 1
cp -v ../../../res/AppRun . || exit 1
#cp /usr/lib/libm.so.6 usr/lib/ || exit 1

View file

@ -198,10 +198,19 @@ String TAAudioJACK::printStatus(jack_status_t status) {
bool TAAudioJACK::init(TAAudioDesc& request, TAAudioDesc& response) {
if (initialized) return false;
if (jack_client_open==NULL) {
int haveJACK=have_libjack();
if (haveJACK==-1) {
logE("JACK library not initialized!");
return false;
}
if (haveJACK==-2) {
logE("JACK not installed!");
return false;
}
if (haveJACK!=0) {
logE("JACK symbol error!");
return false;
}
desc=request;
desc.outFormat=TA_AUDIO_FORMAT_F32;

View file

@ -18,8 +18,7 @@
*/
#include "taAudio.h"
#include <jack/weakjack.h>
#include <jack/jack.h>
#include "../../extern/weakjack/weak_libjack.h"
class TAAudioJACK: public TAAudio {
jack_client_t* ac;

View file

@ -21,7 +21,7 @@
#define _TAAUDIO_H
#include "../ta-utils.h"
#include <memory>
#include <queue>
#include "../fixedQueue.h"
#include <vector>
struct SampleRateChangeEvent {
@ -124,7 +124,7 @@ struct TAMidiMessage {
class TAMidiIn {
public:
std::queue<TAMidiMessage> queue;
FixedQueue<TAMidiMessage,8192> queue;
virtual bool gather();
bool next(TAMidiMessage& where);
virtual bool isDeviceOpen();
@ -139,7 +139,7 @@ class TAMidiIn {
};
class TAMidiOut {
std::queue<TAMidiMessage> queue;
FixedQueue<TAMidiMessage,8192> queue;
public:
virtual bool send(const TAMidiMessage& what);
virtual bool isDeviceOpen();

View file

@ -61,9 +61,11 @@ void DivEngine::initConfDir() {
return;
}
#else
// TODO this should check XDG_CONFIG_HOME first
char* xdgConfigHome=getenv("XDG_CONFIG_HOME");
char* home=getenv("HOME");
if (home==NULL) {
if (xdgConfigHome) {
configPath=xdgConfigHome;
} else if (home==NULL) {
int uid=getuid();
struct passwd* entry=getpwuid(uid);
if (entry==NULL) {
@ -79,8 +81,9 @@ void DivEngine::initConfDir() {
#ifdef __APPLE__
configPath+="/Library/Application Support";
#else
// FIXME this doesn't honour XDG_CONFIG_HOME *at all*
configPath+="/.config";
if (xdgConfigHome==NULL) {
configPath+="/.config";
}
#endif // __APPLE__
#endif // __HAIKU__
#ifdef __APPLE__

View file

@ -2166,6 +2166,13 @@ int DivEngine::getRow() {
return prevRow;
}
void DivEngine::getPlayPos(int& order, int& row) {
playPosLock.lock();
order=prevOrder;
row=prevRow;
playPosLock.unlock();
}
int DivEngine::getElapsedBars() {
return elapsedBars;
}

View file

@ -37,7 +37,7 @@
#include <mutex>
#include <map>
#include <unordered_map>
#include <deque>
#include "../fixedQueue.h"
class DivWorkPool;
@ -58,8 +58,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "0.6pre14"
#define DIV_ENGINE_VERSION 175
#define DIV_VERSION "0.6pre16"
#define DIV_ENGINE_VERSION 178
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -176,14 +176,28 @@ struct DivChannelState {
};
struct DivNoteEvent {
int channel, ins, note, volume;
bool on;
signed char channel;
unsigned char ins;
signed char note, volume;
bool on, nop, pad1, pad2;
DivNoteEvent(int c, int i, int n, int v, bool o):
channel(c),
ins(i),
note(n),
volume(v),
on(o) {}
on(o),
nop(false),
pad1(false),
pad2(false) {}
DivNoteEvent():
channel(-1),
ins(0),
note(0),
volume(0),
on(false),
nop(true),
pad1(false),
pad2(false) {}
};
struct DivDispatchContainer {
@ -428,11 +442,11 @@ class DivEngine {
DivAudioExportModes exportMode;
double exportFadeOut;
DivConfig conf;
std::deque<DivNoteEvent> pendingNotes;
FixedQueue<DivNoteEvent,8192> pendingNotes;
// bitfield
unsigned char walked[8192];
bool isMuted[DIV_MAX_CHANS];
std::mutex isBusy, saveLock;
std::mutex isBusy, saveLock, playPosLock;
String configPath;
String configFile;
String lastError;
@ -834,6 +848,9 @@ class DivEngine {
// get current row
int getRow();
// synchronous get order/row
void getPlayPos(int& order, int& row);
// get beat/bar
int getElapsedBars();
int getElapsedBeats();
@ -886,7 +903,7 @@ class DivEngine {
// get instrument from file
// if the returned vector is empty then there was an error.
std::vector<DivInstrument*> instrumentFromFile(const char* path, bool loadAssets=true);
std::vector<DivInstrument*> instrumentFromFile(const char* path, bool loadAssets=true, bool readInsName=true);
// load temporary instrument
void loadTempIns(DivInstrument* which);

View file

@ -1058,6 +1058,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.systemFlags[0].set("keyPriority",false);
}
// OPM broken pitch
if (ds.system[0]==DIV_SYSTEM_YM2151) {
ds.systemFlags[0].set("brokenPitch",true);
}
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
if (active) quitDispatch();
@ -2976,6 +2981,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// OPM/OPZ slide compat
if (ds.version<176) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_YM2151 ||
ds.system[i]==DIV_SYSTEM_OPZ) {
ds.systemFlags[i].set("brokenPitch",true);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();

View file

@ -1816,7 +1816,7 @@ void DivEngine::loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, S
}
}
std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path, bool loadAssets) {
std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path, bool loadAssets, bool readInsName) {
std::vector<DivInstrument*> ret;
warnings="";
@ -1921,12 +1921,17 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path, bool
reader.seek(dataPtr,SEEK_SET);
}
ins->name=stripPath;
if (ins->readInsData(reader,version,loadAssets?(&song):NULL)!=DIV_DATA_SUCCESS) {
lastError="invalid instrument header/data!";
delete ins;
delete[] buf;
return ret;
} else {
if (!readInsName) {
ins->name=stripPath;
}
ret.push_back(ins);
}
} catch (EndOfFileException& e) {

View file

@ -719,7 +719,7 @@ void DivInstrument::writeFeatureX1(SafeWriter* w) {
FEATURE_END;
}
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
size_t blockStartSeek=0;
size_t blockEndSeek=0;
size_t slSeek=0;
@ -1021,7 +1021,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
}
// check ins name
if (!name.empty()) {
if (!name.empty() && insName) {
featureNA=true;
}
@ -3380,7 +3380,7 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS
return readInsDataOld(reader,version);
}
bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song) {
bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song, bool writeInsName) {
SafeWriter* w=new SafeWriter();
w->init();
@ -3397,14 +3397,14 @@ bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song) {
// pointer to data
w->writeI(32);
// currently reserved (TODO; wavetable and sample here)
// reserved
w->writeS(0);
w->writeS(0);
w->writeI(0);
putInsData(w);
} else {
putInsData2(w,true,song);
putInsData2(w,true,song,writeInsName);
}
FILE* outFile=ps_fopen(path,"wb");

View file

@ -720,7 +720,7 @@ struct DivInstrument {
* save the instrument to a SafeWriter using new format.
* @param w the SafeWriter in question.
*/
void putInsData2(SafeWriter* w, bool fui=false, const DivSong* song=NULL);
void putInsData2(SafeWriter* w, bool fui=false, const DivSong* song=NULL, bool insName=true);
/**
* read instrument data in .fui format.
@ -735,9 +735,10 @@ struct DivInstrument {
* @param path file path.
* @param oldFormat whether to save in legacy Furnace ins format.
* @param song if new format, a DivSong to read wavetables and samples.
* @param writeInsName whether to write the instrument name or not. ignored if old format.
* @return whether it was successful.
*/
bool save(const char* path, bool oldFormat=false, DivSong* song=NULL);
bool save(const char* path, bool oldFormat=false, DivSong* song=NULL, bool writeInsName=true);
/**
* save this instrument to a file in .dmp format.

View file

@ -197,10 +197,10 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
chan[i].pitch2+=chan[i].std.pitch.val*(brokenPitch?2:1);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
chan[i].pitch2=chan[i].std.pitch.val*(brokenPitch?2:1);
}
chan[i].freqChanged=true;
}
@ -354,18 +354,18 @@ void DivPlatformArcade::tick(bool sysTick) {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
chan[i].freq=chan[i].baseFreq+chan[i].pitch-128+chan[i].pitch2;
if (!parent->song.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+chan[i].pitch2;
} else {
chan[i].freq+=chan[i].arpOff<<6;
chan[i].freq+=chan[i].arpOff<<7;
}
}
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1;
immWrite(i+0x28,hScale(chan[i].freq>>6));
immWrite(i+0x30,chan[i].freq<<2);
if (chan[i].freq>=(95<<7)) chan[i].freq=(95<<7)-1;
immWrite(i+0x28,hScale(chan[i].freq>>7));
immWrite(i+0x30,((chan[i].freq<<1)&0xfc));
hardResetElapsed+=2;
chan[i].freqChanged=false;
}
@ -534,13 +534,13 @@ int DivPlatformArcade::dispatch(DivCommand c) {
int newFreq;
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
newFreq=chan[c.chan].baseFreq+c.value*(brokenPitch?2:1);
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
newFreq=chan[c.chan].baseFreq-c.value*(brokenPitch?2:1);
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
@ -932,7 +932,9 @@ void DivPlatformArcade::setFlags(const DivConfig& flags) {
}
CHECK_CUSTOM_CLOCK;
baseFreqOff=round(768.0*(log((COLOR_NTSC/(double)chipClock))/log(2.0)));
baseFreqOff=round(1536.0*(log((COLOR_NTSC/(double)chipClock))/log(2.0)));
brokenPitch=flags.getBool("brokenPitch",false);
rate=chipClock/64;
for (int i=0; i<8; i++) {

View file

@ -20,7 +20,7 @@
#ifndef _AY_H
#define _AY_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/ay8910.h"
class DivPlatformAY8910: public DivDispatch {

View file

@ -20,7 +20,7 @@
#ifndef _AY8930_H
#define _AY8930_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/ay8910.h"
class DivPlatformAY8930: public DivDispatch {

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "sound/c140_c219.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
class DivPlatformC140: public DivDispatch {
struct Channel: public SharedChannel<int> {

View file

@ -21,7 +21,7 @@
#define _C64_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/c64/sid.h"
#include "sound/c64_fp/SID.h"
#include "sound/c64_d/dsid.h"

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../engine.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../macroInt.h"
#include "../sample.h"
#include "vgsound_emu/src/es550x/es5506.hpp"

View file

@ -22,7 +22,7 @@
#include "fmsharedbase.h"
#define NOTE_LINEAR(x) (((x)<<6)+baseFreqOff+log2(parent->song.tuning/440.0)*12.0*64.0)
#define NOTE_LINEAR(x) (((x)<<7)+baseFreqOff+log2(parent->song.tuning/440.0)*12.0*128.0)
class DivPlatformOPM: public DivPlatformFMBase {
protected:
@ -42,13 +42,15 @@ class DivPlatformOPM: public DivPlatformFMBase {
};
unsigned char lfoValue, lfoValue2, lfoShape, lfoShape2;
bool brokenPitch;
DivPlatformOPM():
DivPlatformFMBase(),
lfoValue(0),
lfoValue2(0),
lfoShape(0),
lfoShape2(0) {}
lfoShape2(0),
brokenPitch(false) {}
};
#endif

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../instrument.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#define KVS(x,y) ((chan[x].state.op[y].kvs==2 && isOutput[chan[x].state.alg][y]) || chan[x].state.op[y].kvs==1)

View file

@ -21,7 +21,7 @@
#define _GA20_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../macroInt.h"
#include "sound/ga20/iremga20.h"

View file

@ -23,7 +23,7 @@
#include "../dispatch.h"
#include "../waveSynth.h"
#include "sound/gb/gb.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
class DivPlatformGB: public DivDispatch {
struct Channel: public SharedChannel<signed char> {

View file

@ -21,7 +21,7 @@
#define _K007232_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../macroInt.h"
#include "vgsound_emu/src/k007232/k007232.hpp"

View file

@ -21,7 +21,7 @@
#define _MSM5232_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/oki/msm5232.h"
class DivPlatformMSM5232: public DivDispatch {

View file

@ -21,7 +21,7 @@
#define _MSM6258_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/oki/okim6258.h"
class DivPlatformMSM6258: public DivDispatch {

View file

@ -21,7 +21,7 @@
#define _MSM6295_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "vgsound_emu/src/msm6295/msm6295.hpp"
class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {

View file

@ -21,7 +21,7 @@
#define _N163_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
#include "vgsound_emu/src/n163/n163.hpp"

View file

@ -21,7 +21,7 @@
#define _NAMCOWSG_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/namco.h"

View file

@ -722,6 +722,10 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
}
}
if (properDrums && ch>melodicChans) {
return;
}
if (isMuted[ch]) {
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
if (ops==4) {

View file

@ -21,7 +21,7 @@
#define _OPL_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../../../extern/opl/opl3.h"
#include "sound/ymfm/ymfm_adpcm.h"

View file

@ -21,7 +21,7 @@
#define _OPLL_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
extern "C" {
#include "../../../extern/Nuked-OPLL/opll.h"

View file

@ -21,7 +21,7 @@
#define _PCE_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/pce_psg.h"

View file

@ -21,7 +21,7 @@
#define _PCSPKR_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include <thread>
#include <mutex>
#include <condition_variable>

View file

@ -21,7 +21,7 @@
#define _POKEY_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
extern "C" {
#include "sound/pokey/mzpokeysnd.h"

View file

@ -21,7 +21,7 @@
#define _SAA_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../../../extern/SAASound/src/SAASound.h"
class DivPlatformSAA1099: public DivDispatch {

View file

@ -23,7 +23,7 @@
#include "../dispatch.h"
#include "../instrument.h"
#include "sound/segapcm.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
class DivPlatformSegaPCM: public DivDispatch {
protected:

View file

@ -21,7 +21,7 @@
#define _SM8521_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/sm8521.h"

View file

@ -25,7 +25,7 @@
extern "C" {
#include "../../../extern/Nuked-PSG/ympsg.h"
}
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
class DivPlatformSMS: public DivDispatch {
struct Channel: public SharedChannel<signed char> {

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../waveSynth.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/snes/SPC_DSP.h"
class DivPlatformSNES: public DivDispatch {

View file

@ -21,7 +21,7 @@
#define _SU_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/su.h"
class DivPlatformSoundUnit: public DivDispatch {

View file

@ -23,7 +23,7 @@
#include "../dispatch.h"
#include "../waveSynth.h"
#include "sound/swan.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
class DivPlatformSwan: public DivDispatch {
struct Channel: public SharedChannel<int> {

View file

@ -21,7 +21,7 @@
#define _T6W28_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/t6w28/T6W28_Apu.h"
class DivPlatformT6W28: public DivDispatch {

View file

@ -21,7 +21,7 @@
#define _TED_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/ted-sound.h"
class DivPlatformTED: public DivDispatch {

View file

@ -147,10 +147,10 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
chan[i].pitch2+=chan[i].std.pitch.val*(brokenPitch?2:1);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
chan[i].pitch2=chan[i].std.pitch.val*(brokenPitch?2:1);
}
chan[i].freqChanged=true;
}
@ -337,18 +337,18 @@ void DivPlatformTX81Z::tick(bool sysTick) {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
chan[i].freq=chan[i].baseFreq+chan[i].pitch-128+chan[i].pitch2;
if (!parent->song.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+chan[i].pitch2;
} else {
chan[i].freq+=chan[i].arpOff<<6;
chan[i].freq+=chan[i].arpOff<<7;
}
}
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1;
immWrite(i+0x28,hScale(chan[i].freq>>6));
immWrite(i+0x30,(chan[i].freq<<2)|(chan[i].chVolL==chan[i].chVolR));
if (chan[i].freq>=(95<<7)) chan[i].freq=(95<<7)-1;
immWrite(i+0x28,hScale(chan[i].freq>>7));
immWrite(i+0x30,((chan[i].freq<<1)&0xfc)|(chan[i].chVolL==chan[i].chVolR));
hardResetElapsed+=2;
chan[i].freqChanged=false;
}
@ -523,13 +523,13 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
int newFreq;
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
newFreq=chan[c.chan].baseFreq+c.value*(brokenPitch?2:1);
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
newFreq=chan[c.chan].baseFreq-c.value*(brokenPitch?2:1);
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
@ -1043,7 +1043,9 @@ void DivPlatformTX81Z::setFlags(const DivConfig& flags) {
}
CHECK_CUSTOM_CLOCK;
baseFreqOff=round(768.0*(log((COLOR_NTSC/(double)chipClock))/log(2.0)));
baseFreqOff=round(1536.0*(log((COLOR_NTSC/(double)chipClock))/log(2.0)));
brokenPitch=flags.getBool("brokenPitch",false);
rate=chipClock/64;
for (int i=0; i<8; i++) {

View file

@ -21,7 +21,7 @@
#define _TX81Z_H
#include "fmshared_OPM.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "sound/ymfm/ymfm_opz.h"
class DivTXInterface: public ymfm::ymfm_interface {

View file

@ -21,7 +21,7 @@
#define _PLATFORM_VB_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/vsu.h"

View file

@ -20,7 +20,7 @@
#ifndef _VRC6_H
#define _VRC6_H
#include "../fixedQueue.h"
#include "../../fixedQueue.h"
#include "../dispatch.h"
#include "vgsound_emu/src/vrcvi/vrcvi.hpp"

View file

@ -1167,8 +1167,10 @@ void DivEngine::nextRow() {
}
if (!stepPlay) {
playPosLock.lock();
prevOrder=curOrder;
prevRow=curRow;
playPosLock.unlock();
}
for (int i=0; i<chans; i++) {
@ -1349,8 +1351,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
isOn[pendingNotes[i].channel]=true;
} else {
if (isOn[pendingNotes[i].channel]) {
logV("erasing off -> on sequence in %d",pendingNotes[i].channel);
pendingNotes.erase(pendingNotes.begin()+i);
//logV("erasing off -> on sequence in %d",pendingNotes[i].channel);
pendingNotes[i].nop=true;
}
}
}
@ -1358,12 +1360,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
while (!pendingNotes.empty()) {
DivNoteEvent& note=pendingNotes.front();
if (note.channel<0 || note.channel>=chans) {
if (note.nop || note.channel<0 || note.channel>=chans) {
pendingNotes.pop_front();
continue;
}
if (note.on) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
//dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,(note.volume*(chan[note.channel].volMax>>8))/127));
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note));
keyHit[note.channel]=true;
chan[note.channel].noteOnInhibit=true;
@ -1405,8 +1408,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
endOfSong=false;
if (stepPlay==2) {
stepPlay=1;
playPosLock.lock();
prevOrder=curOrder;
prevRow=curRow;
playPosLock.unlock();
}
nextRow();
break;
@ -1850,7 +1855,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
}
}
logD("%.2x",msg.type);
//logD("%.2x",msg.type);
output->midiIn->queue.pop();
}

View file

@ -24,7 +24,7 @@
#include "defines.h"
#include "safeWriter.h"
#include "dataErrors.h"
#include <deque>
#include "../fixedQueue.h"
enum DivSampleLoopMode: unsigned char {
DIV_SAMPLE_LOOP_FORWARD=0,
@ -144,8 +144,8 @@ struct DivSample {
unsigned int samples;
std::deque<DivSampleHistory*> undoHist;
std::deque<DivSampleHistory*> redoHist;
FixedQueue<DivSampleHistory*,128> undoHist;
FixedQueue<DivSampleHistory*,128> redoHist;
/**
* put sample data.

View file

@ -26,7 +26,7 @@
#include <functional>
#include <future>
#include "fixedQueue.h"
#include "../fixedQueue.h"
class DivWorkPool;

View file

@ -21,17 +21,20 @@
#define _FIXED_QUEUE_H
#include <stdint.h>
#include "../ta-log.h"
#include "ta-log.h"
template<typename T, size_t items> struct FixedQueue {
size_t readPos, writePos;
T data[items];
T& operator[](size_t pos);
T& front();
T& back();
bool pop();
bool push(const T& item);
bool erase(size_t pos);
bool pop_front();
bool pop_back();
bool push_front(const T& item);
@ -44,6 +47,13 @@ template<typename T, size_t items> struct FixedQueue {
writePos(0) {}
};
template <typename T, size_t items> T& FixedQueue<T,items>::operator[](size_t pos) {
if (pos>=size()) {
logW("accessing invalid position. bug!");
}
return data[(readPos+pos)%items];
}
template <typename T, size_t items> T& FixedQueue<T,items>::front() {
return data[readPos];
}
@ -53,6 +63,36 @@ template <typename T, size_t items> T& FixedQueue<T,items>::back() {
return data[writePos-1];
}
template <typename T, size_t items> bool FixedQueue<T,items>::erase(size_t pos) {
size_t curSize=size();
if (pos>=curSize) {
logW("accessing invalid position. bug!");
return false;
}
if (pos==0) {
return pop_front();
}
if (pos==curSize-1) {
return pop_back();
}
for (size_t i=pos+1, p=(readPos+pos)%items, p1=(readPos+pos+1)%items; i<curSize; i++) {
if (p>=items) p-=items;
if (p1>=items) p1-=items;
data[p]=data[p1];
p++;
p1++;
}
if (writePos>0) {
writePos--;
} else {
writePos=items-1;
}
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::pop() {
if (readPos==writePos) return false;
if (++readPos>=items) readPos=0;

View file

@ -166,6 +166,7 @@ const char* aboutLine[]={
"Portable File Dialogs by Sam Hocevar",
"Native File Dialog by Frogtoss Games",
"PortAudio",
"Weak-JACK by x42",
"RtMidi by Gary P. Scavone",
"FFTW by Matteo Frigo and Steven G. Johnson",
"backward-cpp by Google",

View file

@ -29,13 +29,13 @@ void FurnaceGUI::drawClock() {
}
if (!clockOpen) return;
if (ImGui::Begin("Clock",&clockOpen,globalWinFlags)) {
int row=e->getRow();
int row=oldRow;
int elapsedBars=e->getElapsedBars();
int elapsedBeats=e->getElapsedBeats();
bool playing=e->isPlaying();
if (clockShowRow) {
ImGui::PushFont(bigFont);
ImGui::Text("%.3d:%.3d",e->getOrder(),row);
ImGui::Text("%.3d:%.3d",playOrder,row);
ImGui::PopFont();
}
if (clockShowBeat) {

View file

@ -574,6 +574,16 @@ void FurnaceGUI::drawDebug() {
ImGui::PlotLines("##DebugFMPreview",asFloat,FM_PREVIEW_SIZE,0,"Preview",-1.0,1.0,ImVec2(300.0f*dpiScale,150.0f*dpiScale));
ImGui::TreePop();
}
if (ImGui::TreeNode("Recent Files")) {
ImGui::Text("Items: %d - Max: %d",(int)recentFile.size(),settings.maxRecentFile);
ImGui::Text("readPos: %d - writePos: %d",(int)recentFile.readPos,(int)recentFile.writePos);
ImGui::Indent();
for (size_t i=0; i<recentFile.size(); i++) {
ImGui::Text("%d: %s",(int)i,recentFile[i].c_str());
}
ImGui::Unindent();
ImGui::TreePop();
}
if (ImGui::TreeNode("User Interface")) {
if (ImGui::Button("Inspect")) {
inspectorOpen=!inspectorOpen;

View file

@ -1566,8 +1566,6 @@ void FurnaceGUI::doAction(int what) {
e->deleteOrder(curOrder);
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}
makeUndo(GUI_UNDO_CHANGE_ORDER);

View file

@ -1209,8 +1209,6 @@ void FurnaceGUI::doUndo() {
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}
@ -1287,8 +1285,6 @@ void FurnaceGUI::doRedo() {
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}

View file

@ -1134,7 +1134,7 @@ void FurnaceGUI::stop() {
if (followPattern && wasPlaying) {
nextScroll=-1.0f;
nextAddScroll=0.0f;
cursor.y=e->getRow();
cursor.y=oldRow;
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) {
selStart=cursor;
selEnd=cursor;
@ -2207,7 +2207,7 @@ void FurnaceGUI::pushRecentFile(String path) {
if (path.find(backupPath)==0) return;
for (int i=0; i<(int)recentFile.size(); i++) {
if (recentFile[i]==path) {
recentFile.erase(recentFile.begin()+i);
recentFile.erase(i);
i--;
}
}
@ -3545,7 +3545,7 @@ bool FurnaceGUI::loop() {
case SDL_DROPFILE:
if (ev.drop.file!=NULL) {
int sampleCountBefore=e->song.sampleLen;
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file,true,settings.readInsNames);
DivWavetable* droppedWave=NULL;
DivSample* droppedSample=NULL;
if (!instruments.empty()) {
@ -3922,6 +3922,17 @@ bool FurnaceGUI::loop() {
curWindow=GUI_WINDOW_NOTHING;
editOptsVisible=false;
int nextPlayOrder=0;
int nextOldRow=0;
e->getPlayPos(nextPlayOrder,nextOldRow);
playOrder=nextPlayOrder;
if (followPattern) {
curOrder=playOrder;
}
if (e->isPlaying()) {
oldRow=nextOldRow;
}
if (!mobileUI) {
ImGui::BeginMainMenuBar();
if (ImGui::BeginMenu(settings.capitalMenuBar?"File":"file")) {
@ -3948,7 +3959,7 @@ bool FurnaceGUI::loop() {
nextFile=item;
showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP);
} else {
recentFile.erase(recentFile.begin()+i);
recentFile.erase(i);
i--;
if (load(item)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
@ -4379,15 +4390,15 @@ bool FurnaceGUI::loop() {
info+=fmt::sprintf(" @ %gHz (%g BPM) ",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
if (settings.orderRowsBase) {
info+=fmt::sprintf("| Order %.2X/%.2X ",e->getOrder(),e->curSubSong->ordersLen-1);
info+=fmt::sprintf("| Order %.2X/%.2X ",playOrder,e->curSubSong->ordersLen-1);
} else {
info+=fmt::sprintf("| Order %d/%d ",e->getOrder(),e->curSubSong->ordersLen-1);
info+=fmt::sprintf("| Order %d/%d ",playOrder,e->curSubSong->ordersLen-1);
}
if (settings.patRowsBase) {
info+=fmt::sprintf("| Row %.2X/%.2X ",e->getRow(),e->curSubSong->patLen);
info+=fmt::sprintf("| Row %.2X/%.2X ",oldRow,e->curSubSong->patLen);
} else {
info+=fmt::sprintf("| Row %d/%d ",e->getRow(),e->curSubSong->patLen);
info+=fmt::sprintf("| Row %d/%d ",oldRow,e->curSubSong->patLen);
}
info+=fmt::sprintf("| %d:%.2d:%.2d.%.2d",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
@ -4459,10 +4470,6 @@ bool FurnaceGUI::loop() {
MEASURE(calcChanOsc,calcChanOsc());
if (followPattern) {
curOrder=e->getOrder();
}
if (mobileUI) {
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
@ -4850,7 +4857,7 @@ bool FurnaceGUI::loop() {
break;
case GUI_FILE_INS_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
if (e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song)) {
if (e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song,settings.writeInsNames)) {
pushRecentSys(copyOfName.c_str());
}
}
@ -4976,7 +4983,7 @@ bool FurnaceGUI::loop() {
String warns="there were some warnings/errors while loading instruments:\n";
int sampleCountBefore=e->song.sampleLen;
for (String i: fileDialog->getFileName()) {
std::vector<DivInstrument*> insTemp=e->instrumentFromFile(i.c_str());
std::vector<DivInstrument*> insTemp=e->instrumentFromFile(i.c_str(),true,settings.readInsNames);
if (insTemp.empty()) {
warn=true;
warns+=fmt::sprintf("> %s: cannot load instrument! (%s)\n",i,e->getLastError());
@ -5022,7 +5029,7 @@ bool FurnaceGUI::loop() {
}
case GUI_FILE_INS_OPEN_REPLACE: {
int sampleCountBefore=e->song.sampleLen;
std::vector<DivInstrument*> instruments=e->instrumentFromFile(copyOfName.c_str());
std::vector<DivInstrument*> instruments=e->instrumentFromFile(copyOfName.c_str(),true,settings.readInsNames);
if (!instruments.empty()) {
if (e->song.sampleLen!=sampleCountBefore) {
e->renderSamplesP();
@ -5540,8 +5547,6 @@ bool FurnaceGUI::loop() {
stop();
e->clearSubSongs();
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
@ -5552,8 +5557,6 @@ bool FurnaceGUI::loop() {
});
e->setOrder(0);
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
@ -5565,8 +5568,6 @@ bool FurnaceGUI::loop() {
});
e->setOrder(0);
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
@ -5671,8 +5672,6 @@ bool FurnaceGUI::loop() {
undoHist.clear();
redoHist.clear();
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
@ -7079,10 +7078,9 @@ FurnaceGUI::FurnaceGUI():
curSample(0),
curOctave(3),
curOrder(0),
playOrder(0),
prevIns(0),
oldRow(0),
oldOrder(0),
oldOrder1(0),
editStep(1),
exportLoops(0),
soloChan(-1),

View file

@ -27,7 +27,6 @@
#include "imgui_impl_sdl2.h"
#include <SDL.h>
#include <fftw3.h>
#include <deque>
#include <initializer_list>
#include <map>
#include <future>
@ -874,6 +873,18 @@ struct UndoStep {
std::vector<UndoOrderData> ord;
std::vector<UndoPatternData> pat;
std::vector<UndoOtherData> other;
UndoStep():
type(GUI_UNDO_CHANGE_ORDER),
cursor(),
selStart(),
selEnd(),
order(0),
nibble(false),
oldOrdersLen(0),
newOrdersLen(0),
oldPatLen(0),
newPatLen(0) {}
};
// -1 = any
@ -1323,7 +1334,7 @@ class FurnaceGUI {
std::vector<DivSystem> sysSearchResults;
std::vector<FurnaceGUISysDef> newSongSearchResults;
std::deque<String> recentFile;
FixedQueue<String,32> recentFile;
std::vector<DivInstrumentType> makeInsTypeList;
std::vector<String> availRenderDrivers;
std::vector<String> availAudioDrivers;
@ -1391,7 +1402,7 @@ class FurnaceGUI {
String backupPath;
std::mutex midiLock;
std::queue<TAMidiMessage> midiQueue;
FixedQueue<TAMidiMessage,4096> midiQueue;
MIDIMap midiMap;
int learning;
@ -1580,6 +1591,8 @@ class FurnaceGUI {
int chanOscThreads;
int renderPoolThreads;
int showPool;
int writeInsNames;
int readInsNames;
unsigned int maxUndoSteps;
String mainFontPath;
String headFontPath;
@ -1760,6 +1773,8 @@ class FurnaceGUI {
chanOscThreads(0),
renderPoolThreads(0),
showPool(0),
writeInsNames(1),
readInsNames(1),
maxUndoSteps(100),
mainFontPath(""),
headFontPath(""),
@ -1795,7 +1810,7 @@ class FurnaceGUI {
DivInstrument* prevInsData;
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
int curGroove, exitDisabledTimer;
@ -2005,8 +2020,8 @@ class FurnaceGUI {
int oldOrdersLen;
DivOrders oldOrders;
DivPattern* oldPat[DIV_MAX_CHANS];
std::deque<UndoStep> undoHist;
std::deque<UndoStep> redoHist;
FixedQueue<UndoStep,256> undoHist;
FixedQueue<UndoStep,256> redoHist;
// sample editor specific
double sampleZoom;

View file

@ -259,18 +259,18 @@ void FurnaceGUI::drawOrders() {
}
ImGui::PushFont(patFont);
bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x)));
ImGui::PopFont();
float yHeight=ImGui::GetContentRegionAvail().y;
float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale);
if (e->isPlaying()) {
if (followOrders) {
float nextOrdScroll=(playOrder+1)*lineHeight-((yHeight-(tooSmall?ImGui::GetStyle().ScrollbarSize:0.0f))/2.0f);
if (nextOrdScroll<0.0f) nextOrdScroll=0.0f;
ImGui::SetNextWindowScroll(ImVec2(-1.0f,nextOrdScroll));
}
}
if (ImGui::BeginTable("OrdersTable",1+displayChans,(tooSmall?ImGuiTableFlags_SizingFixedFit:ImGuiTableFlags_SizingStretchSame)|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY)) {
ImGui::PushFont(patFont);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,prevSpacing);
ImGui::TableSetupScrollFreeze(1,1);
float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale);
if (e->isPlaying()) {
if (followOrders) {
ImGui::SetScrollY((e->getOrder()+1)*lineHeight-((yHeight-(tooSmall?ImGui::GetStyle().ScrollbarSize:0.0f))/2.0f));
}
}
ImGui::TableNextRow(0,lineHeight);
ImVec2 ra=ImGui::GetContentRegionAvail();
ImGui::TableNextColumn();
@ -283,9 +283,9 @@ void FurnaceGUI::drawOrders() {
ImGui::PopStyleColor();
for (int i=0; i<e->curSubSong->ordersLen; i++) {
ImGui::TableNextRow(0,lineHeight);
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
if (playOrder==i && e->isPlaying()) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
ImGui::TableNextColumn();
if ((!followPattern && curOrder==i) || (followPattern && oldOrder1==i)) {
if (curOrder==i) {
// draw a border
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 rBegin=ImGui::GetCursorScreenPos();
@ -322,7 +322,7 @@ void FurnaceGUI::drawOrders() {
//}
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->curOrders->ord[j][i]==e->curOrders->ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
if (ImGui::Selectable(selID,settings.ordersCursor?(cursor.xCoarse==j && oldOrder1!=i):false)) {
if (ImGui::Selectable(selID,settings.ordersCursor?(cursor.xCoarse==j && curOrder!=i):false)) {
if (curOrder==i) {
if (orderEditMode==0) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
@ -394,9 +394,9 @@ void FurnaceGUI::drawOrders() {
}
}
ImGui::PopStyleVar();
ImGui::PopFont();
ImGui::EndTable();
}
ImGui::PopFont();
if (settings.orderButtonPos==2) {
ImGui::TableNextColumn();
@ -411,6 +411,5 @@ void FurnaceGUI::drawOrders() {
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ORDERS;
oldOrder1=e->getOrder();
ImGui::End();
}

View file

@ -91,7 +91,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (settings.overflowHighlight) {
if (edit && cursor.y==i && curWindowLast==GUI_WINDOW_PATTERN) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
} else if (isPlaying && oldRow==i && ord==playOrder) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
@ -102,7 +102,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
isPushing=true;
if (edit && cursor.y==i && curWindowLast==GUI_WINDOW_PATTERN) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
} else if (isPlaying && oldRow==i && ord==playOrder) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
@ -378,7 +378,7 @@ void FurnaceGUI::drawPattern() {
bool inhibitMenu=false;
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) {
cursor.y=e->isStepping()?e->getRow():oldRow;
cursor.y=oldRow;
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) {
selStart=cursor;
selEnd=cursor;
@ -419,8 +419,7 @@ void FurnaceGUI::drawPattern() {
}
//char id[32];
ImGui::PushFont(patFont);
int ord=oldOrder;
oldOrder=curOrder;
int ord=curOrder;
int chans=e->getTotalChannelCount();
int displayChans=0;
const DivPattern* patCache[DIV_MAX_CHANS];
@ -437,23 +436,24 @@ void FurnaceGUI::drawPattern() {
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+centerOff);
}
}
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(oldRow);
if (--pendingStepUpdate<0) pendingStepUpdate=0;
if (nextScroll>-0.5f) {
ImGui::SetNextWindowScroll(ImVec2(-1.0f,nextScroll));
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea|((settings.cursorFollowsWheel || wheelCalmDown)?ImGuiTableFlags_NoScrollWithMouse:0))) {
ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed);
char chanID[2048];
float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);
int curRow=e->getRow();
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(curRow);
if (--pendingStepUpdate<0) pendingStepUpdate=0;
if (nextScroll>-0.5f) {
ImGui::SetScrollY(nextScroll);
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
if (nextAddScroll!=0.0f) {
ImGui::SetScrollY(ImGui::GetScrollY()+nextAddScroll);
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
ImGui::TableSetupScrollFreeze(1,1);
for (int i=0; i<chans; i++) {
if (!e->curSubSong->chanShow[i]) continue;
@ -937,7 +937,6 @@ void FurnaceGUI::drawPattern() {
ImGui::EndDisabled();
ImGui::PopStyleVar();
oldRow=curRow;
if (demandScrollX) {
int totalDemand=demandX-ImGui::GetScrollX();
if (totalDemand<80) {

View file

@ -1098,7 +1098,7 @@ void FurnaceGUI::initSystemPresets() {
ENTRY(
"Sharp X68000", {
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"),
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "")
CH(DIV_SYSTEM_MSM6258, 1.0f, 0, "clockSel=2")
}
);
ENTRY(

View file

@ -498,8 +498,24 @@ void FurnaceGUI::drawSettings() {
}
ImGui::Unindent();
// SUBSECTION CHIP
CONFIG_SUBSECTION("Chip");
bool writeInsNamesB=settings.writeInsNames;
if (ImGui::Checkbox("Store instrument name in .fui",&writeInsNamesB)) {
settings.writeInsNames=writeInsNamesB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, saving an instrument will store its name.\nthis may increase file size.");
}
bool readInsNamesB=settings.readInsNames;
if (ImGui::Checkbox("Load instrument name from .fui",&readInsNamesB)) {
settings.readInsNames=readInsNamesB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, loading an instrument will use the stored name (if present).\notherwise, it will use the file name.");
}
// SUBSECTION NEW SONG
CONFIG_SUBSECTION("New Song");
ImGui::AlignTextToFramePadding();
ImGui::Text("Initial system:");
ImGui::SameLine();
@ -3341,6 +3357,8 @@ void FurnaceGUI::syncSettings() {
settings.chanOscThreads=e->getConfInt("chanOscThreads",0);
settings.renderPoolThreads=e->getConfInt("renderPoolThreads",0);
settings.showPool=e->getConfInt("showPool",0);
settings.writeInsNames=e->getConfInt("writeInsNames",1);
settings.readInsNames=e->getConfInt("readInsNames",1);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.headFontSize,2,96);
@ -3494,6 +3512,8 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.chanOscThreads,0,256);
clampSetting(settings.renderPoolThreads,0,DIV_MAX_CHIPS);
clampSetting(settings.showPool,0,1);
clampSetting(settings.writeInsNames,0,1);
clampSetting(settings.readInsNames,0,1);
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
@ -3755,6 +3775,8 @@ void FurnaceGUI::commitSettings() {
e->setConf("chanOscThreads",settings.chanOscThreads);
e->setConf("renderPoolThreads",settings.renderPoolThreads);
e->setConf("showPool",settings.showPool);
e->setConf("writeInsNames",settings.writeInsNames);
e->setConf("readInsNames",settings.readInsNames);
// colors
for (int i=0; i<GUI_COLOR_MAX; i++) {

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