diff --git a/.gitignore b/.gitignore index 26d7f5904..6bd68968c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index eaad23559..8e0e76f9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/android/app/build.gradle b/android/app/build.gradle index 91db8e5e0..8939a6d1d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2a283791a..5a99f3cb1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ diff --git a/doc/2-interface/basic-mode.md b/doc/2-interface/basic-mode.md index 198f7c68d..cb69659d3 100644 --- a/doc/2-interface/basic-mode.md +++ b/doc/2-interface/basic-mode.md @@ -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 diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index 66b70d7d2..c6c08a082 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -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 diff --git a/doc/4-instrument/fm-opl.md b/doc/4-instrument/fm-opl.md index f3e1c6d0b..bfd979651 100644 --- a/doc/4-instrument/fm-opl.md +++ b/doc/4-instrument/fm-opl.md @@ -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 diff --git a/doc/4-instrument/fm-opll.md b/doc/4-instrument/fm-opll.md index 3c91ee348..87eac0253 100644 --- a/doc/4-instrument/fm-opll.md +++ b/doc/4-instrument/fm-opll.md @@ -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 diff --git a/doc/4-instrument/fm-opm.md b/doc/4-instrument/fm-opm.md index fe822b428..a5d12f4df 100644 --- a/doc/4-instrument/fm-opm.md +++ b/doc/4-instrument/fm-opm.md @@ -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 diff --git a/doc/4-instrument/fm-opn.md b/doc/4-instrument/fm-opn.md index c78b93b19..fd318ee3b 100644 --- a/doc/4-instrument/fm-opn.md +++ b/doc/4-instrument/fm-opn.md @@ -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 diff --git a/doc/4-instrument/fm-opz.md b/doc/4-instrument/fm-opz.md index 5a15d5f30..d6a400e63 100644 --- a/doc/4-instrument/fm-opz.md +++ b/doc/4-instrument/fm-opz.md @@ -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 diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index 3b975077f..539dbbf0a 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -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. diff --git a/doc/7-systems/ay8930.md b/doc/7-systems/ay8930.md index ce94aa2b1..c865b7f7a 100644 --- a/doc/7-systems/ay8930.md +++ b/doc/7-systems/ay8930.md @@ -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. diff --git a/doc/7-systems/genesis.md b/doc/7-systems/genesis.md index d1775e574..283fe3ddc 100644 --- a/doc/7-systems/genesis.md +++ b/doc/7-systems/genesis.md @@ -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). diff --git a/doc/7-systems/n163.md b/doc/7-systems/n163.md index ecd2611e8..a810f8106 100644 --- a/doc/7-systems/n163.md +++ b/doc/7-systems/n163.md @@ -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. diff --git a/doc/7-systems/opl.md b/doc/7-systems/opl.md index 31ff80b67..2937fa893 100644 --- a/doc/7-systems/opl.md +++ b/doc/7-systems/opl.md @@ -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 diff --git a/doc/7-systems/opll.md b/doc/7-systems/opll.md index 1ed9fb64d..2cf068c13 100644 --- a/doc/7-systems/opll.md +++ b/doc/7-systems/opll.md @@ -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. diff --git a/doc/7-systems/opz.md b/doc/7-systems/opz.md index 6aad70c52..7dcf58241 100644 --- a/doc/7-systems/opz.md +++ b/doc/7-systems/opz.md @@ -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 diff --git a/doc/7-systems/sm8521.md b/doc/7-systems/sm8521.md index 24649afec..2e0e8dc5c 100644 --- a/doc/7-systems/sm8521.md +++ b/doc/7-systems/sm8521.md @@ -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. diff --git a/doc/7-systems/sms.md b/doc/7-systems/sms.md index 889d82e25..576311596 100644 --- a/doc/7-systems/sms.md +++ b/doc/7-systems/sms.md @@ -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 diff --git a/doc/7-systems/x1-010.md b/doc/7-systems/x1-010.md index 3f181850d..8ac56a230 100644 --- a/doc/7-systems/x1-010.md +++ b/doc/7-systems/x1-010.md @@ -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.** diff --git a/doc/7-systems/ym2151.md b/doc/7-systems/ym2151.md index 287a47d52..4c6fce90c 100644 --- a/doc/7-systems/ym2151.md +++ b/doc/7-systems/ym2151.md @@ -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 diff --git a/doc/7-systems/ym2203.md b/doc/7-systems/ym2203.md index a05cb84cf..b01d2a054 100644 --- a/doc/7-systems/ym2203.md +++ b/doc/7-systems/ym2203.md @@ -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. diff --git a/doc/7-systems/ym2608.md b/doc/7-systems/ym2608.md index 528541e91..27d5f1e06 100644 --- a/doc/7-systems/ym2608.md +++ b/doc/7-systems/ym2608.md @@ -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. diff --git a/doc/7-systems/ym2610.md b/doc/7-systems/ym2610.md index 8eb14a1b9..29a85675b 100644 --- a/doc/7-systems/ym2610.md +++ b/doc/7-systems/ym2610.md @@ -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. diff --git a/doc/7-systems/ym2610b.md b/doc/7-systems/ym2610b.md index aa26b9c6f..5f15fb8e2 100644 --- a/doc/7-systems/ym2610b.md +++ b/doc/7-systems/ym2610b.md @@ -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. diff --git a/doc/7-systems/ym2612.md b/doc/7-systems/ym2612.md index 9dceb5814..dd5ba5502 100644 --- a/doc/7-systems/ym2612.md +++ b/doc/7-systems/ym2612.md @@ -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.** diff --git a/doc/README.md b/doc/README.md index fd2536df2..3e6652c13 100644 --- a/doc/README.md +++ b/doc/README.md @@ -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. + + diff --git a/extern/weakjack/.gitignore b/extern/weakjack/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/extern/weakjack/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/extern/weakjack/README.md b/extern/weakjack/README.md new file mode 100644 index 000000000..7b957d0f4 --- /dev/null +++ b/extern/weakjack/README.md @@ -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` 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 diff --git a/extern/weakjack/weak_libjack.c b/extern/weakjack/weak_libjack.c new file mode 100644 index 000000000..f1c7d4bae --- /dev/null +++ b/extern/weakjack/weak_libjack.c @@ -0,0 +1,291 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * 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 +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#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 diff --git a/extern/weakjack/weak_libjack.def b/extern/weakjack/weak_libjack.def new file mode 100644 index 000000000..fda83a4fc --- /dev/null +++ b/extern/weakjack/weak_libjack.def @@ -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 + +/* */ +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) + +/* */ +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),) + +/* */ +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), ) + +/* */ +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) + +/* */ +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 +/* - TODO*/ + +/* */ +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) + +/* */ +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 + +/* */ +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 diff --git a/extern/weakjack/weak_libjack.h b/extern/weakjack/weak_libjack.h new file mode 100644 index 000000000..e23f92389 --- /dev/null +++ b/extern/weakjack/weak_libjack.h @@ -0,0 +1,230 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * 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 + +/* */ +#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 + +/* */ +#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 + +/* */ +#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 + +/* */ +#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 + +/* */ +#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 + +/* */ +#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 +/* */ +#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 + +/* */ +#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 +#include +#include +#include +#include +#include +#include + +#ifndef NO_JACK_METADATA +#include +#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 diff --git a/papers/clipboard-format.md b/papers/clipboard-format.md index fd6b075ed..ec6378837 100644 --- a/papers/clipboard-format.md +++ b/papers/clipboard-format.md @@ -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. diff --git a/papers/format.md b/papers/format.md index 447785bf2..f2281f43d 100644 --- a/papers/format.md +++ b/papers/format.md @@ -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 diff --git a/res/Info.plist b/res/Info.plist index 51055ed6a..da4c26491 100644 --- a/res/Info.plist +++ b/res/Info.plist @@ -15,17 +15,17 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - 0.6pre14 + 0.6pre16 CFBundleName Furnace CFBundlePackageType APPL CFBundleShortVersionString - 0.6pre14 + 0.6pre16 CFBundleSignature ???? CFBundleVersion - 0.6pre14 + 0.6pre16 NSHumanReadableCopyright NSHighResolutionCapable diff --git a/res/docpdf/make_paper.py b/res/docpdf/make_paper.py index 168bab5d7..c02b1eca7 100644 --- a/res/docpdf/make_paper.py +++ b/res/docpdf/make_paper.py @@ -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 = '

contents

    ' + #-- 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 += '
' + + index += '
  • %s' % ( pageLink, pageTitle, pageLink ) + if pageLink.endswith("__README.md"): + index += '
      ' + else: + index += '' # 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 += '
  • ' + # build html final_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__": for version 0.6 +
    +
    +
    +
    +

    authors

    +
      +
    • cam900
    • +
    • DeMOSic
    • +
    • Electric Keet
    • +
    • freq-mod
    • +
    • host12prog
    • +
    • nicco1690
    • +
    • tildearrow
    • +
    +

    special thanks to ZoomTen for providing tools which assisted in the production of this document!

    +

    copyright © 2023 tildearrow and other authors.

    +

    this documentation is under the Creative Commons Attribution 3.0 Unported 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.

    +

    this manual is written for version 0.6 of Furnace.
    it may not necessarily apply to previous or future versions.

    +
    +
    + %s +
    %s ''' % ( - html + index, html ) ) diff --git a/res/furnace.appdata.xml b/res/furnace.appdata.xml.in similarity index 79% rename from res/furnace.appdata.xml rename to res/furnace.appdata.xml.in index 5055d0df9..6103fef1f 100644 --- a/res/furnace.appdata.xml +++ b/res/furnace.appdata.xml.in @@ -4,13 +4,14 @@ Furnace Open-source chiptune tracker + https://github.com/tildearrow/furnace CC0-1.0 GPL-2.0-or-later

    - the biggest chiptune tracker ever made! + Furnace - the biggest chiptune tracker ever made!

    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.

    - rationale for intense profanity: 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.

    @@ -34,4 +35,4 @@ https://tildearrow.org/storage/images/furnace.png - + diff --git a/res/make-appdata.sh b/res/make-appdata.sh new file mode 100755 index 000000000..78d1865ee --- /dev/null +++ b/res/make-appdata.sh @@ -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 " " >> "$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 " " >> "$2" + echo " https://github.com/tildearrow/furnace/releases/tag/$releaseVer" >> "$2" + echo " " >> "$2" +done + +echo " " >> "$2" + +echo "" >> "$2" + +#echo "done." diff --git a/scripts/release-linux-AppImage.sh b/scripts/release-linux-AppImage.sh index 4cf8a8cd4..c535d2f6e 100755 --- a/scripts/release-linux-AppImage.sh +++ b/scripts/release-linux-AppImage.sh @@ -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 diff --git a/src/audio/jack.cpp b/src/audio/jack.cpp index 2ffe19b63..64c106c54 100644 --- a/src/audio/jack.cpp +++ b/src/audio/jack.cpp @@ -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; diff --git a/src/audio/jack.h b/src/audio/jack.h index 8018d6c20..41d76824b 100644 --- a/src/audio/jack.h +++ b/src/audio/jack.h @@ -18,8 +18,7 @@ */ #include "taAudio.h" -#include -#include +#include "../../extern/weakjack/weak_libjack.h" class TAAudioJACK: public TAAudio { jack_client_t* ac; diff --git a/src/audio/taAudio.h b/src/audio/taAudio.h index 20491dbbe..12de61e95 100644 --- a/src/audio/taAudio.h +++ b/src/audio/taAudio.h @@ -21,7 +21,7 @@ #define _TAAUDIO_H #include "../ta-utils.h" #include -#include +#include "../fixedQueue.h" #include struct SampleRateChangeEvent { @@ -124,7 +124,7 @@ struct TAMidiMessage { class TAMidiIn { public: - std::queue queue; + FixedQueue queue; virtual bool gather(); bool next(TAMidiMessage& where); virtual bool isDeviceOpen(); @@ -139,7 +139,7 @@ class TAMidiIn { }; class TAMidiOut { - std::queue queue; + FixedQueue queue; public: virtual bool send(const TAMidiMessage& what); virtual bool isDeviceOpen(); diff --git a/src/engine/configEngine.cpp b/src/engine/configEngine.cpp index f793d35c2..123a19a81 100644 --- a/src/engine/configEngine.cpp +++ b/src/engine/configEngine.cpp @@ -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__ diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fee404523..4d07acd45 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -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; } diff --git a/src/engine/engine.h b/src/engine/engine.h index ccaaf36e3..46b96ac57 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -37,7 +37,7 @@ #include #include #include -#include +#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 pendingNotes; + FixedQueue 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 instrumentFromFile(const char* path, bool loadAssets=true); + std::vector instrumentFromFile(const char* path, bool loadAssets=true, bool readInsName=true); // load temporary instrument void loadTempIns(DivInstrument* which); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 69f329e00..6f7065ef4 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -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& ret, S } } -std::vector DivEngine::instrumentFromFile(const char* path, bool loadAssets) { +std::vector DivEngine::instrumentFromFile(const char* path, bool loadAssets, bool readInsName) { std::vector ret; warnings=""; @@ -1921,12 +1921,17 @@ std::vector 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) { diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 28cb99bca..6b871155c 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -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"); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index a019198f0..20172b7b8 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -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. diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 26ad689a1..ab26b5b08 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -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++) { diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 2948f3b85..354557fda 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -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 { diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 0ff69ad40..ba2f9abea 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -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 { diff --git a/src/engine/platform/c140.h b/src/engine/platform/c140.h index fb74151c0..9dea0c0c5 100644 --- a/src/engine/platform/c140.h +++ b/src/engine/platform/c140.h @@ -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 { diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index def32eaab..38e735d9a 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -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" diff --git a/src/engine/platform/es5506.h b/src/engine/platform/es5506.h index 07bdb2380..6bac20c37 100644 --- a/src/engine/platform/es5506.h +++ b/src/engine/platform/es5506.h @@ -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" diff --git a/src/engine/platform/fmshared_OPM.h b/src/engine/platform/fmshared_OPM.h index e922e8abb..2002db333 100644 --- a/src/engine/platform/fmshared_OPM.h +++ b/src/engine/platform/fmshared_OPM.h @@ -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 diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index 9be217b94..e458904eb 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -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) diff --git a/src/engine/platform/ga20.h b/src/engine/platform/ga20.h index 691b68f0a..3831d32c2 100644 --- a/src/engine/platform/ga20.h +++ b/src/engine/platform/ga20.h @@ -21,7 +21,7 @@ #define _GA20_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "../macroInt.h" #include "sound/ga20/iremga20.h" diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index f92a0b895..0e7fb52d3 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -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 { diff --git a/src/engine/platform/k007232.h b/src/engine/platform/k007232.h index e3409b31f..5eb379985 100644 --- a/src/engine/platform/k007232.h +++ b/src/engine/platform/k007232.h @@ -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" diff --git a/src/engine/platform/msm5232.h b/src/engine/platform/msm5232.h index abdb72f06..efa5c919c 100644 --- a/src/engine/platform/msm5232.h +++ b/src/engine/platform/msm5232.h @@ -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 { diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index ce73ca4bb..2a4c3512b 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -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 { diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index dfa59a0c1..19815f0da 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -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 { diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index c5ec64b7e..e4d5b24d2 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -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" diff --git a/src/engine/platform/namcowsg.h b/src/engine/platform/namcowsg.h index 7d6943323..ab4451705 100644 --- a/src/engine/platform/namcowsg.h +++ b/src/engine/platform/namcowsg.h @@ -21,7 +21,7 @@ #define _NAMCOWSG_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "../waveSynth.h" #include "sound/namco.h" diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 44e9b3b14..5a7580a78 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -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) { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 2298f6132..bcc147da1 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -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" diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 8bb6baa2c..e8bd627a4 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -21,7 +21,7 @@ #define _OPLL_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" extern "C" { #include "../../../extern/Nuked-OPLL/opll.h" diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index f989034af..2436453e4 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -21,7 +21,7 @@ #define _PCE_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "../waveSynth.h" #include "sound/pce_psg.h" diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index d9315b5ec..b2a88b903 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -21,7 +21,7 @@ #define _PCSPKR_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include #include #include diff --git a/src/engine/platform/pokey.h b/src/engine/platform/pokey.h index 979f60754..7e64c3c78 100644 --- a/src/engine/platform/pokey.h +++ b/src/engine/platform/pokey.h @@ -21,7 +21,7 @@ #define _POKEY_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" extern "C" { #include "sound/pokey/mzpokeysnd.h" diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 36db57cb7..339799d2e 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -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 { diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 639d875b4..b03459f5d 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -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: diff --git a/src/engine/platform/sm8521.h b/src/engine/platform/sm8521.h index 02e2f458e..f9faeebda 100644 --- a/src/engine/platform/sm8521.h +++ b/src/engine/platform/sm8521.h @@ -21,7 +21,7 @@ #define _SM8521_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "../waveSynth.h" #include "sound/sm8521.h" diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index d87546b30..5eaf204ba 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -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 { diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index a799c7a5d..0992a46a7 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -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 { diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 99b784df3..4f227e5b5 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -21,7 +21,7 @@ #define _SU_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "sound/su.h" class DivPlatformSoundUnit: public DivDispatch { diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 1e0fbeef7..5f33e81c0 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -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 { diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index 4bb3cda2a..5e69c6268 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -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 { diff --git a/src/engine/platform/ted.h b/src/engine/platform/ted.h index 58b4d0b7d..a2bd1d636 100644 --- a/src/engine/platform/ted.h +++ b/src/engine/platform/ted.h @@ -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 { diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index a8f3a2f46..f677429ff 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -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++) { diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index c14bc0117..93da1cd7a 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -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 { diff --git a/src/engine/platform/vb.h b/src/engine/platform/vb.h index 2282f62fe..508e9a049 100644 --- a/src/engine/platform/vb.h +++ b/src/engine/platform/vb.h @@ -21,7 +21,7 @@ #define _PLATFORM_VB_H #include "../dispatch.h" -#include "../fixedQueue.h" +#include "../../fixedQueue.h" #include "../waveSynth.h" #include "sound/vsu.h" diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index df0aa92e5..f20e0d39c 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -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" diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2cc00ffab..5ce6ec73e 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1167,8 +1167,10 @@ void DivEngine::nextRow() { } if (!stepPlay) { + playPosLock.lock(); prevOrder=curOrder; prevRow=curRow; + playPosLock.unlock(); } for (int i=0; i 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(); } diff --git a/src/engine/sample.h b/src/engine/sample.h index bca881650..253fe347c 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -24,7 +24,7 @@ #include "defines.h" #include "safeWriter.h" #include "dataErrors.h" -#include +#include "../fixedQueue.h" enum DivSampleLoopMode: unsigned char { DIV_SAMPLE_LOOP_FORWARD=0, @@ -144,8 +144,8 @@ struct DivSample { unsigned int samples; - std::deque undoHist; - std::deque redoHist; + FixedQueue undoHist; + FixedQueue redoHist; /** * put sample data. diff --git a/src/engine/workPool.h b/src/engine/workPool.h index 9c41e1ccf..17e4be52a 100644 --- a/src/engine/workPool.h +++ b/src/engine/workPool.h @@ -26,7 +26,7 @@ #include #include -#include "fixedQueue.h" +#include "../fixedQueue.h" class DivWorkPool; diff --git a/src/engine/fixedQueue.h b/src/fixedQueue.h similarity index 80% rename from src/engine/fixedQueue.h rename to src/fixedQueue.h index ad43f160d..9866fe732 100644 --- a/src/engine/fixedQueue.h +++ b/src/fixedQueue.h @@ -21,17 +21,20 @@ #define _FIXED_QUEUE_H #include -#include "../ta-log.h" +#include "ta-log.h" template 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 struct FixedQueue { writePos(0) {} }; +template T& FixedQueue::operator[](size_t pos) { + if (pos>=size()) { + logW("accessing invalid position. bug!"); + } + return data[(readPos+pos)%items]; +} + template T& FixedQueue::front() { return data[readPos]; } @@ -53,6 +63,36 @@ template T& FixedQueue::back() { return data[writePos-1]; } +template bool FixedQueue::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=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 bool FixedQueue::pop() { if (readPos==writePos) return false; if (++readPos>=items) readPos=0; diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 9b82483d3..6b13b66ce 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -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", diff --git a/src/gui/clock.cpp b/src/gui/clock.cpp index 5db890558..a44ae13d5 100644 --- a/src/gui/clock.cpp +++ b/src/gui/clock.cpp @@ -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) { diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 916204030..1efde1e09 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -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; ideleteOrder(curOrder); if (curOrder>=e->curSubSong->ordersLen) { curOrder=e->curSubSong->ordersLen-1; - oldOrder=curOrder; - oldOrder1=curOrder; e->setOrder(curOrder); } makeUndo(GUI_UNDO_CHANGE_ORDER); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 1e55d86e3..aa7e6f5c7 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -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); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 84d3561b6..2e05e79de 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -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 instruments=e->instrumentFromFile(ev.drop.file); + std::vector 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 insTemp=e->instrumentFromFile(i.c_str()); + std::vector 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 instruments=e->instrumentFromFile(copyOfName.c_str()); + std::vector 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), diff --git a/src/gui/gui.h b/src/gui/gui.h index 1cd703f57..c760354b8 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -27,7 +27,6 @@ #include "imgui_impl_sdl2.h" #include #include -#include #include #include #include @@ -874,6 +873,18 @@ struct UndoStep { std::vector ord; std::vector pat; std::vector 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 sysSearchResults; std::vector newSongSearchResults; - std::deque recentFile; + FixedQueue recentFile; std::vector makeInsTypeList; std::vector availRenderDrivers; std::vector availAudioDrivers; @@ -1391,7 +1402,7 @@ class FurnaceGUI { String backupPath; std::mutex midiLock; - std::queue midiQueue; + FixedQueue 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 undoHist; - std::deque redoHist; + FixedQueue undoHist; + FixedQueue redoHist; // sample editor specific double sampleZoom; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index afd96ea5d..43dba3d79 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -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; icurSubSong->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(); } diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index f87b4ae7c..b85b802c1 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -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; icurSubSong->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) { diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index d6463564d..b83794638 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -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( diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 37e21c0ae..51ea3cb01 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -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; igetCurrentSubSong())) { e->changeSongP(i); updateScroll(0); - oldOrder=0; - oldOrder1=0; oldRow=0; cursor.xCoarse=0; cursor.xFine=0; @@ -76,8 +74,6 @@ void FurnaceGUI::drawSubSongs(bool asChild) { } else { e->changeSongP(e->song.subsong.size()-1); updateScroll(0); - oldOrder=0; - oldOrder1=0; oldRow=0; cursor.xCoarse=0; cursor.xFine=0; @@ -98,8 +94,6 @@ void FurnaceGUI::drawSubSongs(bool asChild) { } else { e->changeSongP(e->song.subsong.size()-1); updateScroll(0); - oldOrder=0; - oldOrder1=0; oldRow=0; cursor.xCoarse=0; cursor.xFine=0; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index a247650cd..10a6c76e5 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -461,6 +461,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } case DIV_SYSTEM_YM2151: { int clockSel=flags.getInt("clockSel",0); + bool brokenPitch=flags.getBool("brokenPitch",false); ImGui::Indent(); if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",clockSel==0)) { @@ -477,9 +478,34 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } ImGui::Unindent(); + if (ImGui::Checkbox("Broken pitch macro/slides (compatibility)",&brokenPitch)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); + flags.set("brokenPitch",brokenPitch); + }); + } + break; + } + case DIV_SYSTEM_OPZ: { + bool clockSel=flags.getInt("clockSel",0); + bool brokenPitch=flags.getBool("brokenPitch",false); + + if (ImGui::Checkbox("Pseudo-PAL",&clockSel)) { + altered=true; + } + + if (ImGui::Checkbox("Broken pitch macro/slides (compatibility)",&brokenPitch)) { + altered=true; + } + + if (altered) { + e->lockSave([&]() { + flags.set("clockSel",(int)clockSel); + flags.set("brokenPitch",brokenPitch); }); } break; diff --git a/src/main.cpp b/src/main.cpp index 6cc6b803d..87b36f668 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,6 +167,7 @@ TAParamResult pVersion(String) { printf("- SDL2 by Sam Lantinga (zlib license)\n"); printf("- zlib by Jean-loup Gailly and Mark Adler (zlib license)\n"); printf("- PortAudio (PortAudio license)\n"); + printf("- Weak-JACK by x42 (GPLv2)\n"); printf("- RtMidi by Gary P. Scavone (RtMidi license)\n"); printf("- backward-cpp by Google (MIT)\n"); printf("- Dear ImGui by Omar Cornut (MIT)\n");