diff -Nru zynaddsubfx-3.0.3/AUTHORS.txt zynaddsubfx-3.0.4/AUTHORS.txt --- zynaddsubfx-3.0.3/AUTHORS.txt 2015-11-17 20:35:09.000000000 +0000 +++ zynaddsubfx-3.0.4/AUTHORS.txt 2019-03-10 16:16:42.000000000 +0000 @@ -1,11 +1,13 @@ -Main author: - Nasca Octavian Paul - Active Developers: - Mark McCurry + Mark McCurry (Realtime compat, UI rewrite) Hans Petter Selasky (BSD Compat) Christopher Oliver (Unison + presets fix, mousewheel support, SUBnote overtones, unison enhancements, ...) + Johannes Lorenz (Effect Documentation, PID Files, parameter metadata) + Daniel Sheeler + +Original author (inactive): + Nasca Octavian Paul Contributors: Gerald Folcher (legato, mono notes memory) @@ -25,9 +27,9 @@ Lieven Moors (Spike/Circle waveform) Olaf Schulz (MIDI Aftertouch support) Jonathan Liles (NSM & NTK support) - Johannes Lorenz (Effect Documentation, PID Files) Ilario Glasgo (Italian Doc Translation) Filipe Coelho (Globals Cleanup) Andre Sklenar (UI Pixmaps) Harald Hvaal (General Code Modernization) Olivier Jolly (DSSI Bank Load Fix) + Micky Yun Chan (MXML 3.0 compatibility fix) diff -Nru zynaddsubfx-3.0.3/cmake/BashCompletion.cmake zynaddsubfx-3.0.4/cmake/BashCompletion.cmake --- zynaddsubfx-3.0.3/cmake/BashCompletion.cmake 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/cmake/BashCompletion.cmake 2019-02-23 15:43:37.000000000 +0000 @@ -0,0 +1,92 @@ +# A wrapper around pkg-config-provided and cmake-provided bash completion that +# will have dynamic behavior at INSTALL() time to allow both root-level +# INSTALL() as well as user-level INSTALL(). +# +# See also https://github.com/scop/bash-completion +# +# Copyright (c) 2018, Tres Finocchiaro, +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# Usage: +# INCLUDE(BashCompletion) +# BASHCOMP_INSTALL(foo) +# ... where "foo" is a shell script adjacent to the CMakeLists.txt +# +# How it determines BASHCOMP_PKG_PATH, in order: +# 1. Uses BASHCOMP_PKG_PATH if already set (e.g. -DBASHCOMP_PKG_PATH=...) +# a. If not, uses pkg-config's PKG_CHECK_MODULES to determine path +# b. Fallback to cmake's FIND_PACKAGE(bash-completion) path +# c. Fallback to hard-coded /usr/share/bash-completion/completions +# 2. Final fallback to ${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions if +# detected path is unwritable. + +# - Windows does not support bash completion +# - macOS support should eventually be added for Homebrew (TODO) +IF(WIN32) + MESSAGE(STATUS "Bash competion is not supported on this platform.") +ELSEIF(APPLE) + MESSAGE(STATUS "Bash completion is not yet implemented for this platform.") +ELSE() + INCLUDE(FindUnixCommands) + # Honor manual override if provided + IF(NOT BASHCOMP_PKG_PATH) + # First, use pkg-config, which is the most reliable + FIND_PACKAGE(PkgConfig QUIET) + IF(PKGCONFIG_FOUND) + PKG_CHECK_MODULES(BASH_COMPLETION bash-completion) + PKG_GET_VARIABLE(BASHCOMP_PKG_PATH bash-completion completionsdir) + ELSE() + # Second, use cmake (preferred but less common) + FIND_PACKAGE(bash-completion QUIET) + IF(BASH_COMPLETION_FOUND) + SET(BASHCOMP_PKG_PATH "${BASH_COMPLETION_COMPLETIONSDIR}") + ENDIF() + ENDIF() + + # Third, use a hard-coded fallback value + IF("${BASHCOMP_PKG_PATH}" STREQUAL "") + SET(BASHCOMP_PKG_PATH "/usr/share/bash-completion/completions") + ENDIF() + ENDIF() + + # Always provide a fallback for non-root INSTALL() + SET(BASHCOMP_USER_PATH "${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions") + + # Cmake doesn't allow easy use of conditional logic at INSTALL() time + # this is a problem because ${BASHCOMP_PKG_PATH} may not be writable and we + # need sane fallback behavior for bundled INSTALL() (e.g. .AppImage, etc). + # + # The reason this can't be detected by cmake is that it's fairly common to + # run "cmake" as a one user (i.e. non-root) and "make install" as another user + # (i.e. root). + # + # - Creates a script called "install_${SCRIPT_NAME}_completion.sh" into the + # working binary directory and invokes this script at install. + # - Script handles INSTALL()-time conditional logic for sane ballback behavior + # when ${BASHCOMP_PKG_PATH} is unwritable (i.e. non-root); Something cmake + # can't handle on its own at INSTALL() time) + MACRO(BASHCOMP_INSTALL SCRIPT_NAME) + # A shell script for wrapping conditionl logic + SET(BASHCOMP_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_${SCRIPT_NAME}_completion.sh") + + FILE(WRITE ${BASHCOMP_SCRIPT} "\ +#!${BASH}\n\ +set -e\n\ +BASHCOMP_PKG_PATH=\"${BASHCOMP_USER_PATH}\"\n\ +if [ -w \"${BASHCOMP_PKG_PATH}\" ]; then\n\ + BASHCOMP_PKG_PATH=\"${BASHCOMP_PKG_PATH}\"\n\ +fi\n\ +echo -e \"\\nInstalling bash completion...\\n\"\n\ +mkdir -p \"\$BASHCOMP_PKG_PATH\"\n\ +cp \"${CMAKE_CURRENT_BINARY_DIR}/${SCRIPT_NAME}\" \"\$BASHCOMP_PKG_PATH\"\n\ +chmod a+r \"\$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\ +echo -e \"Bash completion for ${SCRIPT_NAME} has been installed to \$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\ +") + INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x \"install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )") + INSTALL(CODE "EXECUTE_PROCESS(COMMAND \"./install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )") + + MESSAGE(STATUS "Bash completion script for ${SCRIPT_NAME} will be installed to ${BASHCOMP_PKG_PATH} or fallback to ${BASHCOMP_USER_PATH} if unwritable.") + ENDMACRO() +ENDIF() + diff -Nru zynaddsubfx-3.0.3/CMakeLists.txt zynaddsubfx-3.0.4/CMakeLists.txt --- zynaddsubfx-3.0.3/CMakeLists.txt 2017-10-15 01:54:51.000000000 +0000 +++ zynaddsubfx-3.0.4/CMakeLists.txt 2019-03-10 16:32:24.000000000 +0000 @@ -3,7 +3,7 @@ project(zynaddsubfx) set(VERSION_MAJOR "3") set(VERSION_MINOR "0") -set(VERSION_REVISION "3") +set(VERSION_REVISION "4") #Set data directory, if any if(DEFINED ZYN_DATADIR) @@ -38,7 +38,7 @@ install(FILES AUTHORS.txt COPYING HISTORY.txt README.adoc DESTINATION share/doc/zynaddsubfx ) -install(FILES zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop zynaddsubfx-oss.desktop +install(FILES zynaddsubfx-jack-multi.desktop zynaddsubfx-jack.desktop zynaddsubfx-alsa.desktop zynaddsubfx-oss.desktop DESTINATION share/applications) install(FILES zynaddsubfx.svg DESTINATION share/pixmaps) diff -Nru zynaddsubfx-3.0.3/debian/changelog zynaddsubfx-3.0.4/debian/changelog --- zynaddsubfx-3.0.3/debian/changelog 2017-12-06 09:36:01.000000000 +0000 +++ zynaddsubfx-3.0.4/debian/changelog 2019-03-10 23:14:11.000000000 +0000 @@ -1,3 +1,9 @@ +zynaddsubfx (2:3.0.4-1kxstudio1) trusty; urgency=medium + + * Update + + -- falkTX Mon, 11 Mar 2019 00:14:11 +0100 + zynaddsubfx (2:3.0.3-1kxstudio1) trusty; urgency=medium * Update diff -Nru zynaddsubfx-3.0.3/doc/bash-completion/CMakeLists.txt zynaddsubfx-3.0.4/doc/bash-completion/CMakeLists.txt --- zynaddsubfx-3.0.3/doc/bash-completion/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/bash-completion/CMakeLists.txt 2019-02-23 15:43:37.000000000 +0000 @@ -0,0 +1,6 @@ +INCLUDE(BashCompletion) +IF(COMMAND BASHCOMP_INSTALL) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/zynaddsubfx.in + ${CMAKE_CURRENT_BINARY_DIR}/zynaddsubfx @ONLY) + BASHCOMP_INSTALL(zynaddsubfx) +ENDIF() diff -Nru zynaddsubfx-3.0.3/doc/bash-completion/zynaddsubfx.in zynaddsubfx-3.0.4/doc/bash-completion/zynaddsubfx.in --- zynaddsubfx-3.0.3/doc/bash-completion/zynaddsubfx.in 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/bash-completion/zynaddsubfx.in 2019-02-23 15:43:37.000000000 +0000 @@ -0,0 +1,296 @@ +# zynaddsubfx(1) completion -*- shell-script -*- + +_zynaddsubfx_array_contains () +{ + local e match="$1" + shift + for e; do [[ "$e" == "$match" ]] && return 0; done + return 1 +} + +_zynaddsubfx_long_param_of() +{ + case "$1" in + -l) + echo "load" + ;; + -L) + echo "load-instrument" + ;; + -M) + echo "midi-learn" + ;; + -r) + echo "sample-rate" + ;; + -b) + echo "buffer-size" + ;; + -o) + echo "oscil-size" + ;; + -S) + echo "swap" + ;; + -U) + echo "no-gui" + ;; + -N) + echo "named" + ;; + -a) + echo "auto-connect" + ;; + -A) + echo "auto-save" + ;; + -p) + echo "pid-in-client" + ;; + -P) + echo "preferred-port" + ;; + -O) + echo "output" + ;; + -I) + echo "input" + ;; + -e) + echo "exec-after-init" + ;; + -d) + echo "dump-oscdoc" + ;; + -D) + echo "dump-json-schema" + ;; + *) + echo "" + ;; + esac +} + +_zynaddsubfx() +{ + local cword=$COMP_CWORD + local cur="${COMP_WORDS[COMP_CWORD]}" + local params filetypes pars shortargs + + # call routine provided by bash-completion + _init_completion || return + + # pars without args + pars=(--help --version --auto-connect --no-gui --swap) + pars+=(--pid-in-client-name) + # pars with args + pars+=(--load --load-instrument --midi-learn) + pars+=(--sample-rate --buffer-size --oscil-size) + pars+=(--named --auto-save) + pars+=(--preferred-port --output --input) + pars+=(--exec-after-init --dump-oscdoc --dump-json-schema) + + shortargs=(-h -v -l -L -M -r -b -o -S -U -N -a -A -p -P -O -I -e -d -D) + + local prev= + if [ "$cword" -gt 1 ] + then + prev=${COMP_WORDS[cword-1]} + fi + + # don't show shortargs, but complete them when entered + if [[ $cur =~ ^-[^-]$ ]] + then + if _zynaddsubfx_array_contains "$cur" "${shortargs[@]}" + then + COMPREPLY=( "$cur" ) + fi + return + fi + + + local filemode + + # + # please keep those in order like def_pars_args above + # + case $prev in + --load|-l) + filetypes=xmz + filemode=existing_files + ;; + --load-instrument|-L) + filetypes=xiz + filemode=existing_files + ;; + --midi-learn|-M) + filetypes=xlz + filemode=existing_files + ;; + --sample-rate|-r) + params="8000 16000 44100 48000 96000 192000" + ;; + --buffer-size|-b) + # less than 32 sounds insane, more than 4096 observed buggy + params="32 64 128 256 512 1024 2048 4096" + ;; + --oscil-size|-o) + params="128 256 512 1024 2048 4096" + ;; + --named|-N) + ;; + --auto-save|-A) + params="30 60 120 180 300 450 600 900 1200 1800 2400 3600 " + params+="5400 7200 10800 14400 21600 43200 86400" + ;; + --preferred-port|-P) + params= + ;; + --input|--output|-I|-O) + local jack_enable_cmake=@JackEnable@ + local pa_enable_cmake=@PaEnable@ + local alsa_enable_cmake=@AlsaEnable@ + local oss_enable_cmake=@OssEnable@ + + local jack_enable pa_enable alsa_enable oss_enable + [[ $jack_enable_cmake =~ FALSE|0|OFF|^$ ]] || jack_enable=1 + [[ $pa_enable_cmake =~ FALSE|0|OFF|^$ ]] || pa_enable=1 + [[ $alsa_enable_cmake =~ FALSE|0|OFF|^$ ]] || alsa_enable=1 + [[ $oss_enable_cmake =~ FALSE|0|OFF|^$ ]] || oss_enable=1 + + params="null" + [ "$jack_enable" ] && params+=" jack" + [ "$pa_enable" ] && params+=" pa" + [ "$alsa_enable" ] && params+=" alsa" + [ "$oss_enable" ] && params+=" oss" + if [[ $prev =~ --output|-O ]] + then + [ "$jack_enable" ] && params+=" jack-multi" + [ "$oss_enable" ] && params+=" oss-multi" + fi + + if [[ $prev =~ --output|-O ]] + then + for i in "${COMPREPLY[@]}" + do + if [ "$i" == '-a' ] || [ "$i" == '--autoconnect' ] + then + params="jack jack-multi" + break; + fi + done + fi + ;; + --exec-after-init|-e) + filemode=executables + ;; + --dump-oscdoc|-d) + filemode=files + filetypes=xml + ;; + --dump-json-schema|-D) + filemode=files + filetypes=json + ;; + *) + if [[ $prev =~ --help|-h|-version|-v ]] + then + # the -e flag (from --import) and help/version + # always mark the end of arguments + return + fi + + # add pars to params, but also check the history of comp words + local param + for param in "${pars[@]}" + do + local do_append=1 + for i in "${!COMP_WORDS[@]}" + do + if [ "$i" -ne 0 ] && [ "$i" -ne "$cword" ] + then + # disallow double long parameters + if [ "${COMP_WORDS[$i]}" == "$param" ] + then + do_append= + # disallow double short parameters + elif [ "${COMP_WORDS[$i]:0:1}" == '-' ] && ! [ "${COMP_WORDS[$i]:1:2}" == '-' ] && + [ "--$(_zynaddsubfx_long_param_of "${COMP_WORDS[$i]}")" == "$param" ] + then + do_append= + # --help or --version must be the first parameters + elif [ "$cword" -gt 1 ] && [[ $param =~ --help|--version ]] + then + do_append= + fi + fi + done + if [ "$do_append" ] + then + params+="$param " + fi + done + + ;; + esac + + + case $filemode in + + # use completion routine provided by bash-completion + # to fill $COMPREPLY + + existing_files) + _filedir "@($filetypes)" + ;; + + existing_directories) + _filedir -d + ;; + + executables) + + _filedir + local tmp=("${COMPREPLY[@]}") + COMPREPLY=( ) + for i in "${!tmp[@]}" + do + if [ -f "${tmp[i]}" ] && ! [ -x "${tmp[i]}" ] + then + # if it's a file that can't be executed, omit from completion + true + else + COMPREPLY+=( "${tmp[i]}" ) + fi + done + ;; + + files) + + # non existing files complete like directories... + _filedir -d + + # ...except for non-completing files with the right file type + if [ ${#COMPREPLY[@]} -eq 0 ] + then + if ! [[ "$cur" =~ /$ ]] && [ "$filetypes" ] && [[ "$cur" =~ \.($filetypes)$ ]] + then + # file ending fits, we seem to be done + COMPREPLY=( "$cur" ) + fi + fi + ;; + + esac + + if [ "$params" ] + then + # none of our parameters contain spaces, so deactivate shellcheck's warning + # shellcheck disable=SC2207 + COMPREPLY+=( $(compgen -W "${params}" -- "${cur}") ) + fi + +} + + +complete -F _zynaddsubfx zynaddsubfx diff -Nru zynaddsubfx-3.0.3/doc/CMakeLists.txt zynaddsubfx-3.0.4/doc/CMakeLists.txt --- zynaddsubfx-3.0.3/doc/CMakeLists.txt 2017-02-24 00:18:06.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/CMakeLists.txt 2019-02-23 15:43:37.000000000 +0000 @@ -13,3 +13,6 @@ COMMENT "Generating API documentation with Doxygen" VERBATIM) add_custom_target(doc DEPENDS html) endif() + +add_subdirectory(bash-completion) + diff -Nru zynaddsubfx-3.0.3/doc/intro.txt zynaddsubfx-3.0.4/doc/intro.txt --- zynaddsubfx-3.0.3/doc/intro.txt 2015-10-23 14:47:05.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/intro.txt 2018-04-07 03:39:29.000000000 +0000 @@ -14,7 +14,9 @@ ------------------------------ This sets the input driver to be alsa and the output driver to be jack, which -should attempt to autoconnect to your soundcard as per the '-a' flag. +should attempt to autoconnect to your soundcard as per the '-a' flag. For more +details, see the <> chapter. + If this is your first time running ZynAddSubFX, you will see a screen that lets you choose between the advanced and beginner interface. Currently the beginner interface is deprecated, so the advanced one is diff -Nru zynaddsubfx-3.0.3/doc/Makefile zynaddsubfx-3.0.4/doc/Makefile --- zynaddsubfx-3.0.3/doc/Makefile 2015-10-23 14:47:05.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/Makefile 2018-04-07 03:39:29.000000000 +0000 @@ -8,32 +8,26 @@ xhtml: zynaddsubfx.txt envelope.txt intro.txt lfo.txt - asciidoc -a numbered -a toc -b html5 \ + asciidoctor -a numbered -a toc -b html5 \ -a latexmath \ zynaddsubfx.txt chunked: - asciidoc --doctype=article -b docbook \ - zynaddsubfx.txt rm -rf ./zynaddsubfx.chunked mkdir ./zynaddsubfx.chunked rm -f "./zynaddsubfx.chunked/*.html" ln -s ../images/ zynaddsubfx.chunked/images - xsltproc --stringparam html.stylesheet "./docbook-xsl.css" \ - --stringparam callout.graphics 0 \ - --stringparam navig.graphics 0 \ - --stringparam admon.textlabel 1 \ - --stringparam admon.graphics 0 \ - --nonet \ - --stringparam base.dir "zynaddsubfx.chunked/" \ - "/etc/asciidoc/docbook-xsl/chunked.xsl" \ - "./zynaddsubfx.xml" \ + cp *.txt zynaddsubfx.chunked/ + @curl https://raw.githubusercontent.com/asciidoctor/asciidoctor-extensions-lab/master/lib/multipage-html5-converter.rb -o ./zynaddsubfx.chunked/multipage-html5-converter.rb + cd ./zynaddsubfx.chunked; asciidoctor -r ./multipage-html5-converter.rb -b multipage_html5 zynaddsubfx.txt + ln -s zynaddsubfx-chunked.html ./zynaddsubfx.chunked/index.html + rm ./zynaddsubfx.chunked/multipage-html5-converter.rb ./zynaddsubfx.chunked/*.txt pdf: - a2x -f pdf zynaddsubfx.txt + asciidoctor-pdf zynaddsubfx.txt man: zynaddsubfx.1.txt - a2x -f manpage zynaddsubfx.1.txt + asciidoctor -b manpage zynaddsubfx.1.txt clean: rm -f *~ *html *pdf *xml *tex *log zynaddsubfx.chunked/images diff -Nru zynaddsubfx-3.0.3/doc/output.txt zynaddsubfx-3.0.4/doc/output.txt --- zynaddsubfx-3.0.3/doc/output.txt 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/output.txt 2018-04-07 03:39:29.000000000 +0000 @@ -0,0 +1,49 @@ +Output engines +-------------- + +There are different output engines for zynaddsubfx. + +ALSA +~~~~ +Simply start zyn using `zynaddsubfx -O alsa`. + +NOTE: If your system is using PulseAudio, this may not work. Even if it does, +and ALSA redirects to PulseAudio, realtime guarantees can not be made. It is +recommended to follow the steps in the <> section. + +Jack +~~~~ +NOTE: If your system is using PulseAudio, this is the suggested way, but may +not work out of the box, or it may not do what you want. Please seee the +<> section. + +There are different ways to forward zyn to jack: + +* Automatic connecting with `zynaddsfubx -O jack -a` will usually do what you + want +* Manual selective connecting can be done by using `zynaddsfubx -O jack` and + then using one of + - the `qjackctl` gui + - the `jack_connect` commandline utility +* Automatic selective connecting can be done using + http://www.rncbc.org/drupal/node/76[jack's patchbays] + +PulseAudio +~~~~~~~~~~ +PulseAudio is *not* **directly** implemented in zyn. While PulseAudio can be +really practical in many cases, it has no realtime guarantees. Running zyn +right through pulse is thus not intended. In case you want to keep +PulseAudio and still use zyn, the suggested way is to route PulseAudio through +jack. You need to + +* Install jack2 +* Route pulse through jack2 using cadence + +These steps should be described for your distribution. Check out one of the +following links. + +* https://wiki.archlinux.org/index.php/PulseAudio/Examples#The_KXStudio_method[Arch Linux] +* please add more here ... + +After that, follow the usual steps described under <>. + diff -Nru zynaddsubfx-3.0.3/doc/zynaddsubfx.txt zynaddsubfx-3.0.4/doc/zynaddsubfx.txt --- zynaddsubfx-3.0.3/doc/zynaddsubfx.txt 2015-10-23 14:47:05.000000000 +0000 +++ zynaddsubfx-3.0.4/doc/zynaddsubfx.txt 2018-04-07 03:39:29.000000000 +0000 @@ -6,6 +6,8 @@ include::intro.txt[] +include::output.txt[] + include::filter.txt[] include::lfo.txt[] Binary files /tmp/tmpNLpS6d/Tm9RmHRRSp/zynaddsubfx-3.0.3/doc/zyn-fusion-add.png and /tmp/tmpNLpS6d/Dw2c8kyUJ_/zynaddsubfx-3.0.4/doc/zyn-fusion-add.png differ diff -Nru zynaddsubfx-3.0.3/.gitignore zynaddsubfx-3.0.4/.gitignore --- zynaddsubfx-3.0.3/.gitignore 2015-10-23 14:47:05.000000000 +0000 +++ zynaddsubfx-3.0.4/.gitignore 2018-04-07 03:39:29.000000000 +0000 @@ -1,9 +1,17 @@ *.o *~ *.bc +*.swp +*.config +*.creator +*.creator.user +*.files +*.includes src/zynaddsubfx src/Tests/runner src/Tests/runner.cpp CMakeCache.txt CMakeFiles build +doc/gen/ +doc/zynaddsubfx.html diff -Nru zynaddsubfx-3.0.3/.gitmodules zynaddsubfx-3.0.4/.gitmodules --- zynaddsubfx-3.0.3/.gitmodules 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/.gitmodules 2019-02-23 15:43:37.000000000 +0000 @@ -1,10 +1,9 @@ [submodule "instruments"] path = instruments - url = git://git.code.sf.net/p/zynaddsubfx/instruments + url = https://github.com/zynaddsubfx/instruments [submodule "rtosc"] path = rtosc url = https://github.com/fundamental/rtosc - branch = default_values [submodule "DPF"] path = DPF - url = git://github.com/DISTRHO/DPF + url = https://github.com/DISTRHO/DPF diff -Nru zynaddsubfx-3.0.3/HISTORY.txt zynaddsubfx-3.0.4/HISTORY.txt --- zynaddsubfx-3.0.3/HISTORY.txt 2017-12-05 01:10:52.000000000 +0000 +++ zynaddsubfx-3.0.4/HISTORY.txt 2019-03-10 16:37:31.000000000 +0000 @@ -1,3 +1,19 @@ +3.0.4 (10 March 2019) + - Add scripting to zyn-fusion UI for automated screen capture + - Add double click to reset sliders + - Add random LFO to UI + - Add compatibility with MXML 3.0 + - Enhance modulation volume parameter resolution + - Enhance global volume parameter resolution + - Enhance state variable filter interpolation + - Change filter defaults for easier patch setup + - Fix pops with note aftertouch + - Fix pad synth export + - Fix envelope watchpoint visualization + - Fix .xlz load/save in fusion UI + - Fix minor bugs + - Added support for floating point notes via MIDI SYSEX. + 3.0.3 (4 December 2017) - Add bank search to FLTK/NTK GUI - Add parallel padsynth sample generation Binary files /tmp/tmpNLpS6d/Tm9RmHRRSp/zynaddsubfx-3.0.3/instruments/banks/Arpeggios/0006-Aporggio6.xiz and /tmp/tmpNLpS6d/Dw2c8kyUJ_/zynaddsubfx-3.0.4/instruments/banks/Arpeggios/0006-Aporggio6.xiz differ Binary files /tmp/tmpNLpS6d/Tm9RmHRRSp/zynaddsubfx-3.0.3/instruments/banks/Arpeggios/0006-Arpeggio6.xiz and /tmp/tmpNLpS6d/Dw2c8kyUJ_/zynaddsubfx-3.0.4/instruments/banks/Arpeggios/0006-Arpeggio6.xiz differ diff -Nru zynaddsubfx-3.0.3/README.adoc zynaddsubfx-3.0.4/README.adoc --- zynaddsubfx-3.0.3/README.adoc 2016-11-16 00:57:00.000000000 +0000 +++ zynaddsubfx-3.0.4/README.adoc 2018-04-07 03:39:29.000000000 +0000 @@ -3,73 +3,79 @@ image::https://travis-ci.org/zynaddsubfx/zynaddsubfx.svg?branch=master[alt="Build status", link="https://travis-ci.org/zynaddsubfx/zynaddsubfx"] -It is a feature heavy realtime software synthesizer for Linux, OSX, -and Windows. +ZynAddSubFX is a fully featured musical software synthesizer for Linux, MacOS, +BSD, and Windows. +ZynAddSubFX exposes a wide array of synthesis parameters to make it flexible +tool for sound design and a fun experience for playing instruments. + +image::doc/zyn-fusion-add.png[Zyn-Fusion Add Synth Editor] + +Features +~~~~~~~~ + +* Polyphonic with support for legato and mono playing modes +* Three synthesizer engines: +** Additive Synthesis for classic synth sounds composed of a variety of voices with powerful modulation. This exposes modulators ranging from LFOs and envelopes to oscillator modulators for FM, PM, and AM. +** Subtractive Synthesis for creating variable bandwidth harmonics from filtered white noise +** PAD synthesis for creating beautiful pads and other instruments +* Powerful waveform generator with up to 128 sine/non-sine harmonics +* A variety of filters including analogue modeled filters, formant filters, and state variable filters. +* Envelopes can have ADSR (or ASR, etc..) modes or can be free modes (with any shape) +* Effects for Reverb, Echo, Chorus/Flange, Phasing, Wave-shaping, Equalizing, Dynamic Filtering with flexible signal routing +* Instruments can be organized in kits, which allows you to make drum kits or layered instruments; this makes possible to use more than one instrument for a single part. It is possible to choose what items from the kit should be processed by the Part's effects. +* Randomness settings to create subtle differences in each sound to help create that familiar analogue warmth. +* Microtonal capabilities with any scale, up to 128 notes per octave, and key mapping +* Extensive MIDI/Audio driver support including JACK, ALSA, OSS, and PortAudio +* Session Management Support via LASH/NSM +* Plugin Support via DSSI/LV2/VST +* Over 1100 high quality instruments included + + +For more information see: + +- The project page: http://zynaddsubfx.sf.net +- The mailing list: http://lists.sourceforge.net/mailman/listinfo/zynaddsubfx-user +- The public forum: http://www.kvraudio.com/forum/viewforum.php?f=47 +- The IRC channel: ##zynaddsubfx on freenode -Originally written by Nasca Octavian Paul - -Maintained by Mark McCurry 2009+. - -ZynAddSubFX is free program and is distributed WITH NO WARRANTY. -It is licensed under GNU General Public License version 2+. +Dependencies +~~~~~~~~~~~~ -The project page is -http://sourceforge.net/projects/zynaddsubfx -or -http://zynaddsubfx.sourceforge.net - -Mailing List: -http://lists.sourceforge.net/mailman/listinfo/zynaddsubfx-user +ZynAddSubFX depends on a number of dependencies for building. +For more information on building the core along with the FLTK based interface +see doc/building.txt and for building the new interface see +https://github.com/zynaddsubfx/zyn-fusion-build -Public Forum: -http://www.kvraudio.com/forum/viewforum.php?f=47 +Required: -Requirements: -~~~~~~~~~~~~~ - FFTW 3.x.x - necessary for Fast Fourier computations -- MXML-2.5+ from www.minixml.org +- MXML-2.5+ - from www.minixml.org - zlib library from http://www.zlib.org -- Jack Audio (may be optional in the future) +- Liblo - networked open sound control Optional: -~~~~~~~~~ -- FLTK 1.x.x (tested with fltk 1.3+) -- a VST host for the VST version [For more information see: - http://www.kvraudio.com/forum/viewtopic.php?t=268277&sid=95be0b6c9909300d566006428bcbb97d] -Compilation: -~~~~~~~~~~~~ - For the main program see doc/build.txt. - To compile the Spliter, run "make" from the "Spliter" directory. - To compile the Controller, run "make" from the "Controller" directory. - -Running on Linux -~~~~~~~~~~~~~~~~ -Under linux there are several options for both audio output and MIDI input. -Defaults are set at compile time and the desired backend can be set when -starting ZynAddSubFX with the '-I' and '-O' options. -The currently supported backends are: - -- Audio Output - * ALSA (Advanced Linux Sound Architecture) - * OSS (Open Sound System) - * JACK (JACK Audio Connection Kit) - * Port Audio - -- MIDI Input - * ALSA - * OSS - * JACK - -Running on OSX -~~~~~~~~~~~~~~ -Under OSX simply download the binary available on http://sourceforge.net/projects/zynaddsubfx -and run with JACK - -Running on Windows -~~~~~~~~~~~~~~~~~~ -NOTE: At this time only older versions of ZynAddSubFX were compiled on windows - See older versions on sf.net (ie version 2.2.1) +- FLTK (for the old user interface) +- NTK (for the old user interface) +- JACK +- OSS +- ALSA +- CxxTest (for unit tests) +- LASH +- DSSI + +Sibling projects: + +- rtosc - realtime open sound control https://github.com/fundamental/rtosc +- mruby-zest - the framework for the zyn-fusion GUI + https://github.com/mruby-zest/mruby-zest + + +License +~~~~~~~ + +ZynAddSubFX is available under the GPLv2+ license. Have fun! :-) + --The ZynAddSubFX team diff -Nru zynaddsubfx-3.0.3/rtosc/CMakeLists.txt zynaddsubfx-3.0.4/rtosc/CMakeLists.txt --- zynaddsubfx-3.0.3/rtosc/CMakeLists.txt 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/CMakeLists.txt 2019-03-03 15:19:10.000000000 +0000 @@ -6,6 +6,8 @@ set(VERSION_PATCH 0) option(PERF_TEST "Run Performance Timing Tests" OFF) +option(RTOSC_INCLUDE_WHAT_YOU_USE "Check for useless includes" OFF) +mark_as_advanced(FORCE RTOSC_INCLUDE_WHAT_YOU_USE) include("cmake/ColorMessage.cmake") SET(BUILD_RTOSC_EXAMPLES FALSE CACHE BOOL @@ -46,12 +48,27 @@ endif() endif() +if(${CMAKE_VERSION} VERSION_LESS "3.3.0") + set(IWYU_ERR "disabled (cmake < 3.3.0)") +else() + if(RTOSC_INCLUDE_WHAT_YOU_USE) + find_program(RTOSC_IWYU_PATH NAMES include-what-you-use iwyu) + if(NOT RTOSC_IWYU_PATH) + set(IWYU_ERR "package NOT found") + endif() + else() + set(IWYU_ERR "disabled (RTOSC_INCLUDE_WHAT_YOU_USE=OFF)") + endif() +endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_FLAGS} ${CXX11_FLAG}") add_library(rtosc STATIC src/rtosc.c src/dispatch.c src/pretty-format.c - src/arg-val-math.c src/rtosc-time.c + src/arg-val-math.c src/arg-val-cmp.c src/rtosc-time.c src/util.c ${CMAKE_CURRENT_BINARY_DIR}/version.c) -add_library(rtosc-cpp STATIC src/cpp/ports.cpp src/cpp/miditable.cpp +add_library(rtosc-cpp STATIC src/cpp/ports.cpp src/cpp/ports-runtime.cpp + src/cpp/default-value.cpp src/cpp/savefile.cpp + src/cpp/miditable.cpp src/cpp/automations.cpp src/cpp/midimapper.cpp src/cpp/thread-link.cpp @@ -59,6 +76,20 @@ src/cpp/subtree-serialize.cpp) target_link_libraries(rtosc-cpp rtosc) +if(IWYU_ERR) + message (STATUS "Include what you use: ${IWYU_ERR}") +else() + set(RTOSC_IWYU_PATH_AND_OPTIONS + ${RTOSC_IWYU_PATH} + -Xiwyu + --no_comments) + set_property(TARGET rtosc PROPERTY C_INCLUDE_WHAT_YOU_USE + ${RTOSC_IWYU_PATH_AND_OPTIONS}) + set_property(TARGET rtosc-cpp PROPERTY CXX_INCLUDE_WHAT_YOU_USE + ${RTOSC_IWYU_PATH_AND_OPTIONS}) +endif() + + # Example Code find_package(FLTK) if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")) @@ -132,8 +163,10 @@ endmacro(maketestcpp) maketest(version) +maketest(util) maketest(osc-spec) -maketest(cmp-arg-vals) +maketest(arg-val-cmp) +maketest(arg-val-math) maketest(pretty-format) maketest(simple-messages) maketest(null-messages) @@ -145,6 +178,7 @@ maketest(message-alignment) maketest(test-arg-iter) if(LIBLO_FOUND) + add_definitions(-DHAVE_LIBLO) include_directories(${LIBLO_INCLUDE_DIRS}) maketest(liblo) target_link_libraries(liblo ${LIBLO_LDFLAGS}) @@ -156,12 +190,15 @@ maketestcpp(test-midi-mapper) maketestcpp(metadata) -maketestcpp(default-values) +maketestcpp(default-value) maketestcpp(headerlib) maketestcpp(test-walker) if(PERF_TEST) maketestcpp(performance) + if(LIBLO_FOUND) + target_link_libraries(performance ${LIBLO_LDFLAGS}) + endif() maketestcpp(serializer) endif(PERF_TEST) @@ -175,6 +212,19 @@ maketestcpp(test-automation) +find_package(Ruby 1.8) +if(LIBLO_FOUND AND RUBY_FOUND) + add_executable(port-checker test/port-checker.cpp test/port-checker-main.cpp) + target_link_libraries(port-checker rtosc-cpp rtosc ${LIBLO_LDFLAGS}) + add_executable(port-checker-tester test/port-checker.cpp test/port-checker-tester.cpp) + target_link_libraries(port-checker-tester rtosc-cpp rtosc ${LIBLO_LDFLAGS}) + add_executable(port-checker-testapp test/port-checker-testapp.cpp) + target_link_libraries(port-checker-testapp rtosc-cpp rtosc ${LIBLO_LDFLAGS}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/test-port-checker.rb + ${CMAKE_CURRENT_BINARY_DIR}/test-port-checker.rb COPYONLY) + add_test(test-port-checker test-port-checker.rb) +endif() + # Documentation find_package(Doxygen) if(DOXYGEN_FOUND) diff -Nru zynaddsubfx-3.0.3/rtosc/completions/port-checker zynaddsubfx-3.0.4/rtosc/completions/port-checker --- zynaddsubfx-3.0.3/rtosc/completions/port-checker 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/completions/port-checker 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,32 @@ +__port_checker_get_ports() +{ + local mtool=ss + which $mtool >/dev/null 2>/dev/null || mtool=netstat + which $mtool >/dev/null 2>/dev/null || mtool= + + if [ "$mtool" ] + then + # ports must be of local, and should not use + # reserved port names (like 'ntp') + $mtool -lu | + grep -o '\(127.0.0.1\|0.0.0.0\):[0-9]\+' | + sed 's/.*://' | tr '\n' ' ' + fi +} + +_port_checker() +{ + local cword=$COMP_CWORD + local cur=${COMP_WORDS[COMP_CWORD]} + local params= + + if [ "$cword" == 1 ] + then + params="--help $(__port_checker_get_ports)" + fi + + COMPREPLY=( $(compgen -W "${params}" -- ${cur}) ) +} + + +complete -F _port_checker port-checker diff -Nru zynaddsubfx-3.0.3/rtosc/doc/Guide.adoc zynaddsubfx-3.0.4/rtosc/doc/Guide.adoc --- zynaddsubfx-3.0.3/rtosc/doc/Guide.adoc 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/doc/Guide.adoc 2019-03-03 15:19:10.000000000 +0000 @@ -440,7 +440,8 @@ #8badf00d ---------- * Timestamps are written as the UTC date in the ISO 8601 - standard (the conversions are equal to using localtime(3p) and mktime(3p) ) + standard (the conversions are equal to using `localtime(3p)` and + `mktime(3p)`) with an optional, appended time, which contains an optional 'seconds' part and an optional appended fractional part separated by a period. There are two special timestamps called 'now' and 'immediately' with the same @@ -596,12 +597,42 @@ Another situation where you may want to skip a subtree is if its data should never be saved. This can happen if the subtree ... - * contains parameters of your app that shall not be written into savefiles + +* contains parameters of your app that shall not be written into savefiles (e.g. configuration parameters from $HOME/.app) - * contains parameters that can not be accessed at all or not in reasonable time - * shall only be synchronized when certain macros are defined -In such a case, simply add the macro "rNoWalk" to the metadata of the port -containing these subports. +* contains parameters that can not be accessed at all or not in reasonable time +* shall only be synchronized when certain macros are defined + +In such a case, simply add the macro "rNoDefaults" to the metadata of the port +containing these subports (or to the metadata of a single port). + +Checking your ports +~~~~~~~~~~~~~~~~~~~ + +Once your rtosc app is coded, you can check your ports for common issues. +Requirements: + +* your app has a UDP port for OSC +* your app provides the "/path-search" port, see the doxygen comment for the + "path_search" function in ports.h +* rtosc was compiled with liblo support + +To do so, you need to + +* start your app +* extract the UDP port from it (e.g. via stdout) +* source the port-checker bash completion: `. completions/port-checker` +* start the port checker with `test/port-checker ` + +This can be automated by a ruby script, see test/check-ports.rb, which starts +a test app which is used to test the port-checker itself. + +NOTE:: In order to check all of your ports, make sure that each type of port is + being enabled in some path. +NOTE:: As the port-checker will only always take the first element of a bundle + port (e.g. only "multiple0/" of "multiple#16/"), the parent ports of any + port you want to test must be a non-bundle port or the first port of a + bundle port. Undo Support ~~~~~~~~~~~~ diff -Nru zynaddsubfx-3.0.3/rtosc/.gitignore zynaddsubfx-3.0.4/rtosc/.gitignore --- zynaddsubfx-3.0.3/rtosc/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/.gitignore 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,2 @@ +Testing/ + diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/arg-val-cmp.h zynaddsubfx-3.0.4/rtosc/include/rtosc/arg-val-cmp.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/arg-val-cmp.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/arg-val-cmp.h 2018-03-11 15:05:07.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + /** + * @file arg-val-cmp.h + * Two- and three-way comparison of argument values + * + * @test arg-val-cmp.c + */ + +#ifndef ARG_VAL_CMP +#define ARG_VAL_CMP + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * arg val comparing helpers + */ +int rtosc_arg_vals_cmp_has_next(const rtosc_arg_val_itr* litr, + const rtosc_arg_val_itr* ritr, + size_t lsize, size_t rsize); +int rtosc_arg_vals_eq_after_abort(const rtosc_arg_val_itr* litr, + const rtosc_arg_val_itr* ritr, + size_t lsize, size_t rsize); +int rtosc_arg_vals_eq_single(const rtosc_arg_val_t* _lhs, + const rtosc_arg_val_t* _rhs, + const rtosc_cmp_options* opt); +int rtosc_arg_vals_cmp_single(const rtosc_arg_val_t* _lhs, + const rtosc_arg_val_t* _rhs, + const rtosc_cmp_options* opt); + +/** + * Check if two arrays of rtosc_arg_val_t are equal + * + * @param lsize Array size of lhs, e.g. 3 if lhs is just one counting range + * @param opt Comparison options or NULL for default options + * @return One if they are equal, zero if not + */ +int rtosc_arg_vals_eq(const rtosc_arg_val_t *lhs, const rtosc_arg_val_t *rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt); + +/** + * Compare two arrays of rtosc_arg_val_t. + * Whether an argument value is less or greater than another is computed + * - using memcmp for blobs + * - using strcmp for strings and identifiers + * - using numerical comparison for all other types. + * As an exception, the timestamp "immediately" is defined to be smaller than + * every other timestamp. + * + * @param opt Comparison options or NULL for default options + * @param lsize Array size of lhs, e.g. 3 if lhs is just one counting range + * @return An integer less than, equal to, or greater than zero if lhs is found, + * respectively, to be less than, to match, or be greater than rhs. + */ +int rtosc_arg_vals_cmp(const rtosc_arg_val_t *lhs, const rtosc_arg_val_t *rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt); + +#ifdef __cplusplus +}; +#endif +#endif // ARG_VAL_CMP diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/arg-val-math.h zynaddsubfx-3.0.4/rtosc/include/rtosc/arg-val-math.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/arg-val-math.h 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/arg-val-math.h 2018-03-11 15:05:07.000000000 +0000 @@ -24,15 +24,28 @@ /** * @file arg-val-math.h + * + * Math functions for arg values, respecting their types. + * All functions must support all types in "ichfdTF". + * + * @note Chaining these functions can be inefficient: a+b+c involves (at least) + * two switch statements about the types, though one would suffice. + * Use these functions if runtime is not too critical. + * @test arg-val-math.c */ -#ifndef ARG_VAL_MATH_H -#define ARG_VAL_MATH_H +#ifndef RTOSC_ARG_VAL_MATH +#define RTOSC_ARG_VAL_MATH #include +#ifdef __cplusplus +extern "C" { +#endif + int rtosc_arg_val_null(rtosc_arg_val_t* av, char type); int rtosc_arg_val_from_int(rtosc_arg_val_t* av, char type, int number); +int rtosc_arg_val_from_double(rtosc_arg_val_t* av, char type, double number); int rtosc_arg_val_negate(rtosc_arg_val_t *av); int rtosc_arg_val_round(rtosc_arg_val_t *av); int rtosc_arg_val_add(const rtosc_arg_val_t *lhs, const rtosc_arg_val_t *rhs, @@ -49,4 +62,7 @@ rtosc_arg_val_t *rtosc_arg_val_range_arg(const rtosc_arg_val_t* range_arg, int ith, rtosc_arg_val_t *result); -#endif // ARG_VAL_MATH_H +#ifdef __cplusplus +}; +#endif +#endif // RTOSC_ARG_VAL_MATH diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/automations.h zynaddsubfx-3.0.4/rtosc/include/rtosc/automations.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/automations.h 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/automations.h 2019-03-10 16:16:45.000000000 +0000 @@ -95,7 +95,7 @@ void clearSlot(int slot_id); void clearSlotSub(int slot_id, int sub); - + void setSlotSubPath(int slot_id, int sub, const char *msg); void setSlotSubGain(int slot_id, int sub, float f); float getSlotSubGain(int slot_id, int sub); void setSlotSubOffset(int slot_id, int sub, float f); diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/bundle-foreach.h zynaddsubfx-3.0.4/rtosc/include/rtosc/bundle-foreach.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/bundle-foreach.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/bundle-foreach.h 2019-03-10 16:16:45.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * @file bundle-foreach.h + * Define the bundle_foreach template function + * @note Include this file only from cpp files + */ + +#ifndef BUNDLE_FOREACH +#define BUNDLE_FOREACH + +#include +#include +#include +#include "ports.h" + +namespace rtosc { + +/** + * Execute a callback for all bundle elements of a bundle port + * @param name Should be p.name + * TODO: pass base as pointer, so it can be null? + */ +template +void bundle_foreach(const struct Port& p, const char* name, char* old_end, + /* data which is only being used by the ftors */ + const char* name_buffer, const struct Ports& base, + void* data, void* runtime, const F& ftor, + /* options */ + bool expand_bundles = true, + bool cut_afterwards = true) +{ + char *pos = old_end; + while(*name != '#') *pos++ = *name++; + const unsigned max = atoi(name+1); + while(isdigit(*++name)) ; + + char* pos2; + + if(expand_bundles) + for(unsigned i=0; i +#include + +namespace rtosc { + +/** + * Return a port's default value + * + * Returns the default value of a given port, if any exists, as a string. + * For the parameters, see the overloaded function. + * @note The recursive parameter should never be specified. + * @return The default value(s), pretty-printed, or NULL if there is no + * valid default annotation + */ +const char* get_default_value(const char* port_name, const struct Ports& ports, + void* runtime, + const struct Port* port_hint = nullptr, + int32_t idx = -1, int recursive = 1); + +/** + * Return a port's default value + * + * Returns the default value of a given port, if any exists, as an array of + * rtosc_arg_vals . The values in the resulting array are being canonicalized, + * i.e. mapped values are being converted to integers; see + * canonicalize_arg_vals() . + * + * @param port_name the port's OSC path. + * @param port_args the port's arguments, e.g. '::i:c:S' + * @param ports the ports where @a portname is to be searched + * @param runtime object holding @a ports . Optional. Helps finding + * default values dependent on others, such as presets. + * @param port_hint The port itself corresponding to portname (including + * the args). If not specified, will be found using @p portname . + * @param idx If the port is an array (using the '#' notation), this specifies + * the index required for the default value + * @param n Size of the output parameter @res . This size can not be known, + * so you should provide a large enough array. + * @param res The output parameter for the argument values. + * @param strbuf String buffer for storing pretty printed strings and blobs. + * @param strbufsize Size of @p strbuf + * @return The actual number of aruments written to @p res (can be smaller + * than @p n) or -1 if there is no valid default annotation + */ +int get_default_value(const char* port_name, const char *port_args, + const struct Ports& ports, + void* runtime, const struct Port* port_hint, + int32_t idx, + std::size_t n, rtosc_arg_val_t* res, + char *strbuf, size_t strbufsize); + +} + +#endif // RTOSC_DEFAULT_VALUE diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/miditable.h zynaddsubfx-3.0.4/rtosc/include/rtosc/miditable.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/miditable.h 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/miditable.h 2019-03-10 16:16:45.000000000 +0000 @@ -95,7 +95,6 @@ float operator()(int x) const; }; -#include class MidiMappernRT { public: diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/ports.h zynaddsubfx-3.0.4/rtosc/include/rtosc/ports.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/ports.h 2017-11-19 17:33:55.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/ports.h 2019-03-03 15:19:10.000000000 +0000 @@ -35,12 +35,9 @@ #include #include #include -#include -#include -#include -#include -#include #include +#include +#include namespace rtosc { @@ -260,165 +257,6 @@ }; /** - * Return a port's default value - * - * Returns the default value of a given port, if any exists, as a string. - * For the parameters, see the overloaded function. - * @note The recursive parameter should never be specified. - * @return The default value(s), pretty-printed, or NULL if there is no - * valid default annotation - */ -const char* get_default_value(const char* port_name, const Ports& ports, - void* runtime, const Port* port_hint = NULL, - int32_t idx = -1, int recursive = 1); - -/** - * Return a port's default value - * - * Returns the default value of a given port, if any exists, as an array of - * rtosc_arg_vals . The values in the resulting array are being canonicalized, - * i.e. mapped values are being converted to integers; see - * canonicalize_arg_vals() . - * - * @param port_name the port's OSC path. - * @param port_args the port's arguments, e.g. '::i:c:S' - * @param ports the ports where @a portname is to be searched - * @param runtime object holding @a ports . Optional. Helps finding - * default values dependent on others, such as presets. - * @param port_hint The port itself corresponding to portname (including - * the args). If not specified, will be found using @p portname . - * @param idx If the port is an array (using the '#' notation), this specifies - * the index required for the default value - * @param n Size of the output parameter @res . This size can not be known, - * so you should provide a large enough array. - * @param res The output parameter for the argument values. - * @param strbuf String buffer for storing pretty printed strings and blobs. - * @param strbufsize Size of @p strbuf - * @return The actual number of aruments written to @p res (can be smaller - * than @p n) or -1 if there is no valid default annotation - */ -int get_default_value(const char* port_name, const char *port_args, - const Ports& ports, - void* runtime, const Port* port_hint, - int32_t idx, - size_t n, rtosc_arg_val_t* res, - char *strbuf, size_t strbufsize); - - -/** - * Return a string list of all changed values - * - * Return a human readable list of the value that changed - * corresponding to the rDefault macro - * @param ports The static ports structure - * @param runtime The runtime object - * @note This function is not realtime save (It uses std::string), which is - * usually OK, since this function is being run inside a non-RT thread. If you - * need this to be realtime save, add a template parameter for a functor that - * replaces the std::string handling. - * @return The list of ports and their changed values, linewise - */ -std::string get_changed_values(const Ports& ports, void* runtime); - -//! @brief Class to modify and dispatch messages loaded from savefiles. -//! Objects of this class shall be passed to savefile loading routines. You can -//! inherit to change the behaviour, e.g. to modify or discard such messages. -class savefile_dispatcher_t -{ - const Ports* ports; - void* runtime; - char loc[1024]; - -protected: - enum proceed { - abort = -2, //!< the message shall lead to abort the savefile loading - discard = -1 //!< the message shall not be dispatched - }; - - enum dependency_t { - no_dependencies, //! default values don't depend on others - has_dependencies, //! default values do depend on others - not_specified //! it's not know which of the other enum values fit - }; - - rtosc_version rtosc_filever, //!< rtosc versinon savefile was written with - rtosc_curver, //!< rtosc version of this library - app_filever, //!< app version savefile was written with - app_curver; //!< current app version - - //! call this to dispatch a message - bool operator()(const char* msg) { return do_dispatch(msg); } - - static int default_response(size_t nargs, bool first_round, - dependency_t dependency); - -private: - //! callback for when a message shall be dispatched - //! implement this if you need to change a message - virtual int on_dispatch(size_t portname_max, char* portname, - size_t maxargs, size_t nargs, - rtosc_arg_val_t* args, - bool round2, dependency_t dependency); - //! call this to dispatch a message - virtual bool do_dispatch(const char* msg); - - friend int dispatch_printed_messages(const char* messages, - const Ports& ports, void* runtime, - savefile_dispatcher_t *dispatcher); - - friend int load_from_file(const char* file_content, - const Ports& ports, void* runtime, - const char* appname, - rtosc_version appver, - savefile_dispatcher_t* dispatcher); -}; - -/** - * Scan OSC messages from human readable format and dispatch them. - * @param messages The OSC messages, whitespace-separated - * @param ports The static ports structure - * @param runtime The runtime object - * @param dispatcher Object to modify messages prior to dispatching, or NULL. - * You can overwrite its virtual functions, and you should specify any of the - * version structs if needed. All other members shall not be initialized. - * @return The number of messages read, or, if there was a read error, - * or the dispatcher did refuse to dispatch, - * the number of bytes read until the read error occured minus one - */ -int dispatch_printed_messages(const char* messages, - const Ports& ports, void* runtime, - savefile_dispatcher_t *dispatcher = NULL); - -/** - * Return a savefile containing all values that differ from the default values. - * @param ports The static ports structure - * @param runtime The runtime object - * @param appname Name of the application calling this function - * @param appver Version of the application calling this function - * @return The resulting savefile as an std::sting - */ -std::string save_to_file(const Ports& ports, void* runtime, - const char* appname, rtosc_version appver); - -/** - * Read save file and dispatch contained parameters. - * @param file_content The file as a C string - * @param ports The static ports structure - * @param runtime The runtime object - * @param appname Name of the application calling this function; must - * match the file's application name - * @param appver Version of the application calling this function - * @param dispatcher Modifier for the messages; NULL if no modifiers are needed - * @return The number of messages read, or, if there was a read error, - * the negated number of bytes read until the read error occured minus one - */ -int load_from_file(const char* file_content, - const Ports& ports, void* runtime, - const char* appname, - rtosc_version appver, - savefile_dispatcher_t* dispatcher = NULL); - -/** * Convert given argument values to their canonical representation. * * The ports first (or-wise) argument types are defined as canonical. @@ -488,6 +326,51 @@ void *runtime = NULL); /** + * Returns paths and metadata of all direct children of a port, or of the port + * itself if that port has no children. + * If you just want to generate a reply message, use the overloaded function. + * @param root see @p m + * @param str location under @p root to look up port, or empty-string to search + * directly at @p root + * @param needle Only port names starting with this sting are returned (use + * empty-string or nullptr to match everything) + * @param types A buffer where the OSC type string is being written + * @param max_types should be @p max_args +1 for best performance, + * see @p max_args + * @param args An array where the argument values are being written + * @param max_args maximum number of arguments in array at @p args. Should be + * greater or equal than 2 * (maximum no. of child ports of your app's ports). + */ +void path_search(const rtosc::Ports& root, const char *str, const char *needle, + char *types, std::size_t max_types, + rtosc_arg_t* args, std::size_t max_args); + +/** + * Returns a messages of all paths and metadata of all direct children of a + * port, or of the port itself if that port has no children. + * + * Your app should always have a port "path-search", calling this function + * directly, and replying the resulting buffer @p msgbuf directly. That way, it + * will be ready for oscprompt, port-checker etc. + * + * @param root See @p m + * @param m a valid OSC message requesting the path search. The corresponding + * port args must be of types + * * "s" (location under @p root to look up port, or empty-string to search + * directly at @p root) + * * "s" (only port names starting with this sting are returned, use + * empty-string or nullptr to match everything) + * @param max_ports Maximum number (or higher) of child ports in any of your + * app's ports. + * @param msgbuf Buffer for the reply message + * @param bufsize Size of the message buffer @p msgbuf + * @return The length of the reply message (0 means error) + */ +std::size_t path_search(const rtosc::Ports& root, const char *m, + std::size_t max_ports, + char *msgbuf, std::size_t bufsize); + +/** * Return the index with value @p value from the metadata's enumeration. * @param meta The metadata * @param value The value to search the key for diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/ports-runtime.h zynaddsubfx-3.0.4/rtosc/include/rtosc/ports-runtime.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/ports-runtime.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/ports-runtime.h 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * @file ports-runtime.h Functions to retrieve runtime values from ports + */ + +#ifndef PORTS_RUNTIME +#define PORTS_RUNTIME + +#include +#include + +namespace rtosc { +namespace helpers { + +/** + * @brief Returns a port's value pretty-printed from a runtime object. The + * port object must not be known. + * + * For the parameters, see the overloaded function + * @return The argument values, pretty-printed + */ +const char* get_value_from_runtime(void* runtime, const struct Ports& ports, + size_t loc_size, char* loc, + char* buffer_with_port, + std::size_t buffersize, + int cols_used); + +/** + * @brief Returns a port's current value(s) + * + * This function returns the value(s) of a known port object and stores them as + * rtosc_arg_val_t. + * @param runtime The runtime object + * @param port the port where loc is relative to + * @param loc A buffer where dispatch can write down the currently dispatched + * path + * @param loc_size Size of loc + * @param portname_from_base The name of the port, relative to its base + * @param buffer_with_port A buffer which already contains the port. + * This buffer will be modified and must at least have space for 8 more bytes. + * @param buffersize Size of @p buffer_with_port + * @param max_args Maximum capacity of @p arg_vals + * @param arg_vals Argument buffer for returned argument values + * @return The number of argument values stored in @p arg_vals + */ +size_t get_value_from_runtime(void* runtime, const struct Port& port, + size_t loc_size, char* loc, + const char* portname_from_base, + char* buffer_with_port, std::size_t buffersize, + std::size_t max_args, rtosc_arg_val_t* arg_vals); + +// TODO: loc should probably not be passed, +// since it can be allocated in constant time? +// TODO: clean up those funcs: +// * use dispatch instead of cb +// * don't pass loc etc + +} +} + +#endif // PORTS_RUNTIME diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/port-sugar.h zynaddsubfx-3.0.4/rtosc/include/rtosc/port-sugar.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/port-sugar.h 2017-12-05 01:01:05.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/port-sugar.h 2019-03-03 15:19:10.000000000 +0000 @@ -24,6 +24,7 @@ #include #include +#include #ifndef RTOSC_PORT_SUGAR #define RTOSC_PORT_SUGAR @@ -182,15 +183,13 @@ //Array operators #define rArrayF(name, length, ...) \ {STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)} -#define rArray(name, length, ...) \ -{STRINGIFY(name) "#" STRINGIFY(length) "::c:i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)} #define rArrayT(name, length, ...) \ {STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)} #define rArrayI(name, length, ...) \ {STRINGIFY(name) "#" STRINGIFY(length) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayICb(name)} #define rArrayOption(name, length, ...) \ {STRINGIFY(name) "#" STRINGIFY(length) "::i:c:S", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayOptionCb(name)} - +#define rArray rArrayI //Method callback Actions #define rAction(name, ...) \ @@ -231,6 +230,7 @@ //{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)} //let this recurring parameter depend on another port +//the path of the other port must be at the current level or one level above #define rEnabledBy(portname) rMap(enabled by, portname) #define rEnabledByCondition(cond_name) rEnabledBy(cond_name) #define rEnabledCondition(cond_name, condition) \ @@ -278,10 +278,8 @@ #define rPresets(...) rPresetsAt(0, __VA_ARGS__) #define rDefaultDepends(dep_path_) rMap(default depends, dep_path_) #define rDefaultMissing "" // macro to denote yet missing default values -//#define rNoDefaults() ":no defaults\0" //!< this port (and all children) have no defaults -//! @brief Denote that this port and its children must always be skipped from -//! port-walking if a runtime is being given. -#define rNoWalk rProp(no walk) +//!< This port and all children have no default values on purpose +#define rNoDefaults ":no defaults\0" //Misc properties #define rDoc(doc) ":documentation\0=" doc "\0" @@ -441,17 +439,6 @@ #define rBOILS_END rBOIL_END -#define rArrayCb(name) rBOILS_BEGIN \ - if(!strcmp("", args)) {\ - data.reply(loc, "c", obj->name[idx]); \ - } else { \ - char var = rtosc_argument(msg, 0).i; \ - rLIMIT(var, atoi) \ - rAPPLY(name[idx], c) \ - data.broadcast(loc, "c", obj->name[idx]);\ - rChangeCb \ - } rBOILS_END - #define rArrayFCb(name) rBOILS_BEGIN \ if(!strcmp("", args)) {\ data.reply(loc, "f", obj->name[idx]); \ diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/pretty-format.h zynaddsubfx-3.0.4/rtosc/include/rtosc/pretty-format.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/pretty-format.h 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/pretty-format.h 2019-03-03 15:19:10.000000000 +0000 @@ -24,11 +24,13 @@ /** * @file pretty-format.h - * @brief Header for pretty printer and scanner + * Header for pretty printer and scanner + * + * @test pretty-format.c */ -#ifndef PRETTYFORMAT_H -#define PRETTYFORMAT_H +#ifndef RTOSC_PRETTY_FORMAT +#define RTOSC_PRETTY_FORMAT #include @@ -46,7 +48,8 @@ } rtosc_print_options; /** - * Pretty-print rtosct_arg_val_t structure into buffer + * Pretty-print rtosct_arg_val_t structure into buffer. + * Ranges are not being compressed (use rtosc_print_arg_vals instead). * * @param arg Pointer to the structure that shall be printed * @param buffer The buffer to write to @@ -61,7 +64,8 @@ int* cols_used); /** - * Pretty-print rtosct_arg_val_t array into buffer + * Pretty-print rtosct_arg_val_t array into buffer. + * Ranges are being compressed if opt->compress_ranges is true. * * @see rtosc_print_message * @warning in case of possible line breaks (almost always), buffer[-1] must @@ -75,6 +79,7 @@ /** * Pretty-print OSC message into string buffer * + * Ranges are being compressed if opt->compress_ranges is true. * A newline will be appended. * * @param address OSC pattern to send message to @@ -119,7 +124,7 @@ * no complete syntax check. * * @param src The string to scan from - * @return The number of arguments that can be scanned (>=0), or if the nth arg + * @return The number of arguments that can be scanned (>0), or if the nth arg * (range 1...) can not be scanned, -n. Array arguments and array start each * count as one argument. This function never returns 0 */ @@ -197,6 +202,6 @@ char* buffer_for_strings, size_t bufsize); #ifdef __cplusplus -}; +} #endif -#endif // PRETTYFORMAT_H +#endif // RTOSC_PRETTY_FORMAT diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc.h zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc.h 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc.h 2019-03-03 15:19:10.000000000 +0000 @@ -43,22 +43,34 @@ uint8_t *data; } rtosc_blob_t; +//! arg val element indicating an array, or a block of "nothing" +//! blocks of "nothing" are just for internal use and indicate that the +//! following array_start_t::len blocks must be ignored +//! (they can contain rubbish) typedef struct { char type; //!< common type of the elements - int32_t len; //!< number of arg_val_t contained + int32_t len; //!< number of arg_val_t (or "nothing") contained } array_start_t; //! a repeater is being followed by a delta argument (unless has_delta is 0) typedef struct { - int32_t num; //!< how often the element is being repeated + //! how often the first element is being repeated, including itself + int32_t num; int32_t has_delta; //!< if not 0, the next argument is the delta } repeater_t; +//! indicates that the next nothing_t::len blocks count as if nonexistant +//! they might contain rubbish +typedef struct +{ + int32_t len; +} nothing_t; + typedef union { // types that can be used in messages int32_t i; //i,c,r - char T; //I,T,F,N + char T; //I,T,F,N (F=>0, T=>1, I=>n/a, N=>n/a) float f; //f double d; //d int64_t h; //h @@ -67,10 +79,15 @@ const char *s; //s,S rtosc_blob_t b; //b // types that can *not* be used in messages - array_start_t a; //a + array_start_t a; //a,' ' repeater_t r; //- } rtosc_arg_t; +typedef struct { + char type; + rtosc_arg_t val; +} rtosc_arg_val_t; + /** * Write OSC message to fixed length buffer * @@ -114,6 +131,15 @@ /** * @see rtosc_message() */ +size_t rtosc_avmessage(char *buffer, + size_t len, + const char *address, + size_t nargs, + const rtosc_arg_val_t *args); + +/** + * @see rtosc_message() + */ size_t rtosc_amessage(char *buffer, size_t len, const char *address, @@ -140,78 +166,39 @@ const uint8_t *value_pos; } rtosc_arg_itr_t; -typedef struct { - char type; - rtosc_arg_t val; -} rtosc_arg_val_t; - typedef struct { //!< tolerance to when two floats or doubles are equal double float_tolerance; } rtosc_cmp_options; +/* + * arg val iterators + */ +/** + * Iterator over arg values + * + * Always use this iterator for iterating because it automatically skips + * to the next offset, even in case of arrays etc. + * Also, it walks through ranges, as if they were not existing. + */ typedef struct { const rtosc_arg_val_t* av; //!< the arg val referenced size_t i; //!< position of this arg val int range_i; //!< position of this arg val in its range -} rtosc_arg_val_t_const_itr; +} rtosc_arg_val_itr; -/* - * arg val iterators - */ -void rtosc_arg_val_itr_init(rtosc_arg_val_t_const_itr* itr, +void rtosc_arg_val_itr_init(rtosc_arg_val_itr* itr, const rtosc_arg_val_t* av); +//! this usually just returns the value from operand, except for range operands, +//! where the value is being interpolated +//! @param buffer Temporary. Don't access it afterwards. const rtosc_arg_val_t* rtosc_arg_val_itr_get( - const rtosc_arg_val_t_const_itr* itr, + const rtosc_arg_val_itr* itr, rtosc_arg_val_t* buffer); -void rtosc_arg_val_itr_next(rtosc_arg_val_t_const_itr* itr); - -/* - * arg val comparing helpers - */ -int rtosc_arg_vals_cmp_has_next(const rtosc_arg_val_t_const_itr* litr, - const rtosc_arg_val_t_const_itr* ritr, - size_t lsize, size_t rsize); -int rtosc_arg_vals_eq_after_abort(const rtosc_arg_val_t_const_itr* litr, - const rtosc_arg_val_t_const_itr* ritr, - size_t lsize, size_t rsize); -int rtosc_arg_vals_eq_single(const rtosc_arg_val_t* _lhs, - const rtosc_arg_val_t* _rhs, - const rtosc_cmp_options* opt); -int rtosc_arg_vals_cmp_single(const rtosc_arg_val_t* _lhs, - const rtosc_arg_val_t* _rhs, - const rtosc_cmp_options* opt); - -/** - * Check if two arrays of rtosc_arg_val_t are equal - * - * @param lsize Array size of lhs, e.g. 3 if lhs is just one counting range - * @param opt Comparison options or NULL for default options - * @return One if they are equal, zero if not - */ -int rtosc_arg_vals_eq(const rtosc_arg_val_t *lhs, const rtosc_arg_val_t *rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt); - -/** - * Compare two arrays of rtosc_arg_val_t. - * Whether an argument value is less or greater than another is computed - * - using memcmp for blobs - * - using strcmp for strings and identifiers - * - using numerical comparison for all other types. - * As an exception, the timestamp "immediately" is defined to be smaller than - * every other timestamp. - * - * @param opt Comparison options or NULL for default options - * @param lsize Array size of lhs, e.g. 3 if lhs is just one counting range - * @return An integer less than, equal to, or greater than zero if lhs is found, - * respectively, to be less than, to match, or be greater than rhs. - */ -int rtosc_arg_vals_cmp(const rtosc_arg_val_t *lhs, const rtosc_arg_val_t *rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt); +//! @warning will loop forever on infinite ranges! +void rtosc_arg_val_itr_next(rtosc_arg_val_itr* itr); //! va_list container, required for passing va_list as pointers to functions typedef struct { va_list a; } rtosc_va_list_t; diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc-time.h zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc-time.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc-time.h 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc-time.h 2018-03-11 15:05:07.000000000 +0000 @@ -22,8 +22,13 @@ * DEALINGS IN THE SOFTWARE. */ -#ifndef RTOSCTIME_H -#define RTOSCTIME_H +#ifndef RTOSC_TIME +#define RTOSC_TIME + +/** + * @file rtosc-time.h + * Functions and helper functions for conversion between time and arg vals + */ #include #include @@ -60,6 +65,6 @@ float rtosc_secfracs2float(uint64_t secfracs); #ifdef __cplusplus -}; +} #endif -#endif // RTOSCTIME_H +#endif // RTOSC_TIME diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc-version.h zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc-version.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/rtosc-version.h 2017-10-01 13:13:53.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/rtosc-version.h 2018-03-11 15:05:07.000000000 +0000 @@ -25,11 +25,13 @@ /** * @file rtosc-version.h * Definition of rtosc's version struct + * * @note the implementation is in version.c.in + * @test version.c */ -#ifndef RTOSC_VERSION_H -#define RTOSC_VERSION_H +#ifndef RTOSC_VERSION +#define RTOSC_VERSION #ifdef __cplusplus extern "C" { @@ -64,5 +66,5 @@ }; #endif -#endif +#endif // RTOSC_VERSION diff -Nru zynaddsubfx-3.0.3/rtosc/include/rtosc/savefile.h zynaddsubfx-3.0.4/rtosc/include/rtosc/savefile.h --- zynaddsubfx-3.0.3/rtosc/include/rtosc/savefile.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/include/rtosc/savefile.h 2018-03-11 15:05:07.000000000 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * @file savefile.h + * Functions for reading and loading rtosc savefiles + * + * @test default-value.cpp + */ + +#ifndef RTOSC_SAVEFILE +#define RTOSC_SAVEFILE + +#include +#include +#include + +namespace rtosc { + +/** + * Return a string list of all changed values + * + * Return a human readable list of the value that changed + * corresponding to the rDefault macro + * @param ports The static ports structure + * @param runtime The runtime object + * @note This function is not realtime save (It uses std::string), which is + * usually OK, since this function is being run inside a non-RT thread. If you + * need this to be realtime save, add a template parameter for a functor that + * replaces the std::string handling. + * @return The list of ports and their changed values, linewise + */ +std::string get_changed_values(const struct Ports& ports, void* runtime); + +//! @brief Class to modify and dispatch messages loaded from savefiles. +//! Objects of this class shall be passed to savefile loading routines. You can +//! inherit to change the behaviour, e.g. to modify or discard such messages. +class savefile_dispatcher_t +{ + const struct Ports* ports; + void* runtime; + char loc[1024]; + +protected: + enum proceed { + abort = -2, //!< the message shall lead to abort the savefile loading + discard = -1 //!< the message shall not be dispatched + }; + + enum dependency_t { + no_dependencies, //! default values don't depend on others + has_dependencies, //! default values do depend on others + not_specified //! it's not know which of the other enum values fit + }; + + rtosc_version rtosc_filever, //!< rtosc versinon savefile was written with + rtosc_curver, //!< rtosc version of this library + app_filever, //!< app version savefile was written with + app_curver; //!< current app version + + //! call this to dispatch a message + bool operator()(const char* msg) { return do_dispatch(msg); } + + static int default_response(size_t nargs, bool first_round, + dependency_t dependency); + +private: + //! callback for when a message shall be dispatched + //! implement this if you need to change a message + virtual int on_dispatch(size_t portname_max, char* portname, + size_t maxargs, size_t nargs, + rtosc_arg_val_t* args, + bool round2, dependency_t dependency); + //! call this to dispatch a message + virtual bool do_dispatch(const char* msg); + + friend int dispatch_printed_messages(const char* messages, + const struct Ports& ports, + void* runtime, + savefile_dispatcher_t *dispatcher); + + friend int load_from_file(const char* file_content, + const struct Ports& ports, void* runtime, + const char* appname, + rtosc_version appver, + savefile_dispatcher_t* dispatcher); +}; + +/** + * Scan OSC messages from human readable format and dispatch them. + * @param messages The OSC messages, whitespace-separated + * @param ports The static ports structure + * @param runtime The runtime object + * @param dispatcher Object to modify messages prior to dispatching, or NULL. + * You can overwrite its virtual functions, and you should specify any of the + * version structs if needed. All other members shall not be initialized. + * @return The number of messages read, or, if there was a read error, + * or the dispatcher did refuse to dispatch, + * the number of bytes read until the read error occured minus one + */ +int dispatch_printed_messages(const char* messages, + const struct Ports& ports, void* runtime, + savefile_dispatcher_t *dispatcher = NULL); + +/** + * Return a savefile containing all values that differ from the default values. + * @param ports The static ports structure + * @param runtime The runtime object + * @param appname Name of the application calling this function + * @param appver Version of the application calling this function + * @return The resulting savefile as an std::sting + */ +std::string save_to_file(const struct Ports& ports, void* runtime, + const char* appname, rtosc_version appver); + +/** + * Read save file and dispatch contained parameters. + * @param file_content The file as a C string + * @param ports The static ports structure + * @param runtime The runtime object + * @param appname Name of the application calling this function; must + * match the file's application name + * @param appver Version of the application calling this function + * @param dispatcher Modifier for the messages; NULL if no modifiers are needed + * @return The number of messages read, or, if there was a read error, + * the negated number of bytes read until the read error occured minus one + */ +int load_from_file(const char* file_content, + const struct Ports& ports, void* runtime, + const char* appname, + rtosc_version appver, + savefile_dispatcher_t* dispatcher = NULL); + +} + +#endif // RTOSC_SAVEFILE diff -Nru zynaddsubfx-3.0.3/rtosc/README zynaddsubfx-3.0.4/rtosc/README --- zynaddsubfx-3.0.3/rtosc/README 2017-10-01 13:13:53.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -RtOsc - Realtime safe OSC Messaging -=================================== - -A realtime safe library for handling OSC messages. -This library is influenced by liblo and libmapper. - -Project Goals -------------- - -* Provide a simple means of handling OSC messages within an application -* Provide a flexible method of dispatching OSC messages payloads -* Create a C library with C++ wrappers for ease of use -* Allow legacy code to have readable RT safe messaging added on -* Keep networking outside of project scope - -Why? ----- - -Well if an application is going to receive nice serialized messages in the OSC format, -then it would be nice if these messages could be dispatched within a realtime -thread. -This is not possible with any observed C OSC implementation, so this is an -attempt to alleviate this issue without creating an entirely hideous API. - -Status ------- - -- OSC 1.0 spec consistent implementation of all message types, and bundles -- Lightly tested liblo based implementation verification -- Working C++ based dispatch system -- C++ Serialization System -- C++ Syntax Sugar - -Documentation -------------- - -- To see the guide, you can use `asciidoctor Guide.adoc` in the doc directory -- To build doxygen docs, use `make rtosc-doc` in your build directory - -Todo ----- - -- Increase test coverage over C++ layer -- Provide standard compliant path/pattern matching algorithms - diff -Nru zynaddsubfx-3.0.3/rtosc/README.adoc zynaddsubfx-3.0.4/rtosc/README.adoc --- zynaddsubfx-3.0.3/rtosc/README.adoc 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/README.adoc 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,47 @@ +RtOsc - Realtime safe OSC Messaging +=================================== + +image::https://travis-ci.org/fundamental/rtosc.svg?branch=master[alt="Build status", link="https://travis-ci.org/fundamental/rtosc"] + +A realtime safe library for handling OSC messages. +This library is influenced by liblo and libmapper. + +Project Goals +------------- + +* Provide a simple means of handling OSC messages within an application +* Provide a flexible method of dispatching OSC messages payloads +* Create a C library with C++ wrappers for ease of use +* Allow legacy code to have readable RT safe messaging added on +* Keep networking outside of project scope + +Why? +---- + +Well if an application is going to receive nice serialized messages in the OSC format, +then it would be nice if these messages could be dispatched within a realtime +thread. +This is not possible with any observed C OSC implementation, so this is an +attempt to alleviate this issue without creating an entirely hideous API. + +Status +------ + +- OSC 1.0 spec consistent implementation of all message types, and bundles +- Lightly tested liblo based implementation verification +- Working C++ based dispatch system +- C++ Serialization System +- C++ Syntax Sugar + +Documentation +------------- + +- To see the guide, you can use `asciidoctor Guide.adoc` in the doc directory +- To build doxygen docs, use `make rtosc-doc` in your build directory + +Todo +---- + +- Increase test coverage over C++ layer +- Provide standard compliant path/pattern matching algorithms + diff -Nru zynaddsubfx-3.0.3/rtosc/src/arg-val-cmp.c zynaddsubfx-3.0.4/rtosc/src/arg-val-cmp.c --- zynaddsubfx-3.0.3/rtosc/src/arg-val-cmp.c 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/arg-val-cmp.c 2018-09-09 00:40:57.000000000 +0000 @@ -0,0 +1,307 @@ +#include +#include +#include + +#include +#include + +static const rtosc_cmp_options default_cmp_options = { 0.0 }; + +// abort only if one side was finished, or if both are infinite ranges +int rtosc_arg_vals_cmp_has_next(const rtosc_arg_val_itr* litr, + const rtosc_arg_val_itr* ritr, + size_t lsize, size_t rsize) +{ + return (litr->i < lsize) && (ritr->i < rsize) + && (litr->av->type != '-' || ritr->av->type != '-' || + litr->av->val.r.num || ritr->av->val.r.num); +} + +// arrays are equal by now, but is one longer? +// each array must have either been completely passed, or it must +// have reached an infinite range +int rtosc_arg_vals_eq_after_abort(const rtosc_arg_val_itr* litr, + const rtosc_arg_val_itr* ritr, + size_t lsize, size_t rsize) +{ + return (litr->i == lsize || + (litr->av->type == '-' && !litr->av->val.r.num)) + && (ritr->i == rsize || + (ritr->av->type == '-' && !ritr->av->val.r.num)); +} + +// compare single elements, ranges excluded +int rtosc_arg_vals_eq_single(const rtosc_arg_val_t* _lhs, + const rtosc_arg_val_t* _rhs, + const rtosc_cmp_options* opt) +{ +#define mfabs(val) (((val) >= 0) ? (val) : -(val)) + int rval; + + if(!opt) + opt = &default_cmp_options; + + if(_lhs->type == _rhs->type) + switch(_lhs->type) + { + case 'i': + case 'c': + case 'r': + rval = _lhs->val.i == _rhs->val.i; + break; + case 'I': + case 'T': + case 'F': + case 'N': + rval = 1; + break; + case 'f': + rval = (opt->float_tolerance == 0.0) + ? _lhs->val.f == _rhs->val.f + : mfabs(_lhs->val.f - _rhs->val.f) <= + (float)opt->float_tolerance; + break; + case 'd': + rval = (opt->float_tolerance == 0.0) + ? _lhs->val.d == _rhs->val.d + : mfabs(_lhs->val.d - _rhs->val.d) <= + opt->float_tolerance; + break; + case 'h': + rval = _lhs->val.h == _rhs->val.h; + break; + case 't': + rval = _lhs->val.t == _rhs->val.t; + break; + case 'm': + rval = 0 == memcmp(_lhs->val.m, _rhs->val.m, 4); + break; + case 's': + case 'S': + rval = (_lhs->val.s == NULL || _rhs->val.s == NULL) + ? _lhs->val.s == _rhs->val.s + : (0 == strcmp(_lhs->val.s, _rhs->val.s)); + break; + case 'b': + { + int32_t lbs = _lhs->val.b.len, + rbs = _rhs->val.b.len; + rval = lbs == rbs; + if(rval) + rval = 0 == memcmp(_lhs->val.b.data, _rhs->val.b.data, lbs); + break; + } + case 'a': + { + if( _lhs->val.a.type != _rhs->val.a.type + && !(_lhs->val.a.type == 'T' && _rhs->val.a.type == 'F') + && !(_lhs->val.a.type == 'F' && _rhs->val.a.type == 'T')) + rval = 0; + else + rval = rtosc_arg_vals_eq(_lhs+1, _rhs+1, + _lhs->val.a.len, _rhs->val.a.len, + opt); + break; + } + default: + rval = -1; + // no recovery: the programmer did not pass the right args, and + // we don't have a function to compare ranges here + assert(false); + exit(1); + break; + } + else + { + rval = 0; + } + return rval; +#undef mfabs +} + +int rtosc_arg_vals_eq(const rtosc_arg_val_t* lhs, const rtosc_arg_val_t* rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt) +{ + // used if the value of lhs or rhs is range-computed: + rtosc_arg_val_t rlhs, rrhs; + + rtosc_arg_val_itr litr, ritr; + rtosc_arg_val_itr_init(&litr, lhs); + rtosc_arg_val_itr_init(&ritr, rhs); + + int rval = 1; + + if(!opt) + opt = &default_cmp_options; + + for( ; rtosc_arg_vals_cmp_has_next(&litr, &ritr, lsize, rsize) && rval; + rtosc_arg_val_itr_next(&litr), + rtosc_arg_val_itr_next(&ritr)) + { + rval = rtosc_arg_vals_eq_single(rtosc_arg_val_itr_get(&litr, &rlhs), + rtosc_arg_val_itr_get(&ritr, &rrhs), + opt); + } + + return rval + ? rtosc_arg_vals_eq_after_abort(&litr, &ritr, lsize, rsize) + : rval; +} + +// compare single elements, ranges excluded +int rtosc_arg_vals_cmp_single(const rtosc_arg_val_t* _lhs, + const rtosc_arg_val_t* _rhs, + const rtosc_cmp_options* opt) +{ +#define cmp_3way(val1,val2) (((val1) == (val2)) \ + ? 0 \ + : (((val1) > (val2)) ? 1 : -1)) +#define mfabs(val) (((val) >= 0) ? (val) : -(val)) + + int rval; + + if(!opt) + opt = &default_cmp_options; + + if(_lhs->type == _rhs->type) + switch(_lhs->type) + { + case 'i': + case 'c': + case 'r': + rval = cmp_3way(_lhs->val.i, _rhs->val.i); + break; + case 'I': + case 'T': + case 'F': + case 'N': + rval = 0; + break; + case 'f': + rval = (opt->float_tolerance == 0.0) + ? cmp_3way(_lhs->val.f, _rhs->val.f) + : (mfabs(_lhs->val.f - _rhs->val.f) + <= (float)opt->float_tolerance) + ? 0 + : ((_lhs->val.f > _rhs->val.f) ? 1 : -1); + break; + case 'd': + rval = (opt->float_tolerance == 0.0) + ? cmp_3way(_lhs->val.d, _rhs->val.d) + : (mfabs(_lhs->val.d - _rhs->val.d) + <= opt->float_tolerance) + ? 0 + : ((_lhs->val.d > _rhs->val.d) ? 1 : -1); + break; + case 'h': + rval = cmp_3way(_lhs->val.h, _rhs->val.h); + break; + case 't': + // immediately is considered lower than everything else + // this means if you send two events to a client, + // one being "immediately" and one being different, the + // immediately-event has the higher priority, event if the + // other one is in the past + rval = (_lhs->val.t == 1) + ? (_rhs->val.t == 1) + ? 0 + : -1 // _lhs has higher priority => _lhs < _rhs + : (_rhs->val.t == 1) + ? 1 + : cmp_3way(_lhs->val.t, _rhs->val.t); + break; + case 'm': + rval = memcmp(_lhs->val.m, _rhs->val.m, 4); + break; + case 's': + case 'S': + rval = (_lhs->val.s == NULL || _rhs->val.s == NULL) + ? cmp_3way(_lhs->val.s, _rhs->val.s) + : strcmp(_lhs->val.s, _rhs->val.s); + break; + case 'b': + { + int32_t lbs = _lhs->val.b.len, + rbs = _rhs->val.b.len; + int32_t minlen = (lbs < rbs) ? lbs : rbs; + rval = memcmp(_lhs->val.b.data, _rhs->val.b.data, minlen); + if(lbs != rbs && !rval) + { + // both equal until here + // the string that ends here is lexicographically smaller + rval = (lbs > rbs) + ? _lhs->val.b.data[minlen] + : -_rhs->val.b.data[minlen]; + } + + break; + } + case 'a': + { + int32_t llen = _lhs->val.a.len, rlen = _rhs->val.a.len; + if( _lhs->val.a.type != _rhs->val.a.type + && !(_lhs->val.a.type == 'T' && _rhs->val.a.type == 'F') + && !(_lhs->val.a.type == 'F' && _rhs->val.a.type == 'T')) + rval = (_lhs->val.a.type > _rhs->val.a.type) ? 1 : -1; + else + { + // the arg vals differ in this array => compare and return + rval = rtosc_arg_vals_cmp(_lhs+1, _rhs+1, llen, rlen, opt); + } + break; + } + case '-': + rval = -1; + // no recovery: the programmer did not pass the right args, and + // we don't have a function to compare ranges here + assert(false); + exit(1); + break; + } + else + { + rval = (_lhs->type > _rhs->type) ? 1 : -1; + } + + return rval; + +#undef mfabs +#undef cmp_3way +} + +int rtosc_arg_vals_cmp(const rtosc_arg_val_t* lhs, const rtosc_arg_val_t* rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt) +{ + // used if the value of lhs or rhs is range-computed: + rtosc_arg_val_t rlhs, rrhs; + + rtosc_arg_val_itr litr, ritr; + rtosc_arg_val_itr_init(&litr, lhs); + rtosc_arg_val_itr_init(&ritr, rhs); + + int rval = 0; + + if(!opt) + opt = &default_cmp_options; + + for( ; rtosc_arg_vals_cmp_has_next(&litr, &ritr, lsize, rsize) && !rval; + rtosc_arg_val_itr_next(&litr), + rtosc_arg_val_itr_next(&ritr)) + { + rval = rtosc_arg_vals_cmp_single(rtosc_arg_val_itr_get(&litr, &rlhs), + rtosc_arg_val_itr_get(&ritr, &rrhs), + opt); + } + + return rval ? rval + : (rtosc_arg_vals_eq_after_abort(&litr, &ritr, lsize, rsize)) + ? 0 + // they're equal until here, so one array must have + // elements left: + : (lsize-(litr.i) > rsize-(ritr.i)) + ? 1 + : -1; +} + diff -Nru zynaddsubfx-3.0.3/rtosc/src/arg-val-math.c zynaddsubfx-3.0.4/rtosc/src/arg-val-math.c --- zynaddsubfx-3.0.3/rtosc/src/arg-val-math.c 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/arg-val-math.c 2018-03-11 15:05:07.000000000 +0000 @@ -1,9 +1,7 @@ +#include #include #include -// TODO: later commit: move cmp functions here -// TODO: test these functions - int rtosc_arg_val_null(rtosc_arg_val_t* av, char type) { av->type = type; @@ -18,6 +16,8 @@ case 'c': case 'i': case 'r': av->val.i = 0; return true; + case 'T': + case 'F': av->type = 'F'; av->val.T = 0; return true; default: return false; } } @@ -32,6 +32,36 @@ case 'f': av->val.f = number; return true; case 'c': case 'i': av->val.i = number; return true; + case 'F': + case 'T': + // note: we discard av->type here! + // it's clear that the decision should be based on "number", + // not on the current type + av->val.T = (number != 0.0); + av->type = av->val.T ? 'T' : 'F'; + return true; + default: return false; + } +} + +int rtosc_arg_val_from_double(rtosc_arg_val_t* av, char type, double number) +{ + av->type = type; + switch(type) + { + case 'h': av->val.h = number; return true; + case 'd': av->val.d = number; return true; + case 'f': av->val.f = number; return true; + case 'c': + case 'i': av->val.i = number; return true; + case 'F': + case 'T': + // note: we discard av->type here! + // it's clear that the decision should be based on "number", + // not on the current type + av->val.T = (number != 0.0); + av->type = av->val.T ? 'T' : 'F'; + return true; default: return false; } } @@ -45,8 +75,8 @@ case 'f': av->val.f = -av->val.f; return true; case 'c': case 'i': av->val.i = -av->val.i; return true; - case 'T': - case 'F': av->val.T = !av->val.T; return true; + case 'T': av->val.T = 0; av->type = 'F'; return true; + case 'F': av->val.T = 1; av->type = 'T'; return true; default: return false; } } @@ -58,11 +88,11 @@ { case 'd': tmp = (int)(av->val.d); - av->val.d = tmp + (int)(av->val.d - tmp > 0.001); + av->val.d = tmp + (int)(av->val.d - tmp >= 0.999); return true; case 'f': tmp = (int)(av->val.f); - av->val.f = tmp + (int)(av->val.f - tmp > 0.001f); + av->val.f = tmp + (int)(av->val.f - tmp >= 0.999f); return true; case 'h': case 'c': @@ -79,16 +109,30 @@ rtosc_arg_val_t* res) { if(lhs->type != rhs->type) - return false; - res->type = lhs->type; - switch(lhs->type) { - case 'd': res->val.d = lhs->val.d + rhs->val.d; return true; - case 'f': res->val.f = lhs->val.f + rhs->val.f; return true; - case 'h': res->val.h = lhs->val.h + rhs->val.h; return true; - case 'c': - case 'i': res->val.i = lhs->val.i + rhs->val.i; return true; - default: return false; + if((lhs->type == 'F' && rhs->type == 'T') || + (lhs->type == 'T' && rhs->type == 'F')) + { + res->type = 'T'; + res->val.T = 1; + return true; + } + else return false; + } + else + { + res->type = lhs->type; + switch(lhs->type) + { + case 'd': res->val.d = lhs->val.d + rhs->val.d; return true; + case 'f': res->val.f = lhs->val.f + rhs->val.f; return true; + case 'h': res->val.h = lhs->val.h + rhs->val.h; return true; + case 'c': + case 'i': res->val.i = lhs->val.i + rhs->val.i; return true; + case 'T': + case 'F': res->type = 'F'; res->val.T = 0; return true; + default: return false; + } } } @@ -96,7 +140,7 @@ rtosc_arg_val_t* res) { if(lhs->type != rhs->type) - return false; + return rtosc_arg_val_add(lhs, rhs, res); res->type = lhs->type; switch(lhs->type) { @@ -105,6 +149,8 @@ case 'h': res->val.h = lhs->val.h - rhs->val.h; return true; case 'c': case 'i': res->val.i = lhs->val.i - rhs->val.i; return true; + case 'T': + case 'F': res->type = 'F'; res->val.T = 0; return true; default: return false; } } @@ -113,16 +159,30 @@ rtosc_arg_val_t* res) { if(lhs->type != rhs->type) - return false; - res->type = lhs->type; - switch(lhs->type) { - case 'd': res->val.d = lhs->val.d * rhs->val.d; return true; - case 'f': res->val.f = lhs->val.f * rhs->val.f; return true; - case 'h': res->val.h = lhs->val.h * rhs->val.h; return true; - case 'c': - case 'i': res->val.i = lhs->val.i * rhs->val.i; return true; - default: return false; + if((lhs->type == 'F' && rhs->type == 'T') || + (lhs->type == 'T' && rhs->type == 'F')) + { + res->type = 'F'; + res->val.T = 0; + return true; + } + else return false; + } + else + { + res->type = lhs->type; + switch(lhs->type) + { + case 'd': res->val.d = lhs->val.d * rhs->val.d; return true; + case 'f': res->val.f = lhs->val.f * rhs->val.f; return true; + case 'h': res->val.h = lhs->val.h * rhs->val.h; return true; + case 'c': + case 'i': res->val.i = lhs->val.i * rhs->val.i; return true; + case 'T': res->type = 'T'; res->val.T = 1; return true; + case 'F': res->type = 'F'; res->val.T = 0; return true; + default: return false; + } } } @@ -139,6 +199,8 @@ case 'h': res->val.h = lhs->val.h / rhs->val.h; return true; case 'c': case 'i': res->val.i = lhs->val.i / rhs->val.i; return true; + case 'T': res->type = 'T'; res->val.T = 1; return true; + case 'F': assert(false); return false; // divide by 0 default: return false; } } @@ -152,6 +214,9 @@ case 'h': *res = av->val.h; return true; case 'c': case 'i': *res = av->val.i; return true; + case 'T': + case 'F': + *res = av->val.T; return true; default: return false; } } diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/automations.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/automations.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/automations.cpp 2017-09-13 16:36:22.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/automations.cpp 2019-03-10 16:16:45.000000000 +0000 @@ -1,4 +1,6 @@ +#include "../util.h" #include +#include #include using namespace rtosc; @@ -77,7 +79,14 @@ au.param_min = atof(meta["min"]); au.param_max = atof(meta["max"]); } - strncpy(au.param_path, path, sizeof(au.param_path)); + fast_strcpy(au.param_path, path, sizeof(au.param_path)); + + if(meta["scale"] && strstr(meta["scale"], "log")) { + au.map.control_scale = 1; + au.param_min = logf(au.param_min); + au.param_max = logf(au.param_max); + } else + au.map.control_scale = 0; au.map.gain = 100.0; au.map.offset = 0; @@ -152,6 +161,9 @@ else if(v < mn) v = mn; + if(au.map.control_scale == 1) + v = expf(v); + rtosc_message(msg, 256, path, "f", v); } else if(type == 'T' || type == 'F') { float v = value*(b-a) + a; @@ -218,6 +230,61 @@ damaged = true; } +void AutomationMgr::setSlotSubPath(int slot, int ind, const char *path) +{ + if(slot >= nslots || slot < 0) + return; + + assert(p); + const Port *port = p->apropos(path); + if(!port) { + fprintf(stderr, "[Zyn:Error] port '%s' does not exist\n", path); + return; + } + auto meta = port->meta(); + if(!(meta.find("min") && meta.find("max"))) { + if(!strstr(port->name, ":T")) { + fprintf(stderr, "No bounds for '%s' known\n", path); + return; + } + } + if(meta.find("internal") || meta.find("no learn")) { + fprintf(stderr, "[Warning] port '%s' is unlearnable\n", path); + return; + } + + slots[slot].used = true; + + auto &au = slots[slot].automations[ind]; + + au.used = true; + au.active = true; + au.param_type = 'i'; + if(strstr(port->name, ":f")) + au.param_type = 'f'; + else if(strstr(port->name, ":T")) + au.param_type = 'T'; + if(au.param_type == 'T') { + au.param_min = 0.0; + au.param_max = 1.0; + } else { + au.param_min = atof(meta["min"]); + au.param_max = atof(meta["max"]); + } + fast_strcpy(au.param_path, path, sizeof(au.param_path)); + + if(meta["scale"] && strstr(meta["scale"], "log")) { + au.map.control_scale = 1; + au.param_min = logf(au.param_min); + au.param_max = logf(au.param_max); + } else + au.map.control_scale = 0; + + updateMapping(slot, ind); + damaged = true; + +} + void AutomationMgr::setSlotSubGain(int slot_id, int sub, float f) { if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0) @@ -251,7 +318,7 @@ { if(slot_id >= nslots || slot_id < 0) return; - strncpy(slots[slot_id].name, msg, sizeof(slots[slot_id].name)); + fast_strcpy(slots[slot_id].name, msg, sizeof(slots[slot_id].name)); damaged = 1; } const char *AutomationMgr::getName(int slot_id) diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/default-value.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/default-value.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/default-value.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/default-value.cpp 2018-03-11 15:05:07.000000000 +0000 @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include + +namespace rtosc { + +const char* get_default_value(const char* port_name, const Ports& ports, + void* runtime, const Port* port_hint, + int32_t idx, int recursive) +{ + constexpr std::size_t buffersize = 8192; + char buffer[buffersize]; + char loc[buffersize] = ""; + + assert(recursive >= 0); // forbid recursing twice + + char default_annotation[20] = "default"; +// if(idx > 0) +// snprintf(default_annotation + 7, 13, "[%" PRId32 "]", idx); + const char* const dependent_annotation = "default depends"; + const char* return_value = nullptr; + + if(!port_hint) + port_hint = ports.apropos(port_name); + assert(port_hint); // port must be found + const Port::MetaContainer metadata = port_hint->meta(); + + // Let complex cases depend upon a marker variable + // If the runtime is available the exact preset number can be found + // This generalizes to envelope types nicely if envelopes have a read + // only port which indicates if they're amplitude/frequency/etc + const char* dependent = metadata[dependent_annotation]; + if(dependent) + { + char* dependent_port = buffer; + *dependent_port = 0; + + assert(strlen(port_name) + strlen(dependent_port) + 5 < buffersize); + strncat(dependent_port, port_name, + buffersize - strlen(dependent_port) - 1); + strncat(dependent_port, "/../", + buffersize - strlen(dependent_port) - 1); + strncat(dependent_port, dependent, + buffersize - strlen(dependent_port) - 1); + dependent_port = Ports::collapsePath(dependent_port); + + // TODO: collapsePath bug? + // Relative paths should not start with a slash after collapsing ... + if(*dependent_port == '/') + ++dependent_port; + + const char* dependent_value = + runtime + ? helpers::get_value_from_runtime(runtime, ports, + buffersize, loc, + dependent_port, + buffersize-1, 0) + : get_default_value(dependent_port, ports, + runtime, NULL, recursive-1); + + assert(strlen(dependent_value) < 16); // must be an int + + char* default_variant = buffer; + *default_variant = 0; + assert(strlen(default_annotation) + 1 + 16 < buffersize); + strncat(default_variant, default_annotation, + buffersize - strlen(default_variant)); + strncat(default_variant, " ", buffersize - strlen(default_variant)); + strncat(default_variant, dependent_value, + buffersize - strlen(default_variant)); + + return_value = metadata[default_variant]; + } + + // If return_value is NULL, this can have two meanings: + // 1. there was no depedent annotation + // => check for a direct (non-dependent) default value + // (a non existing direct default value is OK) + // 2. there was a dependent annotation, but the dependent value has no + // mapping (mapping for default_variant was NULL) + // => check for the direct default value, which acts as a default + // mapping for all presets; a missing default value indicates an + // error in the metadata + if(!return_value) + { + return_value = metadata[default_annotation]; + assert(!dependent || return_value); + } + + return return_value; +} + +int get_default_value(const char* port_name, const char* port_args, + const Ports& ports, void* runtime, const Port* port_hint, + int32_t idx, std::size_t n, rtosc_arg_val_t* res, + char* strbuf, size_t strbufsize) +{ + const char* pretty = get_default_value(port_name, ports, runtime, port_hint, + idx, 0); + + int nargs; + if(pretty) + { + nargs = rtosc_count_printed_arg_vals(pretty); + assert(nargs > 0); // parse error => error in the metadata? + assert((size_t)nargs < n); + + rtosc_scan_arg_vals(pretty, res, nargs, strbuf, strbufsize); + + { + // TODO: port_hint could be NULL here! + int errs_found = canonicalize_arg_vals(res, + nargs, + port_args, + port_hint->meta()); + if(errs_found) + fprintf(stderr, "Could not canonicalize %s for port %s\n", + pretty, port_name); + assert(!errs_found); // error in the metadata? + } + } + else + nargs = -1; + + return nargs; +} + +} diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/miditable.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/miditable.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/miditable.cpp 2015-10-23 14:47:50.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/miditable.cpp 2018-09-09 00:40:57.000000000 +0000 @@ -1,3 +1,4 @@ +#include "../util.h" #include #include @@ -120,7 +121,7 @@ } if(MidiAddr *e = this->get(ch,ctl)) { - strncpy(e->path,path,impl->len); + fast_strcpy(e->path,path,impl->len); if(!mash_port(*e, *port)) { e->ch = RTOSC_INVALID_MIDI; e->ctl = RTOSC_INVALID_MIDI; @@ -134,7 +135,7 @@ if(e.ch == RTOSC_INVALID_MIDI) {//free spot e.ch = ch; e.ctl = ctl; - strncpy(e.path,path,impl->len); + fast_strcpy(e.path,path,impl->len); if(!mash_port(e, *port)) { e.ch = RTOSC_INVALID_MIDI; e.ctl = RTOSC_INVALID_MIDI; @@ -162,7 +163,7 @@ return; } clear_entry(s); - strncpy(unhandled_path, s, MAX_UNHANDLED_PATH); + fast_strcpy(unhandled_path, s, MAX_UNHANDLED_PATH); unhandled_path[MAX_UNHANDLED_PATH-1] = '\0'; check_learn(); } diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/ports.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/ports.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/ports.cpp 2017-12-05 01:01:05.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/ports.cpp 2019-03-10 16:16:45.000000000 +0000 @@ -1,13 +1,14 @@ -#include "../../include/rtosc/ports.h" -#include "../../include/rtosc/rtosc.h" -#include "../../include/rtosc/pretty-format.h" +#include "../util.h" +#include "../../include/rtosc/ports.h" +#include "../../include/rtosc/ports-runtime.h" +#include "../../include/rtosc/bundle-foreach.h" -#include #include #include #include #include #include +#include /* Compatibility with non-clang compilers */ #ifndef __has_feature @@ -315,7 +316,7 @@ { int dups = 0; int N = t.size(); - bool mark[t.size()]; + STACKALLOC(bool, mark, t.size()); memset(mark, 0, N); for(int i=0; i= 8); - // append type - memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments - buffer_with_port[addr_len + (4-addr_len%4)] = ','; - - d.message = buffer_with_port; - - // buffer_with_port is a message in this call: - ports.dispatch(buffer_with_port, d, false); - - return d.value(); -} - -//! RtData subclass to capture argument values from a runtime object -class Capture : public RtData -{ - size_t max_args; - rtosc_arg_val_t* arg_vals; - int nargs; - - void chain(const char *path, const char *args, ...) override - { - nargs = 0; - } - - void chain(const char *msg) override - { - nargs = 0; - } - - void reply(const char *) override { assert(false); } - - void replyArray(const char*, const char *args, - rtosc_arg_t *vals) override - { - size_t cur_idx = 0; - for(const char* ptr = args; *ptr; ++ptr, ++cur_idx) - { - assert(cur_idx < max_args); - arg_vals[cur_idx].type = *ptr; - arg_vals[cur_idx].val = vals[cur_idx]; - } - nargs = cur_idx; - } - - void reply_va(const char *args, va_list va) - { - nargs = strlen(args); - assert((size_t)nargs <= max_args); - - rtosc_v2argvals(arg_vals, nargs, args, va); - } - - void broadcast(const char *, const char *args, ...) override - { - va_list va; - va_start(va, args); - reply_va(args, va); - va_end(va); - } - - void reply(const char *, const char *args, ...) override - { - va_list va; - va_start(va,args); - reply_va(args, va); - va_end(va); - } -public: - //! Return the number of argument values stored - int size() const { return nargs; } - Capture(std::size_t max_args, rtosc_arg_val_t* arg_vals) : - max_args(max_args), arg_vals(arg_vals), nargs(-1) {} -}; - -/** - * @brief Returns a port's current value(s) - * - * This function returns the value(s) of a known port object and stores them as - * rtosc_arg_val_t. - * @param runtime The runtime object - * @param port the port where the value shall be retrieved - * @param loc A buffer where dispatch can write down the currently dispatched - * path - * @param loc_size Size of loc - * @param portname_from_base The name of the port, relative to its base - * @param buffer_with_port A buffer which already contains the port. - * This buffer will be modified and must at least have space for 8 more bytes. - * @param buffersize Size of @p buffer_with_port - * @param max_args Maximum capacity of @p arg_vals - * @param arg_vals Argument buffer for returned argument values - * @return The number of argument values stored in @p arg_vals - */ -static size_t get_value_from_runtime(void* runtime, - const Port& port, - size_t loc_size, - char* loc, - const char* portname_from_base, - char* buffer_with_port, - std::size_t buffersize, - std::size_t max_args, - rtosc_arg_val_t* arg_vals) -{ - strncpy(buffer_with_port, portname_from_base, buffersize); - std::size_t addr_len = strlen(buffer_with_port); - - Capture d(max_args, arg_vals); - d.obj = runtime; - d.loc_size = loc_size; - d.loc = loc; - d.port = &port; - d.matches = 0; - assert(*loc); - - // does the message at least fit the arguments? - assert(buffersize - addr_len >= 8); - // append type - memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments - buffer_with_port[addr_len + (4-addr_len%4)] = ','; - - // TODO? code duplication - - // buffer_with_port is a message in this call: - d.message = buffer_with_port; - port.cb(buffer_with_port, d); - - assert(d.size() >= 0); - return d.size(); -} - -/* - * default values - */ - -const char* rtosc::get_default_value(const char* port_name, const Ports& ports, - void* runtime, const Port* port_hint, - int32_t idx, int recursive) -{ - constexpr std::size_t buffersize = 1024; - char buffer[buffersize]; - char loc[buffersize] = ""; - - assert(recursive >= 0); // forbid recursing twice - - char default_annotation[20] = "default"; -// if(idx > 0) -// snprintf(default_annotation + 7, 13, "[%" PRId32 "]", idx); - const char* const dependent_annotation = "default depends"; - const char* return_value = nullptr; - - if(!port_hint) - port_hint = ports.apropos(port_name); - assert(port_hint); // port must be found - const Port::MetaContainer metadata = port_hint->meta(); - - // Let complex cases depend upon a marker variable - // If the runtime is available the exact preset number can be found - // This generalizes to envelope types nicely if envelopes have a read - // only port which indicates if they're amplitude/frequency/etc - const char* dependent = metadata[dependent_annotation]; - if(dependent) - { - char* dependent_port = buffer; - *dependent_port = 0; - - assert(strlen(port_name) + strlen(dependent_port) + 5 < buffersize); - strncat(dependent_port, port_name, - buffersize - strlen(dependent_port) - 1); - strncat(dependent_port, "/../", - buffersize - strlen(dependent_port) - 1); - strncat(dependent_port, dependent, - buffersize - strlen(dependent_port) - 1); - dependent_port = Ports::collapsePath(dependent_port); - - // TODO: collapsePath bug? - // Relative paths should not start with a slash after collapsing ... - if(*dependent_port == '/') - ++dependent_port; - - const char* dependent_value = - runtime - ? get_value_from_runtime(runtime, ports, - buffersize, loc, - dependent_port, - buffersize-1, 0) - : get_default_value(dependent_port, ports, - runtime, NULL, recursive-1); - - assert(strlen(dependent_value) < 16); // must be an int - - char* default_variant = buffer; - *default_variant = 0; - assert(strlen(default_annotation) + 1 + 16 < buffersize); - strncat(default_variant, default_annotation, - buffersize - strlen(default_variant)); - strncat(default_variant, " ", buffersize - strlen(default_variant)); - strncat(default_variant, dependent_value, - buffersize - strlen(default_variant)); - - return_value = metadata[default_variant]; - } - - // If return_value is NULL, this can have two meanings: - // 1. there was no depedent annotation - // => check for a direct (non-dependent) default value - // (a non existing direct default value is OK) - // 2. there was a dependent annotation, but the dependent value has no - // mapping (mapping for default_variant was NULL) - // => check for the direct default value, which acts as a default - // mapping for all presets; a missing default value indicates an - // error in the metadata - if(!return_value) - { - return_value = metadata[default_annotation]; - assert(!dependent || return_value); - } - - return return_value; -} - int rtosc::canonicalize_arg_vals(rtosc_arg_val_t* av, size_t n, const char* port_args, Port::MetaContainer meta) @@ -1044,560 +727,6 @@ } } -int rtosc::get_default_value(const char* port_name, const char* port_args, - const Ports& ports, - void* runtime, const Port* port_hint, - int32_t idx, - size_t n, rtosc_arg_val_t* res, - char* strbuf, size_t strbufsize) -{ - const char* pretty = get_default_value(port_name, ports, runtime, port_hint, - idx, 0); - - int nargs; - if(pretty) - { - nargs = rtosc_count_printed_arg_vals(pretty); - assert(nargs > 0); // parse error => error in the metadata? - assert((size_t)nargs < n); - - rtosc_scan_arg_vals(pretty, res, nargs, strbuf, strbufsize); - - { - // TODO: port_hint could be NULL here! - int errs_found = canonicalize_arg_vals(res, - nargs, - port_args, - port_hint->meta()); - if(errs_found) - fprintf(stderr, "Could not canonicalize %s for port %s\n", - pretty, port_name); - assert(!errs_found); // error in the metadata? - } - } - else - nargs = -1; - - return nargs; -} - -// TODO: pass base as pointer, so it can be null? -template -void bundle_foreach(const Port& p, char* old_end, - /* data which is only being used by the ftors */ - const char* name_buffer, const Ports& base, - void* data, void* runtime, const F& ftor, - /* options */ - bool expand_bundles = true, - bool cut_afterwards = true) -{ - const char *name = p.name; - char *pos = old_end; - while(*name != '#') *pos++ = *name++; - const unsigned max = atoi(name+1); - while(isdigit(*++name)) ; - - char* pos2; - - if(expand_bundles) - for(unsigned i=0; imeta(); -#if 0 -// practical for debugging if a parameter was changed, but not saved - if(!strncmp(port_buffer, "/part0/partefx2/Phaser/", 23)) - { - puts("break here"); - } -#endif - - if((p->name[strlen(p->name)-1] != ':' && !strstr(p->name, "::")) - || meta.find("parameter") == meta.end()) - { - // runtime information can not be retrieved, - // thus, it can not be compared with the default value - return; - } - else - { // TODO: duplicate to above? (colon[1]) - const char* colon = strchr(p->name, ':'); - if(!colon || !colon[1]) - { - // runtime information can not be loaded, so don't save it - // a possible FEATURE would be to save it anyways - return; - } - } - - char loc[buffersize] = ""; // buffer to hold the dispatched path - rtosc_arg_val_t arg_vals_default[max_arg_vals]; - rtosc_arg_val_t arg_vals_runtime[max_arg_vals]; - // buffer to hold the message (i.e. /port ..., without port's bases) - char buffer_with_port[buffersize]; - char strbuf[buffersize]; // temporary string buffer for pretty-printing - - std::string* res = (std::string*)data; - assert(strlen(port_buffer) + 1 < buffersize); - // copy the path until before the message - strncpy(loc, port_buffer, std::min((ptrdiff_t)buffersize, - port_from_base - port_buffer)); - char* loc_end = loc + (port_from_base - port_buffer); - size_t loc_remain_size = buffersize - (port_from_base - port_buffer); - *loc_end = 0; - - const char* portargs = strchr(p->name, ':'); - if(!portargs) - portargs = p->name + strlen(p->name); - -#if 0 // debugging stuff - if(!strncmp(port_buffer, "/part1/Penabled", 5) && - !strncmp(port_buffer+6, "/Penabled", 9)) - { - printf("runtime: %ld\n", (long int)runtime); - } -#endif -// TODO: p->name: duplicate to p - int nargs_default = get_default_value(p->name, - portargs, - base, - runtime, - p, - -1, - max_arg_vals, - arg_vals_default, - strbuf, - buffersize); - - if(nargs_default > 0) - { - size_t nargs_runtime = 0; - - auto ftor = [&](const Port* p, const char* name_buffer, - const char* old_end, - const Ports& ,void* ,void* runtime) - { - // TODO: strncpy is slow, replace it everywhere - strncpy(buffer_with_port, p->name, buffersize); - - // the caller of ftor (in some cases bundle_foreach) has - // already filled old_end correctly, but we have to copy this - // over to loc_end - strncpy(loc_end, old_end, loc_remain_size); - - size_t - nargs_runtime_cur = get_value_from_runtime(runtime, - *p, - buffersize, loc, - old_end, - buffer_with_port, - buffersize, - max_arg_vals, - arg_vals_runtime + - nargs_runtime); - nargs_runtime += nargs_runtime_cur; - }; - - auto refix_old_end = [&base](const Port* _p, char* _old_end) - { // TODO: remove base capture - bundle_foreach(*_p, _old_end, NULL, - base, NULL, NULL, bundle_foreach_do_nothing, - false, false); - }; - - if(strchr(p->name, '#')) - { - // idea: - // p/a/b - // bundle_foreach => p/a#0/b, p/a#1/b, ... p/a#n/b, p - // bundle_foreach => p/a/b - // => justification for const_cast - - // Skip the array element (type 'a') for now... - ++nargs_runtime; - - // Start filling at arg_vals_runtime + 1 - char* old_end_noconst = const_cast(port_from_base); - bundle_foreach(*p, old_end_noconst, port_buffer + 1, - base, data, runtime, - ftor, true); - - // glue the old end behind old_end_noconst again - refix_old_end(p, old_end_noconst); - - // "Go back" to fill arg_vals_runtime + 0 - arg_vals_runtime[0].type = 'a'; - arg_vals_runtime[0].val.a.len = nargs_runtime-1; - arg_vals_runtime[0].val.a.type = arg_vals_runtime[1].type; - } - else - ftor(p, port_buffer, port_from_base, base, NULL, runtime); - -#if 0 -// practical for debugging if a parameter was changed, but not saved - if(!strncmp(port_buffer, "/part0/partefx2/Phaser/", 23)) - { - puts("break here"); - } -#endif - canonicalize_arg_vals(arg_vals_default, nargs_default, - strchr(p->name, ':'), meta); - - auto write_msg = [&res, &meta, &port_buffer] - (const rtosc_arg_val_t* arg_vals_default, - rtosc_arg_val_t* arg_vals_runtime, - int nargs_default, size_t nargs_runtime) - { - if(!rtosc_arg_vals_eq(arg_vals_default, arg_vals_runtime, - nargs_default, nargs_runtime, nullptr)) - { - char cur_value_pretty[buffersize] = " "; - - map_arg_vals(arg_vals_runtime, nargs_runtime, meta); - - rtosc_print_arg_vals(arg_vals_runtime, nargs_runtime, - cur_value_pretty + 1, buffersize - 1, - NULL, strlen(port_buffer) + 1); - *res += port_buffer; - *res += cur_value_pretty; - *res += "\n"; - } - }; // functor write_msg - - if(arg_vals_runtime[0].type == 'a' && strchr(port_from_base, '/')) - { - // These are grouped as an array, but the port structure - // implicits that they shall be handled as single values - // inside their subtrees - // => We don't print this as an array - // => All arrays in savefiles have their numbers after - // the last port separator ('/') - - // used if the value of lhs or rhs is range-computed: - rtosc_arg_val_t rlhs, rrhs; - - rtosc_arg_val_t_const_itr litr, ritr; - rtosc_arg_val_itr_init(&litr, arg_vals_default+1); - rtosc_arg_val_itr_init(&ritr, arg_vals_runtime+1); - - auto write_msg_adaptor = [&litr, &ritr,&rlhs,&rrhs,&write_msg]( - const Port* p, - const char* port_buffer, const char* old_end, - const Ports&, void*, void*) - { - const rtosc_arg_val_t - * lcur = rtosc_arg_val_itr_get(&litr, &rlhs), - * rcur = rtosc_arg_val_itr_get(&ritr, &rrhs); - - if(!rtosc_arg_vals_eq_single( - rtosc_arg_val_itr_get(&litr, &rlhs), - rtosc_arg_val_itr_get(&ritr, &rrhs), nullptr)) - { - auto get_sz = [](const rtosc_arg_val_t* a) { - return a->type == 'a' ? (a->val.a.len + 1) : 1; }; - // the const-ness does not matter - write_msg(lcur, - const_cast(rcur), - get_sz(lcur), get_sz(rcur)); - } - - rtosc_arg_val_itr_next(&litr); - rtosc_arg_val_itr_next(&ritr); - }; - - char* old_end_noconst = const_cast(port_from_base); - - // iterate over the whole array - bundle_foreach(*p, old_end_noconst, port_buffer, - base, NULL, NULL, - write_msg_adaptor, true); - - // glue the old end behind old_end_noconst again - refix_old_end(p, old_end_noconst); - - } - else - { - write_msg(arg_vals_default, arg_vals_runtime, - nargs_default, nargs_runtime); - } - } - }; - - walk_ports(&ports, port_buffer, buffersize, &res, on_reach_port, false, - runtime); - - if(res.length()) // remove trailing newline - res.resize(res.length()-1); - return res; -} - -bool rtosc::savefile_dispatcher_t::do_dispatch(const char* msg) -{ - *loc = 0; - RtData d; - d.obj = runtime; - d.loc = loc; // we're always dispatching at the base - d.loc_size = 1024; - ports->dispatch(msg, d, true); - return !!d.matches; -} - -int savefile_dispatcher_t::default_response(size_t nargs, - bool first_round, - savefile_dispatcher_t::dependency_t - dependency) -{ - // default implementation: - // no dependencies => round 0, - // has dependencies => round 1, - // not specified => both rounds - return (dependency == not_specified - || !(dependency ^ first_round)) - ? nargs // argument number is not changed - : (int)discard; -} - -int savefile_dispatcher_t::on_dispatch(size_t, char *, - size_t, size_t nargs, - rtosc_arg_val_t *, - bool round2, - dependency_t dependency) -{ - return default_response(nargs, round2, dependency); -} - -int rtosc::dispatch_printed_messages(const char* messages, - const Ports& ports, void* runtime, - savefile_dispatcher_t* dispatcher) -{ - constexpr std::size_t buffersize = 1024; - char portname[buffersize], message[buffersize], strbuf[buffersize]; - int rd, rd_total = 0; - int nargs; - int msgs_read = 0; - bool ok = true; - - savefile_dispatcher_t dummy_dispatcher; - if(!dispatcher) - dispatcher = &dummy_dispatcher; - dispatcher->ports = &ports; - dispatcher->runtime = runtime; - - // scan all messages twice: - // * in the second round, only dispatch those with ports that depend on - // other ports - // * in the first round, only dispatch all others - for(int round = 0; round < 2 && ok; ++round) - { - msgs_read = 0; - rd_total = 0; - const char* msg_ptr = messages; - while(*msg_ptr && ok) - { - nargs = rtosc_count_printed_arg_vals_of_msg(msg_ptr); - if(nargs >= 0) - { - // nargs << 1 is usually too much, but it allows the user to use - // these values (using on_dispatch()) - size_t maxargs = std::max(nargs << 1, 16); - rtosc_arg_val_t arg_vals[maxargs]; - rd = rtosc_scan_message(msg_ptr, portname, buffersize, - arg_vals, nargs, strbuf, buffersize); - rd_total += rd; - - const Port* port = ports.apropos(portname); - savefile_dispatcher_t::dependency_t dependency = - (savefile_dispatcher_t::dependency_t) - (port - ? !!port->meta()["default depends"] - : (int)savefile_dispatcher_t::not_specified); - - // let the user modify the message and the args - // the argument number may have changed, or the user - // wants to discard the message or abort the savefile loading - nargs = dispatcher->on_dispatch(buffersize, portname, - maxargs, nargs, arg_vals, - round, dependency); - - if(nargs == savefile_dispatcher_t::abort) - ok = false; - else - { - if(nargs != savefile_dispatcher_t::discard) - { - size_t max; - const rtosc_arg_val_t* arg_val_ptr; - bool is_array; - if(nargs && arg_vals[0].type == 'a') - { - is_array = true; - max = arg_vals[0].val.a.len; - nargs = 1; // overwrite nargs - arg_val_ptr = arg_vals + 1; - } - else { - is_array = false; - max = 1; - arg_val_ptr = arg_vals; - } - - rtosc_arg_t vals[nargs]; - char argstr[nargs+1]; - char* portname_end = portname + strlen(portname); - - for(size_t arr_idx = 0; arr_idx < max && ok; ++arr_idx) - { - for(int i = 0; i < nargs; ++i) { - vals[i] = arg_val_ptr[arr_idx + i].val; - argstr[i] = arg_val_ptr[arr_idx + i].type; - } - argstr[nargs] = 0; - - if(is_array) - snprintf(portname_end, 8, "%d", (int)arr_idx); - - rtosc_amessage(message, buffersize, portname, - argstr, vals); - - ok = (*dispatcher)(message); - } - } - } - - msg_ptr += rd; - ++msgs_read; - } - else if(nargs == std::numeric_limits::min()) - { - // this means the (rest of the) file is whitespace only - // => don't increase msgs_read - while(*++msg_ptr) ; - } - else { - ok = false; - } - } - } - return ok ? msgs_read : -rd_total-1; -} - -std::string rtosc::save_to_file(const Ports &ports, void *runtime, - const char *appname, rtosc_version appver) -{ - std::string res; - char rtosc_vbuf[12], app_vbuf[12]; - - { - rtosc_version rtoscver = rtosc_current_version(); - rtosc_version_print_to_12byte_str(&rtoscver, rtosc_vbuf); - rtosc_version_print_to_12byte_str(&appver, app_vbuf); - } - - res += "% RT OSC v"; res += rtosc_vbuf; res += " savefile\n" - "% "; res += appname; res += " v"; res += app_vbuf; res += "\n"; - res += get_changed_values(ports, runtime); - - return res; -} - -int rtosc::load_from_file(const char* file_content, - const Ports& ports, void* runtime, - const char* appname, - rtosc_version appver, - savefile_dispatcher_t* dispatcher) -{ - char appbuf[128]; - int bytes_read = 0; - - if(dispatcher) - { - dispatcher->app_curver = appver; - dispatcher->rtosc_curver = rtosc_current_version(); - } - - unsigned vma, vmi, vre; - int n = 0; - - sscanf(file_content, - "%% RT OSC v%u.%u.%u savefile%n ", &vma, &vmi, &vre, &n); - if(n <= 0 || vma > 255 || vmi > 255 || vre > 255) - return -bytes_read-1; - if(dispatcher) - { - dispatcher->rtosc_filever.major = vma; - dispatcher->rtosc_filever.minor = vmi; - dispatcher->rtosc_filever.revision = vre; - } - file_content += n; - bytes_read += n; - n = 0; - - sscanf(file_content, - "%% %128s v%u.%u.%u%n ", appbuf, &vma, &vmi, &vre, &n); - if(n <= 0 || strcmp(appbuf, appname) || vma > 255 || vmi > 255 || vre > 255) - return -bytes_read-1; - - if(dispatcher) - { - dispatcher->app_filever.major = vma; - dispatcher->app_filever.minor = vmi; - dispatcher->app_filever.revision = vre; - } - file_content += n; - bytes_read += n; - n = 0; - - int rval = dispatch_printed_messages(file_content, - ports, runtime, dispatcher); - return (rval < 0) ? (rval-bytes_read) : rval; -} - /* * Miscellaneous */ @@ -1776,6 +905,7 @@ bool port_is_enabled(const Port* port, char* loc, size_t loc_size, const Ports& base, void *runtime) { + // TODO: this code should be improved if(port && runtime) { const char* enable_port = port->meta()["enabled by"]; @@ -1807,32 +937,31 @@ /* concatenate the location string */ + int loclen = strlen(loc); + STACKALLOC(char, loc_copy, loc_size); + strcpy(loc_copy, loc); if(subport) - strncat(loc, "/../", loc_size - strlen(loc) - 1); - strncat(loc, enable_port, loc_size - strlen(loc) - 1); + strncat(loc_copy, "/../", loc_size - loclen - 1); + strncat(loc_copy, enable_port, loc_size - loclen - 4 - 1); - char* collapsed_loc = Ports::collapsePath(loc); - loc_size -= (collapsed_loc - loc); + char* collapsed_loc = Ports::collapsePath(loc_copy); + loc_size -= (collapsed_loc - loc_copy); /* receive the "enabled" property */ - char buf[loc_size]; -#ifdef NEW_CODE - strncpy(buf, collapsed_loc, loc_size); -#else - // TODO: try to use portname_from_base, since Ports might + STACKALLOC(char, buf, loc_size); + // TODO: pass a parameter portname_from_base, since Ports might // also be of type a#N/b const char* last_slash = strrchr(collapsed_loc, '/'); - strncpy(buf, - last_slash ? last_slash + 1 : collapsed_loc, - loc_size); -#endif - get_value_from_runtime(runtime, *ask_port, - loc_size, collapsed_loc, ask_port_str, - buf, 0, 1, &rval); + fast_strcpy(buf, last_slash ? last_slash + 1 : collapsed_loc, + loc_size); + + helpers::get_value_from_runtime(runtime, + *ask_port, loc_size, collapsed_loc, ask_port_str, + buf, 0, 1, &rval); assert(rval.type == 'T' || rval.type == 'F'); - return rval.val.T == 'T'; + return rval.type == 'T'; } else // Port has no "enabled" property, so it is always enabled return true; @@ -1869,23 +998,26 @@ r.obj = runtime; r.port = &p; - char buf[1024]; - strncpy(buf, old_end, 1024); - strncat(buf, "pointer", 1024 - strlen(buf) - 1); + char buf[1024] = ""; + fast_strcpy(buf, old_end, sizeof(buf)); + // there is no "pointer" callback. thus, there will be nothing + // dispatched, but the rRecur*Cb already have set r.obj + // that way, we get our pointer + strncat(buf, "pointer", sizeof(buf) - strlen(buf) - 1); assert(1024 - strlen(buf) >= 8); - strncpy(buf + strlen(buf) + 1, ",", 2); + fast_strcpy(buf + strlen(buf) + 1, ",", 2); p.cb(buf, r); - runtime = r.obj; // callback has stored the child pointer here - // if there is runtime information, but the pointer is NULL, - // the port is not enabled - enabled = (bool) runtime; + // if there is runtime information (see above), but this pointer + // is NULL, the port is not enabled + enabled = (bool) r.obj; // r.obj = the next runtime object if(enabled) { // check if the port is disabled by a switch enabled = port_is_enabled(&p, name_buffer, buffer_size, base, runtime); } + runtime = r.obj; // callback has stored the child pointer here } } if(enabled) @@ -1941,8 +1073,8 @@ } } else { if(strchr(p.name,'#')) { - bundle_foreach(p, old_end, name_buffer, *base, data, runtime, - walker, expand_bundles); + bundle_foreach(p, p.name, old_end, name_buffer, *base, + data, runtime, walker, expand_bundles); } else { //Append the path scat(name_buffer, p.name); @@ -2032,6 +1164,82 @@ } } +void rtosc::path_search(const rtosc::Ports& root, + const char *str, const char* needle, + char *types, std::size_t max_types, + rtosc_arg_t* args, std::size_t max_args) +{ + using rtosc::Ports; + using rtosc::Port; + + if(!needle) + needle = ""; + + // the last char of "types" is being used for a terminating 0 + std::size_t max = std::min(max_types - 1, max_args); + size_t pos = 0; + const Ports *ports = nullptr; + const Port *single_port = nullptr; + + //zero out data + memset(types, 0, max + 1); + memset(args, 0, max); + + if(!*str) { + ports = &root; + } else { + const Port *port = root.apropos(str); + if(port) { + if(port->ports) { + ports = port->ports; + } else { + single_port = port; + } + } + } + + const auto fn = [&pos,&needle,&types,&args,&max](const Port& p) + { + assert(pos < max); + if(p.name && strstr(p.name, needle) == p.name) + { + types[pos] = 's'; + args[pos++].s = p.name; + types[pos] = 'b'; + if(p.metadata && *p.metadata) { + args[pos].b.data = (unsigned char*) p.metadata; + auto tmp = rtosc::Port::MetaContainer(p.metadata); + args[pos++].b.len = tmp.length(); + } else { + args[pos].b.data = (unsigned char*) NULL; + args[pos++].b.len = 0; + } + } + }; + + if(ports) + for(const Port &p:*ports) fn(p); + else if(single_port) + fn(*single_port); +} + +std::size_t rtosc::path_search(const Ports &root, const char *m, + std::size_t max_ports, + char *msgbuf, std::size_t bufsize) +{ + const char *str = rtosc_argument(m,0).s; + const char *needle = rtosc_argument(m,1).s; + size_t max_args = max_ports << 1; + size_t max_types = max_args + 1; + STACKALLOC(char, types, max_types); + STACKALLOC(rtosc_arg_t, args, max_args); + + path_search(root, str, needle, types, max_types, args, max_args); + size_t length = rtosc_amessage(msgbuf, bufsize, + "/paths", types, args); + return length; +} + static void units(std::ostream &o, const char *u) { if(!u) diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/ports-runtime.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/ports-runtime.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/ports-runtime.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/ports-runtime.cpp 2019-03-10 16:16:45.000000000 +0000 @@ -0,0 +1,208 @@ +#include "../util.h" +#include +#include +#include + +#include +#include +#include + +namespace rtosc { +namespace helpers { + +//! RtData subclass to capture argument values pretty-printed from +//! a runtime object +class CapturePretty : public RtData +{ + char* buffer; + std::size_t buffersize; + int cols_used; + + void reply(const char *) override { assert(false); } + +/* void replyArray(const char*, const char *args, + rtosc_arg_t *vals) + { + size_t cur_idx = 0; + for(const char* ptr = args; *ptr; ++ptr, ++cur_idx) + { + assert(cur_idx < max_args); + arg_vals[cur_idx].type = *ptr; + arg_vals[cur_idx].val = vals[cur_idx]; + } + + // TODO: refactor code, also with Capture ? + size_t wrt = rtosc_print_arg_vals(arg_vals, cur_idx, + buffer, buffersize, NULL, + cols_used); + assert(wrt); + }*/ + + void reply_va(const char *args, va_list va) + { + size_t nargs = strlen(args); + STACKALLOC(rtosc_arg_val_t, arg_vals, nargs); + + rtosc_v2argvals(arg_vals, nargs, args, va); + + size_t wrt = rtosc_print_arg_vals(arg_vals, nargs, + buffer, buffersize, NULL, + cols_used); + assert(wrt); + (void)wrt; + } + + void broadcast(const char *, const char *args, ...) override + { + va_list va; + va_start(va,args); + reply_va(args, va); + va_end(va); + } + + void reply(const char *, const char *args, ...) override + { + va_list va; + va_start(va,args); + reply_va(args, va); + va_end(va); + } + +public: + //! Return the argument values, pretty-printed + const char* value() const { return buffer; } + CapturePretty(char* buffer, std::size_t size, int cols_used) : + buffer(buffer), buffersize(size), cols_used(cols_used) {} +}; + +const char* get_value_from_runtime(void* runtime, const Ports& ports, + size_t loc_size, char* loc, + char* buffer_with_port, + std::size_t buffersize, + int cols_used) +{ + std::size_t addr_len = strlen(buffer_with_port); + + // use the port buffer to print the result, but do not overwrite the + // port name + CapturePretty d(buffer_with_port + addr_len, buffersize - addr_len, + cols_used); + d.obj = runtime; + d.loc_size = loc_size; + d.loc = loc; + d.matches = 0; + + // does the message at least fit the arguments? + assert(buffersize - addr_len >= 8); + // append type + memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments + buffer_with_port[addr_len + (4-addr_len%4)] = ','; + + d.message = buffer_with_port; + + // buffer_with_port is a message in this call: + ports.dispatch(buffer_with_port, d, false); + + return d.value(); +} + +//! RtData subclass to capture argument values from a runtime object +class Capture : public RtData +{ + size_t max_args; + rtosc_arg_val_t* arg_vals; + int nargs; + + void chain(const char *path, const char *args, ...) override + { + nargs = 0; + } + + void chain(const char *msg) override + { + nargs = 0; + } + + void reply(const char *) override { assert(false); } + + void replyArray(const char*, const char *args, + rtosc_arg_t *vals) override + { + size_t cur_idx = 0; + for(const char* ptr = args; *ptr; ++ptr, ++cur_idx) + { + assert(cur_idx < max_args); + arg_vals[cur_idx].type = *ptr; + arg_vals[cur_idx].val = vals[cur_idx]; + } + nargs = cur_idx; + } + + void reply_va(const char *args, va_list va) + { + nargs = strlen(args); + assert((size_t)nargs <= max_args); + + rtosc_v2argvals(arg_vals, nargs, args, va); + } + + void broadcast(const char *, const char *args, ...) override + { + va_list va; + va_start(va, args); + reply_va(args, va); + va_end(va); + } + + void reply(const char *, const char *args, ...) override + { + va_list va; + va_start(va,args); + reply_va(args, va); + va_end(va); + } +public: + //! Return the number of argument values stored + int size() const { return nargs; } + Capture(std::size_t max_args, rtosc_arg_val_t* arg_vals) : + max_args(max_args), arg_vals(arg_vals), nargs(-1) {} + //! Silence compiler warnings + std::size_t dont_use_this_function() { return max_args; } +}; + +size_t get_value_from_runtime(void* runtime, const Port& port, + size_t loc_size, char* loc, + const char* portname_from_base, + char* buffer_with_port, std::size_t buffersize, + std::size_t max_args, rtosc_arg_val_t* arg_vals) +{ + fast_strcpy(buffer_with_port, portname_from_base, buffersize); + std::size_t addr_len = strlen(buffer_with_port); + + Capture d(max_args, arg_vals); + d.obj = runtime; + d.loc_size = loc_size; + d.loc = loc; + d.port = &port; + d.matches = 0; + assert(*loc); + + // does the message at least fit the arguments? + assert(buffersize - addr_len >= 8); + // append type + memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments + buffer_with_port[addr_len + (4-addr_len%4)] = ','; + + // TODO? code duplication + + // buffer_with_port is a message in this call: + d.message = buffer_with_port; + port.cb(buffer_with_port, d); + + assert(d.size() >= 0); + return d.size(); +} + +} // namespace helpers +} // namespace rtosc + diff -Nru zynaddsubfx-3.0.3/rtosc/src/cpp/savefile.cpp zynaddsubfx-3.0.4/rtosc/src/cpp/savefile.cpp --- zynaddsubfx-3.0.3/rtosc/src/cpp/savefile.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/cpp/savefile.cpp 2019-03-10 16:16:45.000000000 +0000 @@ -0,0 +1,528 @@ +#include +#include +#include +#include + +#include "../util.h" +#include +#include +#include +#include +#include +#include +#include + +namespace rtosc { + +namespace { + constexpr std::size_t buffersize = 8192; + constexpr size_t max_arg_vals = 2048; +} + +std::string get_changed_values(const Ports& ports, void* runtime) +{ + std::string res; + char port_buffer[buffersize]; + memset(port_buffer, 0, buffersize); // requirement for walk_ports + + auto on_reach_port = + [](const Port* p, const char* port_buffer, + const char* port_from_base, const Ports& base, + void* data, void* runtime) + { + + assert(runtime); + const Port::MetaContainer meta = p->meta(); +#if 0 +// practical for debugging if a parameter was changed, but not saved + const char* cmp = "/part15/kit0/adpars/GlobalPar/Reson/Prespoints"; + if(!strncmp(port_buffer, cmp, strlen(cmp))) + { + puts("break here"); + } +#endif + + if((p->name[strlen(p->name)-1] != ':' && !strstr(p->name, "::")) + || meta.find("parameter") == meta.end()) + { + // runtime information can not be retrieved, + // thus, it can not be compared with the default value + return; + } + else + { // TODO: duplicate to above? (colon[1]) + const char* colon = strchr(p->name, ':'); + if(!colon || !colon[1]) + { + // runtime information can not be loaded, so don't save it + // a possible FEATURE would be to save it anyways + return; + } + } + + char loc[buffersize] = ""; // buffer to hold the dispatched path + rtosc_arg_val_t arg_vals_default[max_arg_vals]; + rtosc_arg_val_t arg_vals_runtime[max_arg_vals]; + // buffer to hold the message (i.e. /port ..., without port's bases) + char buffer_with_port[buffersize]; + char strbuf[buffersize]; // temporary string buffer for pretty-printing + + std::string* res = (std::string*)data; + assert(strlen(port_buffer) + 1 < buffersize); + // copy the path until before the message + fast_strcpy(loc, port_buffer, std::min((ptrdiff_t)buffersize, + port_from_base - port_buffer + 1 + )); + char* loc_end = loc + (port_from_base - port_buffer); + size_t loc_remain_size = buffersize - (port_from_base - port_buffer); + *loc_end = 0; + + const char* portargs = strchr(p->name, ':'); + if(!portargs) + portargs = p->name + strlen(p->name); + +#if 0 // debugging stuff + if(!strncmp(port_buffer, "/part1/Penabled", 5) && + !strncmp(port_buffer+6, "/Penabled", 9)) + { + printf("runtime: %ld\n", (long int)runtime); + } +#endif +// TODO: p->name: duplicate to p + int nargs_default = get_default_value(p->name, + portargs, + base, + runtime, + p, + -1, + max_arg_vals, + arg_vals_default, + strbuf, + buffersize); + + if(nargs_default > 0) + { + size_t nargs_runtime = 0; + + auto ftor = [&](const Port* p, const char* , + const char* old_end, + const Ports& ,void* ,void* runtime) + { + fast_strcpy(buffer_with_port, p->name, buffersize); + + // the caller of ftor (in some cases bundle_foreach) has + // already filled old_end correctly, but we have to copy this + // over to loc_end + fast_strcpy(loc_end, old_end, loc_remain_size); + + size_t nargs_runtime_cur = + helpers::get_value_from_runtime(runtime, *p, + buffersize, loc, old_end, + buffer_with_port, + buffersize, + max_arg_vals, + arg_vals_runtime + + nargs_runtime); + nargs_runtime += nargs_runtime_cur; + }; + + auto refix_old_end = [&base](const Port* _p, char* _old_end) + { // TODO: remove base capture + bundle_foreach(*_p, _p->name, _old_end, NULL, + base, NULL, NULL, bundle_foreach_do_nothing, + false, false); + }; + + if(strchr(p->name, '#')) + { + // idea: + // p/a/b + // bundle_foreach => p/a#0/b, p/a#1/b, ... p/a#n/b, p + // bundle_foreach => p/a/b + // => justification for const_cast + + // Skip the array element (type 'a') for now... + ++nargs_runtime; + + // Start filling at arg_vals_runtime + 1 + char* old_end_noconst = const_cast(port_from_base); + bundle_foreach(*p, p->name, old_end_noconst, port_buffer + 1, + base, data, runtime, + ftor, true); + + // glue the old end behind old_end_noconst again + refix_old_end(p, old_end_noconst); + + // "Go back" to fill arg_vals_runtime + 0 + arg_vals_runtime[0].type = 'a'; + arg_vals_runtime[0].val.a.len = nargs_runtime-1; + arg_vals_runtime[0].val.a.type = arg_vals_runtime[1].type; + } + else + ftor(p, port_buffer, port_from_base, base, NULL, runtime); + +#if 0 +// practical for debugging if a parameter was changed, but not saved + const char* cmp = "/part15/kit0/adpars/GlobalPar/Reson/Prespoints"; + if(!strncmp(port_buffer, cmp, strlen(cmp))) + { + puts("break here"); + } +#endif + canonicalize_arg_vals(arg_vals_default, nargs_default, + strchr(p->name, ':'), meta); + + auto write_msg = [&res, &meta, &port_buffer] + (const rtosc_arg_val_t* arg_vals_default, + rtosc_arg_val_t* arg_vals_runtime, + int nargs_default, size_t nargs_runtime) + { + if(!rtosc_arg_vals_eq(arg_vals_default, arg_vals_runtime, + nargs_default, nargs_runtime, nullptr)) + { + char cur_value_pretty[buffersize] = " "; + + map_arg_vals(arg_vals_runtime, nargs_runtime, meta); + + rtosc_print_arg_vals(arg_vals_runtime, nargs_runtime, + cur_value_pretty + 1, buffersize - 1, + NULL, strlen(port_buffer) + 1); + *res += port_buffer; + *res += cur_value_pretty; + *res += "\n"; + } + }; // functor write_msg + + if(arg_vals_runtime[0].type == 'a' && strchr(port_from_base, '/')) + { + // These are grouped as an array, but the port structure + // implicits that they shall be handled as single values + // inside their subtrees + // => We don't print this as an array + // => All arrays in savefiles have their numbers after + // the last port separator ('/') + + // used if the value of lhs or rhs is range-computed: + rtosc_arg_val_t rlhs, rrhs; + + rtosc_arg_val_itr litr, ritr; + rtosc_arg_val_itr_init(&litr, arg_vals_default+1); + rtosc_arg_val_itr_init(&ritr, arg_vals_runtime+1); + + auto write_msg_adaptor = [&litr, &ritr,&rlhs,&rrhs,&write_msg]( + const Port* p, + const char* port_buffer, const char* old_end, + const Ports&, void*, void*) + { + const rtosc_arg_val_t + * lcur = rtosc_arg_val_itr_get(&litr, &rlhs), + * rcur = rtosc_arg_val_itr_get(&ritr, &rrhs); + + if(!rtosc_arg_vals_eq_single( + rtosc_arg_val_itr_get(&litr, &rlhs), + rtosc_arg_val_itr_get(&ritr, &rrhs), nullptr)) + { + auto get_sz = [](const rtosc_arg_val_t* a) { + return a->type == 'a' ? (a->val.a.len + 1) : 1; }; + // the const-ness does not matter + write_msg(lcur, + const_cast(rcur), + get_sz(lcur), get_sz(rcur)); + } + + rtosc_arg_val_itr_next(&litr); + rtosc_arg_val_itr_next(&ritr); + }; + + char* old_end_noconst = const_cast(port_from_base); + + // iterate over the whole array + bundle_foreach(*p, p->name, old_end_noconst, port_buffer, + base, NULL, NULL, + write_msg_adaptor, true); + + // glue the old end behind old_end_noconst again + refix_old_end(p, old_end_noconst); + + } + else + { + write_msg(arg_vals_default, arg_vals_runtime, + nargs_default, nargs_runtime); + } + } + }; + + walk_ports(&ports, port_buffer, buffersize, &res, on_reach_port, false, + runtime); + + if(res.length()) // remove trailing newline + res.resize(res.length()-1); + return res; +} + +bool savefile_dispatcher_t::do_dispatch(const char* msg) +{ + *loc = 0; + RtData d; + d.obj = runtime; + d.loc = loc; // we're always dispatching at the base + d.loc_size = 1024; + ports->dispatch(msg, d, true); + return !!d.matches; +} + +int savefile_dispatcher_t::default_response(size_t nargs, + bool first_round, + savefile_dispatcher_t::dependency_t + dependency) +{ + // default implementation: + // no dependencies => round 0, + // has dependencies => round 1, + // not specified => both rounds + return (dependency == not_specified + || !(dependency ^ first_round)) + ? nargs // argument number is not changed + : (int)discard; +} + +int savefile_dispatcher_t::on_dispatch(size_t, char *, + size_t, size_t nargs, + rtosc_arg_val_t *, + bool round2, + dependency_t dependency) +{ + return default_response(nargs, round2, dependency); +} + +int dispatch_printed_messages(const char* messages, + const Ports& ports, void* runtime, + savefile_dispatcher_t* dispatcher) +{ + constexpr std::size_t buffersize = 8192; + char portname[buffersize], message[buffersize], strbuf[buffersize]; + int rd, rd_total = 0; + int nargs; + int msgs_read = 0; + bool ok = true; + + savefile_dispatcher_t dummy_dispatcher; + if(!dispatcher) + dispatcher = &dummy_dispatcher; + dispatcher->ports = &ports; + dispatcher->runtime = runtime; + + // scan all messages twice: + // * in the second round, only dispatch those with ports that depend on + // other ports + // * in the first round, only dispatch all others + for(int round = 0; round < 2 && ok; ++round) + { + msgs_read = 0; + rd_total = 0; + const char* msg_ptr = messages; + while(*msg_ptr && ok) + { + nargs = rtosc_count_printed_arg_vals_of_msg(msg_ptr); + if(nargs >= 0) + { + // nargs << 1 is usually too much, but it allows the user to use + // these values (using on_dispatch()) + size_t maxargs = std::max(nargs << 1, 16); + STACKALLOC(rtosc_arg_val_t, arg_vals, maxargs); + rd = rtosc_scan_message(msg_ptr, portname, buffersize, + arg_vals, nargs, strbuf, buffersize); + rd_total += rd; + + const Port* port = ports.apropos(portname); + savefile_dispatcher_t::dependency_t dependency = + (savefile_dispatcher_t::dependency_t) + (port + ? !!port->meta()["default depends"] + : (int)savefile_dispatcher_t::not_specified); + + // let the user modify the message and the args + // the argument number may have changed, or the user + // wants to discard the message or abort the savefile loading + nargs = dispatcher->on_dispatch(buffersize, portname, + maxargs, nargs, arg_vals, + round, dependency); + + if(nargs == savefile_dispatcher_t::abort) + ok = false; + else + { + if(nargs != savefile_dispatcher_t::discard) + { + const rtosc_arg_val_t* arg_val_ptr; + bool is_array; + if(nargs && arg_vals[0].type == 'a') + { + is_array = true; + // arrays of arrays are not yet supported - + // neither by rtosc_*message, nor by the inner for + // loop below. + // arrays will probably have an 'a' (or #) + assert(arg_vals[0].val.a.type != 'a' && + arg_vals[0].val.a.type != '#'); + // we won't read the array arg val anymore + --nargs; + arg_val_ptr = arg_vals + 1; + } + else { + is_array = false; + arg_val_ptr = arg_vals; + } + + char* portname_end = portname + strlen(portname); + + rtosc_arg_val_itr itr; + rtosc_arg_val_t buffer; + const rtosc_arg_val_t* cur; + + rtosc_arg_val_itr_init(&itr, arg_val_ptr); + + // for bundles, send each element separately + // for non-bundles, send all elements at once + for(size_t arr_idx = 0; + itr.i < (size_t)std::max(nargs,1) && ok; ++arr_idx) + { + // this will fail for arrays of arrays, + // since it only copies one arg val + // (arrays are not yet specified) + size_t i; + const size_t last_pos = itr.i; + const size_t elem_limit = is_array + ? 1 : std::numeric_limits::max(); + + // equivalent to the for loop below, in order to + // find out the array size + size_t val_max = 0; + { + rtosc_arg_val_itr itr2 = itr; + for(val_max = 0; + itr2.i - last_pos < (size_t)nargs && + val_max < elem_limit; + ++val_max) + { + rtosc_arg_val_itr_next(&itr2); + } + } + STACKALLOC(rtosc_arg_t, vals, val_max); + STACKALLOC(char, argstr, val_max+1); + + for(i = 0; + itr.i - last_pos < (size_t)nargs && + i < elem_limit; + ++i) + { + cur = rtosc_arg_val_itr_get(&itr, &buffer); + vals[i] = cur->val; + argstr[i] = cur->type; + rtosc_arg_val_itr_next(&itr); + } + + argstr[i] = 0; + + if(is_array) + snprintf(portname_end, 8, "%d", (int)arr_idx); + + rtosc_amessage(message, buffersize, portname, + argstr, vals); + + ok = (*dispatcher)(message); + } + } + } + + msg_ptr += rd; + ++msgs_read; + } + else if(nargs == std::numeric_limits::min()) + { + // this means the (rest of the) file is whitespace only + // => don't increase msgs_read + while(*++msg_ptr) ; + } + else { + ok = false; + } + } + } + return ok ? msgs_read : -rd_total-1; +} + +std::string save_to_file(const Ports &ports, void *runtime, + const char *appname, rtosc_version appver) +{ + std::string res; + char rtosc_vbuf[12], app_vbuf[12]; + + { + rtosc_version rtoscver = rtosc_current_version(); + rtosc_version_print_to_12byte_str(&rtoscver, rtosc_vbuf); + rtosc_version_print_to_12byte_str(&appver, app_vbuf); + } + + res += "% RT OSC v"; res += rtosc_vbuf; res += " savefile\n" + "% "; res += appname; res += " v"; res += app_vbuf; res += "\n"; + res += get_changed_values(ports, runtime); + + return res; +} + +int load_from_file(const char* file_content, + const Ports& ports, void* runtime, + const char* appname, + rtosc_version appver, + savefile_dispatcher_t* dispatcher) +{ + char appbuf[128]; + int bytes_read = 0; + + if(dispatcher) + { + dispatcher->app_curver = appver; + dispatcher->rtosc_curver = rtosc_current_version(); + } + + unsigned vma, vmi, vre; + int n = 0; + + sscanf(file_content, + "%% RT OSC v%u.%u.%u savefile%n ", &vma, &vmi, &vre, &n); + if(n <= 0 || vma > 255 || vmi > 255 || vre > 255) + return -bytes_read-1; + if(dispatcher) + { + dispatcher->rtosc_filever.major = vma; + dispatcher->rtosc_filever.minor = vmi; + dispatcher->rtosc_filever.revision = vre; + } + file_content += n; + bytes_read += n; + n = 0; + + sscanf(file_content, + "%% %128s v%u.%u.%u%n ", appbuf, &vma, &vmi, &vre, &n); + if(n <= 0 || strcmp(appbuf, appname) || vma > 255 || vmi > 255 || vre > 255) + return -bytes_read-1; + + if(dispatcher) + { + dispatcher->app_filever.major = vma; + dispatcher->app_filever.minor = vmi; + dispatcher->app_filever.revision = vre; + } + file_content += n; + bytes_read += n; + n = 0; + + int rval = dispatch_printed_messages(file_content, + ports, runtime, dispatcher); + return (rval < 0) ? (rval-bytes_read) : rval; +} + +} + diff -Nru zynaddsubfx-3.0.3/rtosc/src/dispatch.c zynaddsubfx-3.0.4/rtosc/src/dispatch.c --- zynaddsubfx-3.0.3/rtosc/src/dispatch.c 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/dispatch.c 2019-03-10 16:16:45.000000000 +0000 @@ -2,7 +2,9 @@ #include #include #include +#ifndef _MSC_VER #include +#endif #include diff -Nru zynaddsubfx-3.0.3/rtosc/src/pretty-format.c zynaddsubfx-3.0.4/rtosc/src/pretty-format.c --- zynaddsubfx-3.0.3/rtosc/src/pretty-format.c 2017-12-05 01:01:05.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/pretty-format.c 2019-03-10 16:16:45.000000000 +0000 @@ -1,3 +1,4 @@ +#include "util.h" #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include /** @file pretty-format.c @@ -45,13 +47,13 @@ //! delta args (from ranges) seen as @p additional_for_delta args static int next_arg_offset(const rtosc_arg_val_t* cur) { - return (cur->type == 'a') + return (cur->type == 'a' || cur->type == ' ') ? cur->val.a.len + 1 : (cur->type == '-') - ? 1 /* range arg */ - + next_arg_offset(cur + 1) /* start arg */ - + (int) cur->val.r.has_delta /* delta arg */ - : 1; + ? 1 /* range arg */ + + next_arg_offset(cur + 1) /* start arg */ + + (int) cur->val.r.has_delta /* delta arg */ + : 1; } /** @@ -111,7 +113,7 @@ // insert "\n " *last_sep = '\n'; assert(*bs >= 4); - memmove(last_sep+5, last_sep+1, inc); + memmove(last_sep+5, last_sep+1, 1+inc); last_sep[1] = last_sep[2] = last_sep[3] = last_sep[4] = ' '; *cols_used = 4 + inc; *wrt += 4; @@ -121,10 +123,8 @@ } } -size_t rtosc_print_arg_val(const rtosc_arg_val_t *arg, - char *buffer, size_t bs, - const rtosc_print_options* opt, int *cols_used) -{ +const size_t range_min = 5; + #define COUNT_UP(number) { int _n1 = number; bs -= _n1; buffer += _n1; \ wrt += _n1; } #define COUNT_UP_COL(number) { int _n2 = number; \ @@ -132,6 +132,230 @@ #define COUNT_UP_WRITE(c) { assert(bs); \ *buffer++ = c; --bs; ++wrt; ++*cols_used; } +static int rtosc_print_range(const rtosc_arg_val_t* arg, + char* buffer, size_t bs, + const rtosc_print_options* opt, int* cols_used) +{ + size_t wrt = 0; + int start; + const rtosc_arg_t* val = &arg->val; + + // prepare for the loop + if(opt->compress_ranges || !val->r.num) + { + if(val->r.has_delta || !val->r.num) + { + // delta or endless range (endless has no delta) + + const rtosc_arg_val_t* first_arg = arg+1+!!val->r.has_delta; + int tmp = rtosc_print_arg_val(first_arg, buffer, bs, + opt, cols_used); + COUNT_UP(tmp); + + if(val->r.has_delta) + { + rtosc_arg_val_t one, m_one; + rtosc_arg_val_from_int( &one, first_arg->type, 1); + rtosc_arg_val_from_int(&m_one, first_arg->type, -1); + if( !rtosc_arg_vals_eq_single(arg+1, &one, NULL) + && !rtosc_arg_vals_eq_single(arg+1, &m_one, NULL)) + { + asnprintf(buffer, bs, " "); + COUNT_UP_COL(1); + + rtosc_arg_val_t second; + rtosc_arg_val_range_arg(arg, 1, &second); + tmp = rtosc_print_arg_val(&second, buffer, bs, + opt, cols_used); + COUNT_UP(tmp); + } + } + + asnprintf(buffer, bs, " ... "); + COUNT_UP_COL(5); + + // print only the last value + // (or nothing if the range is infinite) + start = val->r.num - (!!val->r.num); + } + else + { + int tmp; + // print "nx..." + tmp = asnprintf(buffer, bs, "%dx", val->r.num); + COUNT_UP_COL(tmp); + tmp = rtosc_print_arg_val(arg+1, buffer, bs, + opt, cols_used); + COUNT_UP(tmp); + + // skip loop below + start = val->r.num; + } + + } + else { + start = 0; // print the whole range + } + + // loop over all args of the range + char* last_sep = buffer - 1; + int args_written_this_line = (cols_used) ? 1 : 0; + + for(int i = start; i < val->r.num; ++i) + { + rtosc_arg_val_t tmparg; + const rtosc_arg_val_t *cur; + if(val->r.has_delta) + { + rtosc_arg_val_range_arg(arg, i, &tmparg); + cur = &tmparg; + } + else + cur = arg + 1; + + size_t tmp = rtosc_print_arg_val(cur, buffer, bs, + opt, cols_used); + COUNT_UP(tmp); + + linebreak_check_after_write(cols_used, &wrt, + last_sep, &buffer, &bs, + tmp, &args_written_this_line, + opt->linelength); + + last_sep = buffer; + COUNT_UP_WRITE(' '); + } + + if(start < val->r.num) { + // => remove last separator, that we have written too much + buffer[-1] = 0; + --wrt; // we have overridden space with '\0', and '\0' doesn't count + } + + return wrt; +} + +// only used in the function below (print_arg_val_range) +static size_t incsize(const rtosc_arg_val_t* av) { + return av->type == 'a' ? av->val.a.len + 1 : 1; +} + +//! insert a range block at the arg at position arg +//! arg and lhsarg may be the same pointer +static void insert_arg_range(rtosc_arg_val_t* arg, int32_t num, + const rtosc_arg_val_t* lhsarg, + int has_delta, const rtosc_arg_val_t* delta, + int lhs_overlap, + int delta_less_is_endless) +{ + rtosc_arg_val_t* first_arg = arg; + if(has_delta) + *++arg = *delta; + if(lhs_overlap) + { + if(lhsarg->type == 'a') + { + // we inserted an 'a' element, so we need to + // shift more than one element + // the shifting amount is always one since array ranges + // are delta-less + // we have: '-' a a a a + // => '-' 'a' a a a a + for(int32_t i = lhsarg->val.a.len; i > 0; --i) + arg[i+1] = arg[i]; + } + *++arg = *lhsarg; + } + else + { + memcpy(++arg, lhsarg, incsize(lhsarg) * sizeof(rtosc_arg_val_t)); + } + first_arg->type = '-'; + first_arg->val.r.num = (!has_delta && delta_less_is_endless) ? 0 : num; + first_arg->val.r.has_delta = has_delta; +} + +static const char* numeric_range_types() { return "cihfdTF"; } + +static const char* numeric_range_convertible_types() +{ + // note: floats can not be converted to counting ranges safely + // (at least, not that I knew): + // 0.00 0.33 0.67 0.10 => is it a counting range? + return "cihTF"; +} + +//! tries to convert all args starting at @a arg into +//! an arg val range - if possible +//! @param arg_out array, output which must have the size of arg or more; +//! may be the same as arg +//! @return The number of really skipped argument values +static int32_t rtosc_convert_to_range(const rtosc_arg_val_t* const arg, + size_t size, + rtosc_arg_val_t* arg_out, + const rtosc_print_options* opt) +{ + if(size < range_min || arg[0].type == '-' || !opt->compress_ranges) + return 0; + char type = arg->type; + size_t num_common = 0; + for(size_t i = 0; i < size; i += incsize(arg+i), ++num_common) + { + if(type != arg[i].type) + break; + } + if(num_common < range_min) + return 0; + + int32_t skipped; + num_common = 1; + int has_delta; + rtosc_arg_val_t delta, added; + + if(rtosc_arg_vals_eq_single(arg, arg + incsize(arg), NULL)) + has_delta = 0; + else if(strchr(numeric_range_convertible_types(), arg->type)) { + has_delta = 1; + rtosc_arg_val_sub(arg+1, arg, &delta); + } + else return 0; + + { + int go_on = 1; + size_t next; + for(skipped = incsize(arg); go_on; skipped = next, ++num_common) + { + next = skipped + incsize(arg+skipped); + + if(has_delta) + rtosc_arg_val_add(arg+skipped, &delta, &added); + + if(next >= size || !rtosc_arg_vals_eq_single(has_delta ? &added + : arg, + arg+next, NULL)) + go_on = false; + } + } + + if(num_common >= range_min) + { + insert_arg_range(arg_out, num_common, arg, has_delta, &delta, + false, false); + const rtosc_arg_val_t* old_arg_out = arg_out; + arg_out += 1 + has_delta; + arg_out += incsize(arg); + arg_out->type = ' '; + arg_out->val.a.len = skipped - (arg_out - old_arg_out) - 1; + return skipped; + } + else + return 0; +} + +size_t rtosc_print_arg_val(const rtosc_arg_val_t *arg, + char *buffer, size_t bs, + const rtosc_print_options* opt, int *cols_used) +{ size_t wrt = 0; if(!opt) opt = default_print_options; @@ -142,22 +366,22 @@ { case 'T': assert(bs>4); - strncpy(buffer, "true", bs); + fast_strcpy(buffer, "true", bs); wrt = 4; break; case 'F': assert(bs>5); - strncpy(buffer, "false", bs); + fast_strcpy(buffer, "false", bs); wrt = 5; break; case 'N': assert(bs>3); - strncpy(buffer, "nil", bs); + fast_strcpy(buffer, "nil", bs); wrt = 3; break; case 'I': assert(bs>3); - strncpy(buffer, "inf", bs); + fast_strcpy(buffer, "inf", bs); wrt = 3; break; case 'h': @@ -283,7 +507,7 @@ for(const char* s = val->s; *s; ++s) { // "3": 2 quote signs and escaping backslash - if(*cols_used > opt->linelength - 3) + if(!plain && *cols_used > opt->linelength - 3) break_string(&b, bs, wrt, cols_used); assert(bs); int as_esc = as_escaped_char(*s, false); @@ -292,7 +516,7 @@ *b++ = '\\'; *b++ = as_esc; *cols_used += 2; - if(as_esc == 'n') + if(!plain && as_esc == 'n') break_string(&b, bs, wrt, cols_used); } else { @@ -339,14 +563,18 @@ { char* last_sep = buffer - 1; int args_written_this_line = (cols_used) ? 1 : 0; + STACKALLOC(rtosc_arg_val_t, args_converted, val->a.len); // range conversion COUNT_UP_WRITE('['); if(val->a.len) for(int32_t i = 1; i <= val->a.len; ) { - size_t tmp = rtosc_print_arg_val(arg+i, buffer, bs, + int32_t conv = rtosc_convert_to_range(arg+i, val->a.len+1-i, args_converted, opt); + const rtosc_arg_val_t* input = conv ? args_converted : arg+i; + + size_t tmp = rtosc_print_arg_val(input, buffer, bs, opt, cols_used); - i += next_arg_offset(arg+i); + i += conv ? conv : next_arg_offset(arg+i); COUNT_UP(tmp); linebreak_check_after_write(cols_used, &wrt, @@ -369,75 +597,7 @@ } case '-': { - int start; - - // prepare for the loop - if(opt->compress_ranges || !val->r.num) - { - if(val->r.has_delta || !val->r.num) - { - int tmp = rtosc_print_arg_val(arg+1+!!val->r.has_delta, - buffer, bs, - opt, cols_used); - COUNT_UP(tmp); - - asnprintf(buffer, bs, " ... "); - COUNT_UP_COL(5); - - // print only the last value - // (or nothing if the range is infinite) - start = val->r.num - (!!val->r.num); - } - else - { - int tmp; - // print "nx..." - tmp = asnprintf(buffer, bs, "%dx", arg->val.r.num + 1); - COUNT_UP_COL(tmp); - tmp = rtosc_print_arg_val(arg+1, buffer, bs, - opt, cols_used); - COUNT_UP(tmp); - - // skip loop below - start = val->r.num; - } - - } - else { - start = 0; // print the whole range - } - - // loop over all args of the range - char* last_sep = buffer - 1; - int args_written_this_line = (cols_used) ? 1 : 0; - - for(int i = start; i < val->r.num; ++i) - { - rtosc_arg_val_t tmparg; - const rtosc_arg_val_t *cur; - if(arg->val.r.has_delta) - { - rtosc_arg_val_range_arg(arg, i, &tmparg); - cur = &tmparg; - } - else - cur = arg + 1; - - size_t tmp = rtosc_print_arg_val(cur, buffer, bs, - opt, cols_used); - COUNT_UP(tmp); - - linebreak_check_after_write(cols_used, &wrt, - last_sep, &buffer, &bs, - tmp, &args_written_this_line, - opt->linelength); - - last_sep = buffer; - COUNT_UP_WRITE(' '); - } - - buffer[-1] = 0; - --wrt; // we have overridden space with '\0', and '\0' doesn't count + wrt += rtosc_print_range(arg, buffer, bs, opt, cols_used); break; } default: @@ -458,10 +618,10 @@ } return wrt; +} #undef COUNT_UP_COL #undef COUNT_UP #undef COUNT_UP_WRITE -} size_t rtosc_print_arg_vals(const rtosc_arg_val_t *args, size_t n, char *buffer, size_t bs, @@ -473,30 +633,32 @@ opt = default_print_options; size_t sep_len = strlen(opt->sep); char* last_sep = buffer - 1; - char lasttype[2] = "x"; + STACKALLOC(rtosc_arg_val_t, args_converted, n); // only used for range conversion for(size_t i = 0; i < n;) { - size_t tmp = rtosc_print_arg_val(args, buffer, bs, opt, &cols_used); + int32_t conv = rtosc_convert_to_range(args, n-i, args_converted, opt); + const rtosc_arg_val_t* input = conv ? args_converted : args; + + size_t tmp = rtosc_print_arg_val(input, buffer, bs, opt, &cols_used); wrt += tmp; buffer += tmp; bs -= tmp; - *lasttype = args->type; // these compute the newlines themselves - if(! strpbrk(lasttype, "-asSb")) + if(! strchr("-asb", args->type)) linebreak_check_after_write(&cols_used, &wrt, last_sep, &buffer, &bs, tmp, &args_written_this_line, opt->linelength); - size_t inc = next_arg_offset(args); + size_t inc = conv ? conv : next_arg_offset(args); i += inc; args += inc; if(isep, bs); + fast_strcpy(buffer, opt->sep, bs); cols_used += sep_len; wrt += sep_len; buffer += sep_len; @@ -612,15 +774,14 @@ } /** Return the right format string for reading the next numeric value */ -static const char* scanf_fmtstr_scan(const char* src, char* bytes8, - char* type) +static const char* scanf_fmtstr_scan(const char* src, char* bytes16, char* type) { const char *buf = scanf_fmtstr(src, type); assert(buf); - assert(bytes8); - strncpy(bytes8, buf, 8); - *++bytes8 = '%'; // transform "%*" to "%" - return bytes8; + assert(bytes16); + fast_strcpy(bytes16, buf, 16); + *++bytes16 = '%'; // transform "%*" to "%" + return bytes16; } /** Skip the next numeric at @p src */ @@ -693,7 +854,7 @@ /** * Skips string @p exp at the current string pointed to by @p str, * but only if it's a separated word, i.e. if there's a char after the string, - * it mus be a slash or whitespace. + * it must be a slash or whitespace. * * @return The position after the word, or NULL if the word was not present * at @p str . @@ -798,16 +959,19 @@ return rval; } -char arraytypes_match(char type1, char type2) +int types_match(char type1, char type2) { return (type1 == type2) || - type1 == '-' || type2 == '-' || // ranges match everything - // (TODO: actually a bug) (type1 == 'T' && type2 == 'F') || (type1 == 'F' && type2 == 'T'); } -static const char* numeric_range_types() { return "cihfd"; } +int arraytypes_match(char type1, char type2) +{ + return type1 == '-' || type2 == '-' || // ranges match everything + // (TODO: actually a bug) + types_match(type1, type2); +} const char* rtosc_skip_next_printed_arg(const char* src, int* skipped, char* type, const char* llhssrc, @@ -1105,10 +1269,8 @@ rhssrc += 2; while(isspace(*++rhssrc)) ; - char lhstype_arr[2] = { lhstype, 0 }; - bool numeric_range = strpbrk(lhstype_arr, - numeric_range_types()); - bool infinite_range = false; + bool numeric_range = strchr(numeric_range_types(), lhstype), + infinite_range = false; const char* endsrc; if(*rhssrc == ']') { @@ -1167,7 +1329,7 @@ rtosc_skip_next_printed_arg(llhssrc, &llhsskipped, &llhstype, NULL, 0, inside_bundle); - if(llhstype == lhstype) + if(types_match(llhstype, lhstype)) { rtosc_scan_arg_val(llhssrc, &llhsarg, 1, NULL, &zero, 0, 0); @@ -1320,12 +1482,16 @@ { rtosc_arg_val_immediatelly(arg); } - else if(skip_word("nil", &src) || - skip_word("inf", &src) || - skip_word("true", &src) || + else if(skip_word("true", &src) || skip_word("false", &src) ) { - arg->type = arg->val.T = toupper(*src_backup); + arg->type = toupper(*src_backup); + arg->val.T = (src[-2] == 'u'); + } + else if(skip_word("nil", &src) || + skip_word("inf", &src)) + { + arg->type = toupper(*src_backup); } else { @@ -1449,6 +1615,8 @@ start_arg->type = 'a'; start_arg->val.a.type = arrtype; start_arg->val.a.len = num_read; + + arg = start_arg; // invariant: arg is back at the start break; } case 'B': // blob @@ -1571,7 +1739,7 @@ } else { - char bytes8[8]; + char bytes16[16]; char type = arg->type = 0; bool repeat_once = false; @@ -1579,7 +1747,7 @@ { rd = 0; - const char *fmtstr = scanf_fmtstr_scan(src, bytes8, + const char *fmtstr = scanf_fmtstr_scan(src, bytes16, &type); if(!arg->type) // the first occurence determins the type arg->type = type; @@ -1663,7 +1831,7 @@ bool llhsarg_is_useless = (args_before < 1 || - lhsarg.type == '-' || llhsarg->type != lhsarg.type + lhsarg.type == '-' || !types_match(llhsarg->type, lhsarg.type) /* this includes llhsarg == '-' */ ); bool has_delta = true; @@ -1671,6 +1839,7 @@ if(infinite_range && llhsarg_is_useless) { has_delta = false; + num = 0; // suppress compiler warnings } else { @@ -1685,12 +1854,7 @@ } } - arg->type = '-'; - arg->val.r.num = has_delta ? num : 0; - arg->val.r.has_delta = has_delta; - if(has_delta) - *++arg = delta; - *++arg = lhsarg; + insert_arg_range(arg, num, &lhsarg, has_delta, &delta, true, true); } return (size_t)(src-start); diff -Nru zynaddsubfx-3.0.3/rtosc/src/rtosc.c zynaddsubfx-3.0.4/rtosc/src/rtosc.c --- zynaddsubfx-3.0.3/rtosc/src/rtosc.c 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/rtosc.c 2019-03-10 16:16:45.000000000 +0000 @@ -8,6 +8,7 @@ #include #include +#include "util.h" const char *rtosc_argument_string(const char *msg) { @@ -233,20 +234,14 @@ return pos; } -static const rtosc_cmp_options default_cmp_options = { 0.0 }; - -void rtosc_arg_val_itr_init(rtosc_arg_val_t_const_itr* itr, +void rtosc_arg_val_itr_init(rtosc_arg_val_itr* itr, const rtosc_arg_val_t* av) { itr->av = av; itr->i = itr->range_i = 0; } -//! helper function for arg val comparing -//! this usually just returns the value from operand, except for range operands, -//! where the value is being interpolated -const rtosc_arg_val_t* rtosc_arg_val_itr_get( - const rtosc_arg_val_t_const_itr* itr, +const rtosc_arg_val_t* rtosc_arg_val_itr_get(const rtosc_arg_val_itr *itr, rtosc_arg_val_t* buffer) { const rtosc_arg_val_t* result; @@ -262,7 +257,7 @@ return result; } -void rtosc_arg_val_itr_next(rtosc_arg_val_t_const_itr* itr) +void rtosc_arg_val_itr_next(rtosc_arg_val_itr *itr) { // increase the range index if(itr->av->type == '-') @@ -294,296 +289,6 @@ } } -// abort only if one side was finished, or if both are infinite ranges -int rtosc_arg_vals_cmp_has_next(const rtosc_arg_val_t_const_itr* litr, - const rtosc_arg_val_t_const_itr* ritr, - size_t lsize, size_t rsize) -{ - return (litr->i < lsize) && (ritr->i < rsize) - && (litr->av->type != '-' || ritr->av->type != '-' || - litr->av->val.r.num || ritr->av->val.r.num); -} - -// arrays are equal by now, but is one longer? -// each array must have either been completely passed, or it must -// have reached an infinite range -int rtosc_arg_vals_eq_after_abort(const rtosc_arg_val_t_const_itr* litr, - const rtosc_arg_val_t_const_itr* ritr, - size_t lsize, size_t rsize) -{ - return (litr->i == lsize || - (litr->av->type == '-' && !litr->av->val.r.num)) - && (ritr->i == rsize || - (ritr->av->type == '-' && !ritr->av->val.r.num)); -} - -// compare single elements, ranges excluded -int rtosc_arg_vals_eq_single(const rtosc_arg_val_t* _lhs, - const rtosc_arg_val_t* _rhs, - const rtosc_cmp_options* opt) -{ -#define mfabs(val) (((val) >= 0) ? (val) : -(val)) - int rval; - - if(!opt) - opt = &default_cmp_options; - - if(_lhs->type == _rhs->type) - switch(_lhs->type) - { - case 'i': - case 'c': - case 'r': - rval = _lhs->val.i == _rhs->val.i; - break; - case 'I': - case 'T': - case 'F': - case 'N': - rval = 1; - break; - case 'f': - rval = (opt->float_tolerance == 0.0) - ? _lhs->val.f == _rhs->val.f - : mfabs(_lhs->val.f - _rhs->val.f) <= - (float)opt->float_tolerance; - break; - case 'd': - rval = (opt->float_tolerance == 0.0) - ? _lhs->val.d == _rhs->val.d - : mfabs(_lhs->val.d - _rhs->val.d) <= - opt->float_tolerance; - break; - case 'h': - rval = _lhs->val.h == _rhs->val.h; - break; - case 't': - rval = _lhs->val.t == _rhs->val.t; - break; - case 'm': - rval = 0 == memcmp(_lhs->val.m, _rhs->val.m, 4); - break; - case 's': - case 'S': - rval = (_lhs->val.s == NULL || _rhs->val.s == NULL) - ? _lhs->val.s == _rhs->val.s - : (0 == strcmp(_lhs->val.s, _rhs->val.s)); - break; - case 'b': - { - int32_t lbs = _lhs->val.b.len, - rbs = _rhs->val.b.len; - rval = lbs == rbs; - if(rval) - rval = 0 == memcmp(_lhs->val.b.data, _rhs->val.b.data, lbs); - break; - } - case 'a': - { - if( _lhs->val.a.type != _rhs->val.a.type - && !(_lhs->val.a.type == 'T' && _rhs->val.a.type == 'F') - && !(_lhs->val.a.type == 'F' && _rhs->val.a.type == 'T')) - rval = 0; - else - rval = rtosc_arg_vals_eq(_lhs+1, _rhs+1, - _lhs->val.a.len, _rhs->val.a.len, - opt); - break; - } - case '-': - assert(false); - break; - } - else - { - rval = 0; - } - return rval; -#undef mfabs -} - -int rtosc_arg_vals_eq(const rtosc_arg_val_t* lhs, const rtosc_arg_val_t* rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt) -{ - // used if the value of lhs or rhs is range-computed: - rtosc_arg_val_t rlhs, rrhs; - - rtosc_arg_val_t_const_itr litr, ritr; - rtosc_arg_val_itr_init(&litr, lhs); - rtosc_arg_val_itr_init(&ritr, rhs); - - int rval = 1; - - if(!opt) - opt = &default_cmp_options; - - for( ; rtosc_arg_vals_cmp_has_next(&litr, &ritr, lsize, rsize) && rval; - rtosc_arg_val_itr_next(&litr), - rtosc_arg_val_itr_next(&ritr)) - { - rval = rtosc_arg_vals_eq_single(rtosc_arg_val_itr_get(&litr, &rlhs), - rtosc_arg_val_itr_get(&ritr, &rrhs), - opt); - } - - return rval - ? rtosc_arg_vals_eq_after_abort(&litr, &ritr, lsize, rsize) - : rval; -} - -// compare single elements, ranges excluded -int rtosc_arg_vals_cmp_single(const rtosc_arg_val_t* _lhs, - const rtosc_arg_val_t* _rhs, - const rtosc_cmp_options* opt) -{ -#define cmp_3way(val1,val2) (((val1) == (val2)) \ - ? 0 \ - : (((val1) > (val2)) ? 1 : -1)) -#define mfabs(val) (((val) >= 0) ? (val) : -(val)) - - int rval; - - if(!opt) - opt = &default_cmp_options; - - if(_lhs->type == _rhs->type) - switch(_lhs->type) - { - case 'i': - case 'c': - case 'r': - rval = cmp_3way(_lhs->val.i, _rhs->val.i); - break; - case 'I': - case 'T': - case 'F': - case 'N': - rval = 0; - break; - case 'f': - rval = (opt->float_tolerance == 0.0) - ? cmp_3way(_lhs->val.f, _rhs->val.f) - : (mfabs(_lhs->val.f - _rhs->val.f) - <= (float)opt->float_tolerance) - ? 0 - : ((_lhs->val.f > _rhs->val.f) ? 1 : -1); - break; - case 'd': - rval = (opt->float_tolerance == 0.0) - ? cmp_3way(_lhs->val.d, _rhs->val.d) - : (mfabs(_lhs->val.d - _rhs->val.d) - <= opt->float_tolerance) - ? 0 - : ((_lhs->val.d > _rhs->val.d) ? 1 : -1); - break; - case 'h': - rval = cmp_3way(_lhs->val.h, _rhs->val.h); - break; - case 't': - // immediately is considered lower than everything else - // this means if you send two events to a client, - // one being "immediately" and one being different, the - // immediately-event has the higher priority, event if the - // other one is in the past - rval = (_lhs->val.t == 1) - ? (_rhs->val.t == 1) - ? 0 - : -1 // _lhs has higher priority => _lhs < _rhs - : (_rhs->val.t == 1) - ? 1 - : cmp_3way(_lhs->val.t, _rhs->val.t); - break; - case 'm': - rval = memcmp(_lhs->val.m, _rhs->val.m, 4); - break; - case 's': - case 'S': - rval = (_lhs->val.s == NULL || _rhs->val.s == NULL) - ? cmp_3way(_lhs->val.s, _rhs->val.s) - : strcmp(_lhs->val.s, _rhs->val.s); - break; - case 'b': - { - int32_t lbs = _lhs->val.b.len, - rbs = _rhs->val.b.len; - int32_t minlen = (lbs < rbs) ? lbs : rbs; - rval = memcmp(_lhs->val.b.data, _rhs->val.b.data, minlen); - if(lbs != rbs && !rval) - { - // both equal until here - // the string that ends here is lexicographically smaller - rval = (lbs > rbs) - ? _lhs->val.b.data[minlen] - : -_rhs->val.b.data[minlen]; - } - - break; - } - case 'a': - { - int32_t llen = _lhs->val.a.len, rlen = _rhs->val.a.len; - if( _lhs->val.a.type != _rhs->val.a.type - && !(_lhs->val.a.type == 'T' && _rhs->val.a.type == 'F') - && !(_lhs->val.a.type == 'F' && _rhs->val.a.type == 'T')) - rval = (_lhs->val.a.type > _rhs->val.a.type) ? 1 : -1; - else - { - // the arg vals differ in this array => compare and return - rval = rtosc_arg_vals_cmp(_lhs+1, _rhs+1, llen, rlen, opt); - } - break; - } - case '-': - assert(false); - break; - } - else - { - rval = (_lhs->type > _rhs->type) ? 1 : -1; - } - - return rval; - -#undef mfabs -#undef cmp_3way -} - -int rtosc_arg_vals_cmp(const rtosc_arg_val_t* lhs, const rtosc_arg_val_t* rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt) -{ - // used if the value of lhs or rhs is range-computed: - rtosc_arg_val_t rlhs, rrhs; - - rtosc_arg_val_t_const_itr litr, ritr; - rtosc_arg_val_itr_init(&litr, lhs); - rtosc_arg_val_itr_init(&ritr, rhs); - - int rval = 0; - - if(!opt) - opt = &default_cmp_options; - - for( ; rtosc_arg_vals_cmp_has_next(&litr, &ritr, lsize, rsize) && !rval; - rtosc_arg_val_itr_next(&litr), - rtosc_arg_val_itr_next(&ritr)) - { - rval = rtosc_arg_vals_cmp_single(rtosc_arg_val_itr_get(&litr, &rlhs), - rtosc_arg_val_itr_get(&ritr, &rrhs), - opt); - } - - return rval ? rval - : (rtosc_arg_vals_eq_after_abort(&litr, &ritr, lsize, rsize)) - ? 0 - // they're equal until here, so one array must have - // elements left: - : (lsize-(litr.i) > rsize-(ritr.i)) - ? 1 - : -1; -} - void rtosc_v2args(rtosc_arg_t* args, size_t nargs, const char* arg_str, rtosc_va_list_t* ap) { @@ -625,10 +330,10 @@ args[arg_pos++].f = va_arg(ap->a, double); break; case 'T': + args[arg_pos++].T = 1; + break; case 'F': - case 'N': - case 'I': - args[arg_pos++].T = arg_str[-1]; + args[arg_pos++].T = 0; break; default: ; @@ -673,7 +378,7 @@ if(!nargs) return rtosc_amessage(buffer,len,address,arguments,NULL); - rtosc_arg_t args[nargs]; + STACKALLOC(rtosc_arg_t, args, nargs); rtosc_va_list_t ap2; va_copy(ap2.a, ap); rtosc_v2args(args, nargs, arguments, &ap2); @@ -681,6 +386,41 @@ return rtosc_amessage(buffer,len,address,arguments,args); } +size_t rtosc_avmessage(char *buffer, + size_t len, + const char *address, + size_t nargs, + const rtosc_arg_val_t *args) +{ + rtosc_arg_val_itr itr; + rtosc_arg_val_itr_init(&itr, args); + + int val_max; + { + // equivalent to the for loop below, in order to + // find out the array size + rtosc_arg_val_itr itr2 = itr; + for(val_max = 0; itr2.i < nargs; ++val_max) + rtosc_arg_val_itr_next(&itr2); + } + + STACKALLOC(rtosc_arg_t, vals, val_max); + STACKALLOC(char, argstr,val_max+1); + + int i; + for(i = 0; i < val_max; ++i) + { + rtosc_arg_val_t av_buffer; + const rtosc_arg_val_t* cur = rtosc_arg_val_itr_get(&itr, &av_buffer); + vals[i] = cur->val; + argstr[i] = cur->type; + rtosc_arg_val_itr_next(&itr); + } + argstr[i] = 0; + + return rtosc_amessage(buffer, len, address, argstr, vals); +} + size_t rtosc_amessage(char *buffer, size_t len, const char *address, diff -Nru zynaddsubfx-3.0.3/rtosc/src/rtosc-time.c zynaddsubfx-3.0.4/rtosc/src/rtosc-time.c --- zynaddsubfx-3.0.3/rtosc/src/rtosc-time.c 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/rtosc-time.c 2018-09-09 00:40:57.000000000 +0000 @@ -40,6 +40,7 @@ int written = snprintf(secfracs_as_hex, 16, "%a", secfracsf); assert(written >= 0); // no error assert(written < 16); // size suffices + (void) written; assert(secfracs_as_hex[3]=='.'); // 0x?. secfracs_as_hex[3] = secfracs_as_hex[2]; // remove '.' @@ -93,6 +94,7 @@ int written = snprintf(lossless, 16, "0x%xp-32", (unsigned)secfracs); assert(written >= 0); // no error assert(written < 16); // size suffices + (void) written; float flt; int rd = 0; diff -Nru zynaddsubfx-3.0.3/rtosc/src/util.c zynaddsubfx-3.0.4/rtosc/src/util.c --- zynaddsubfx-3.0.3/rtosc/src/util.c 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/util.c 2018-09-09 00:40:57.000000000 +0000 @@ -0,0 +1,8 @@ +#include +#include "util.h" + +char *fast_strcpy(char *dest, const char *src, size_t buffersize) +{ + *dest = 0; + return strncat(dest, src, buffersize-1); +} diff -Nru zynaddsubfx-3.0.3/rtosc/src/util.h zynaddsubfx-3.0.4/rtosc/src/util.h --- zynaddsubfx-3.0.3/rtosc/src/util.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/src/util.h 2019-03-10 16:16:45.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + /** + * @file util.h + * Utilities shared by rtosc functions + * + * @test util.c + */ + +#ifndef UTIL_H +#define UTIL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Copy string to another memory location, including the terminating zero byte + * @param dest Destination memory location + * @param src Source string + * @param buffersize Maximal number of bytes that you can write to @p dest + * @return A pointer to @p dest + * @warning @p dest and @p src shall not overlap + * @warning if buffersize is larger than strlen(src)+1, unused bytes in @p dest + * are not overwritten. Secure information may be released. Don't use this if + * you want to send the string somewhere else, e.g. via IPC. + */ +char *fast_strcpy(char *dest, const char *src, size_t buffersize); + +/*TODO: Add documentation?*/ +#ifdef _MSC_VER +#define STACKALLOC(type, name, size) type *name = (type*)(_alloca((size)*sizeof(type))) +#else +#define STACKALLOC(type, name, size) type name[size] +#endif +#ifdef __cplusplus +} +#endif + +#endif // UTIL_H diff -Nru zynaddsubfx-3.0.3/rtosc/test/arg-val-cmp.c zynaddsubfx-3.0.4/rtosc/test/arg-val-cmp.c --- zynaddsubfx-3.0.3/rtosc/test/arg-val-cmp.c 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/arg-val-cmp.c 2018-09-09 00:40:57.000000000 +0000 @@ -0,0 +1,412 @@ +#include +#include +#include "common.h" + +enum { + gt, + lt, + eq +}; + +rtosc_arg_val_t l[3], r[3]; // left, right +const int tc_len = 1024; +char tc_full[1024]; // descr. for full testcase name + +/* + helper functions + */ +void cmp_1(int exp, + rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt, + const char* ldesc, const char* rdesc, int line) +{ + printf("cmp1(%c, %c)\n", lhs->type, rhs->type); + int res = rtosc_arg_vals_cmp(lhs, rhs, lsize, rsize, opt); + + *tc_full = 0; + strncat(tc_full, ldesc, tc_len-1); + const char* sgn = "ERROR"; + switch(exp) + { + case gt: sgn = " >(3way)"; break; + case eq: sgn = "==(3way)"; break; + case lt: sgn = " <(3way)"; break; + } + + snprintf(tc_full, tc_len, "\"%s\" %s \"%s\"", ldesc, sgn, rdesc); + + switch(exp) + { + case gt: assert_int_eq(1, res > 0, tc_full, line); break; + case eq: assert_int_eq(1, res == 0, tc_full, line); break; + case lt: assert_int_eq(1, res < 0, tc_full, line); break; + } + + int exp_eq = (exp == eq); + + sgn = exp_eq ? "==(bool)" : "!=(bool)"; + snprintf(tc_full, tc_len, "\"%s\" %s \"%s\"", ldesc, sgn, rdesc); + + res = rtosc_arg_vals_eq(lhs, rhs, lsize, rsize, opt); + assert_int_eq(exp_eq, res, tc_full, line); +} + +void cmp_gt(rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, + size_t lsize, size_t rsize, + const rtosc_cmp_options* opt, + const char* ldesc, const char* rdesc, int line) +{ + cmp_1(gt, lhs, rhs, lsize, rsize, opt, ldesc, rdesc, line); + cmp_1(lt, rhs, lhs, rsize, lsize, opt, rdesc, ldesc, line); + cmp_1(eq, lhs, lhs, lsize, lsize, opt, ldesc, ldesc, line); + cmp_1(eq, rhs, rhs, rsize, rsize, opt, rdesc, rdesc, line); +} + +/* + tests + */ + +void ints() +{ + l[0].type = r[0].type = 'i'; + l[0].val.i = 12345; + r[0].val.i = 42; + + cmp_gt(l, r, 1, 1, NULL, "12345", "42", __LINE__); +} + +void asn_special(rtosc_arg_val_t* l, rtosc_arg_val_t* r, char type) +{ + l->type = r->type = type; + if(type == 'T') + l->val.T = r->val.T = 1; + else if(type == 'F') + l->val.T = r->val.T = 0; +} + +void special() +{ + asn_special(l, r, 'N'); + cmp_1(eq, l, r, 1, 1, NULL, "nil", "nil", __LINE__); + asn_special(l, r, 'F'); + cmp_1(eq, l, r, 1, 1, NULL, "false", "false", __LINE__); + asn_special(l, r, 'T'); + cmp_1(eq, l, r, 1, 1, NULL, "true", "true", __LINE__); + asn_special(l, r, 'I'); + cmp_1(eq, l, r, 1, 1, NULL, "inf", "inf", __LINE__); +} + +void floats() +{ + l[0].type = r[0].type = 'f'; + l[0].val.f = 1.0f; + r[0].val.f = -1.0f; + + cmp_gt(l, r, 1, 1, NULL, "1.0f", "-1.0f", __LINE__); + + rtosc_cmp_options very_tolerant = { 2.0 }; + + cmp_1(eq, l, r, 1, 1, &very_tolerant, + "1.0f", "-1.0f (2.0 tolerance)", __LINE__); + cmp_1(eq, r, l, 1, 1, &very_tolerant, + "-1.0f", "1.0f (2.0 tolerance)", __LINE__); +} + +void doubles() +{ + l[0].type = r[0].type = 'd'; + l[0].val.d = 123456790.123456789; + r[0].val.d = 123456789.0; + + cmp_gt(l, r, 1, 1, NULL, + "larger double value", "smaller double value", __LINE__); + + l[0].type = r[0].type = 'd'; + l[0].val.d = 1.0f; + r[0].val.d = -1.0f; + + cmp_gt(l, r, 1, 1, NULL, "1.0", "-1.0", __LINE__); + + rtosc_cmp_options very_tolerant = { 2.0 }; + + cmp_1(eq, l, r, 1, 1, &very_tolerant, + "1.0", "-1.0 (2.0 tolerance)", __LINE__); + cmp_1(eq, r, l, 1, 1, &very_tolerant, + "-1.0", "1.0 (2.0 tolerance)", __LINE__); +} + +void huge_ints() +{ + l[0].type = r[0].type = 'h'; + l[0].val.h = 5000000000; + r[0].val.h = -5000000000; + + cmp_gt(l, r, 1, 1, NULL, "5000000000", "-5000000000", __LINE__); +} + +void timestamps() +{ + l[0].type = r[0].type = 't'; + l[0].val.t = 0xFFFF0000; + r[0].val.t = 0x00000002; + + cmp_gt(l, r, 1, 1, NULL, "future", "nearly epoch", __LINE__); + + l[0].val.t = 0; // epoch + r[0].val.t = 1; // immediately + cmp_gt(l, r, 1, 1, NULL, "immediately", "epoch", __LINE__); + + l[0].val.t = 0xFFFFFFFF; // future + cmp_gt(l, r, 1, 1, NULL, "immediately", "future", __LINE__); +} + +void midi() +{ + l[0].type = r[0].type = 'm'; + l[0].val.m[0] = l[0].val.m[1] = l[0].val.m[2] = 0; l[0].val.m[3] = 1; + r[0].val.m[0] = r[0].val.m[1] = r[0].val.m[2] = r[0].val.m[3] = 0; + + cmp_gt(l, r, 1, 1, NULL, "MIDI event 1", "MIDI event 2", __LINE__); +} + +void strings() +{ + l[0].type = l[1].type = 's'; + + l[0].val.s = "rtosc rtosc rtosc"; + l[1].val.s = "rtosc"; + r[0].val.s = "rt0sc"; + r[1].val.s = ""; + + cmp_gt(l, r, 1, 1, NULL, "one string", "another string", __LINE__); + cmp_gt(l, l+1, 1, 1, NULL, "one string", "subset string", __LINE__); + cmp_gt(r, r+1, 1, 1, NULL, "normal string", "empty string", __LINE__); +} + +void blobs() +{ + l[0].type = 'b'; + l[0].val.b.len = 14; + l[0].val.b.data = (uint8_t*)"rtosc_is_awful"; + + l[1].type = 'b'; + l[1].val.b.len = 0; + l[1].val.b.data = (uint8_t*)""; + + r[0].type = 'b'; + r[0].val.b.len = 16; + r[0].val.b.data = (uint8_t*)"rtosc_is_awesome"; + + r[1].type = 'b'; + r[1].val.b.len = 11; + r[1].val.b.data = (uint8_t*)"rtosc_is_aw"; + + cmp_gt(l, r, 1, 1, NULL, "one blob", "another blob", __LINE__); + cmp_gt(r, r+1, 1, 1, NULL, "one blob", "subset blob", __LINE__); + cmp_gt(l, l+1, 1, 1, NULL, "normal blob", "empty blob", __LINE__); +} + +void arrays() +{ + // fill three arrays, both with the ints 1, 2, 3 resp. 1, 2, 2 + rtosc_arg_val_t la[16], ra[16]; // left array, right array + la[0].type = ra[0].type = 'a'; + la[0].val.a.type = ra[0].val.a.type = 'i'; + la[0].val.a.len = ra[0].val.a.len = 3; + la[1].type = la[2].type = la[3].type = + ra[1].type = ra[2].type = ra[3].type = 'i'; + la[1].val.i = 1; la[2].val.i = 2; la[3].val.i = 3; + ra[1].val.i = 1; ra[2].val.i = 2; ra[3].val.i = 2; + + // same size + cmp_gt(la, ra, 4, 4, NULL, "[1 2 3]", "[1 2 2]", __LINE__); + + // different size + ra[0].val.a.len = 2; + ra[3].val.i = 3; + cmp_gt(la, ra, 4, 4, NULL, "[1 2 3]", "[1 2] 3", __LINE__); +} + +void ranges() +{ + rtosc_arg_val_t l[8], r[8]; + + // generate range 1 ... 5 inside of an array + l[0].type = 'a'; + l[0].val.a.len = 3; // number of arg_val_t inside + l[0].val.a.type = 'i'; + l[1].type = '-'; + l[1].val.r.has_delta = 1; + l[1].val.r.num = 5; + l[2].type = 'i'; + l[2].val.i = 1; + l[3].type = 'i'; + l[3].val.i = 1; + + r[0].type = 'a'; + r[0].val.a.len = 5; + r[0].val.a.type = 'i'; + for(size_t i = 0; i < 5; ++i) { + r[i+1].type = 'i'; + r[i+1].val.i = i+1; + } + + cmp_1(eq, l+1, r+1, 3, 5, NULL, + "range 1 ... 5", "integers 1 2 3 4 5", __LINE__); + + cmp_1(eq, l, r, 4, 6, NULL, + "[1 ... 5]", "[1 2 3 4 5]", __LINE__); + + // let l+1 = 1 ... 5 6 7 + // and r+1 = 1 2 3 4 ... 7 + l[4].val.i = 6; + l[5].val.i = 7; + l[4].type = l[5].type = 'i'; + r[4].type = '-'; + r[4].val.r.has_delta = 1; + r[4].val.r.num = 4; + r[5].type = 'i'; + r[5].val.i = 1; + r[6].type = 'i'; + r[6].val.i = 4; + + cmp_1(eq, l+1, r+1, 5, 6, NULL, + "1 ... 5 6 7", "1 2 3 ... 7", __LINE__); + + // keep l+1 = 1 ... 5 6 7 + // and let r+1 = 1 2 3 4 ... + r[4].val.r.num = 0; + //r[6].type = 'x'; + + cmp_1(eq, l+1, r+1, 5, 6, NULL, + "1 ... 5 6 7", "1 2 3 4 ...", __LINE__); + + // let l+1 = 1 ... 5 6 6 + // and keep r+1 = 1 2 3 4 ... + l[5].val.i = 6; + // careful! r and l are swapped! + cmp_gt(r+1, l+1, 6, 5, NULL, "infinite range", "integers", __LINE__); + + // let l+1 = 1 ... 7 + // and keep r+1 = 1 2 3 4 ... + l[1].val.r.num = 7; + cmp_1(eq, l+1, r+1, 3, 6, NULL, "finite range", "infinite range", __LINE__); + + // let l = 1 2 3 ... + // and r = 1 2 ... + l[0].type = l[1].type = 'i'; + l[0].val.i = 1; l[1].val.i = 2; + l[2].type = '-'; + l[2].val.r.has_delta = 1; + l[2].val.r.num = 0; + l[3].type = l[4].type = 'i'; + l[3].val.i = 3; l[4].val.i = 1; + + r[0].type = 'i'; + r[0].val.i = 1; + r[1].type = '-'; + r[1].val.r.has_delta = 1; + r[1].val.r.num = 0; + r[2].type = r[3].type = 'i'; + r[2].val.i = 1; r[3].val.i = 2; + + cmp_1(eq, l, r, 5, 4, NULL, + "infinite range", "infinite_range", __LINE__); + + // let l = 1 1 1 + // and r = 1 1 ... + l[1].val.i = 1; + l[2].type = 'i'; + l[2].val.i = 1; + r[1].val.r.has_delta = 0; + r[2].val.i = 1; + + cmp_1(eq, l, r, 3, 3, NULL, + "numbers", "infinite_range (no delta)", __LINE__); + + // let l = 1 + // and keep r = 1 1 ... + cmp_1(eq, l, r, 1, 3, NULL, + "nothing", "infinite_range (no delta)", __LINE__); + + // let l = 1 ... + // and keep r = 1 1 ... + l[0].type = '-'; + l[0].val.r.num = 0; + l[0].val.r.has_delta = 0; + l[1].type = 'i'; + l[1].val.i = 1; + cmp_1(eq, l, r, 2, 3, NULL, + "infinite_range (no delta)", "infinite_range (no delta)", __LINE__); + + + // keep l = 1 ... + // and let r = 1 ... + r[0].type = '-'; + r[0].val.r.num = 0; + r[0].val.r.has_delta = 0; + r[1].type = 'i'; + r[1].val.i = 1; + cmp_1(eq, l, r, 2, 2, NULL, "1 ...", "1 ...", __LINE__); +} + +void different_types() +{ + l[0].type = 'i'; + l[0].val.i = 0; + r[0].type = 'h'; + r[0].val.h = 1; + + // 1 > 0, but 'i' > 'h' (alphabetically) + cmp_gt(l, r, 1, 1, NULL, "int type", "huge int type", __LINE__); +} + +void multiple_args() +{ + l[0].type = r[0].type = 'i'; + l[0].val.i = r[0].val.i = 42; + l[1].type = r[1].type = 'T'; + l[1].val.T = r[1].val.T = 1; + l[2].type = r[2].type = 's'; + l[2].val.s = "1"; + r[2].val.s = "0"; //different values + + cmp_gt(l, r, 3, 3, NULL, "multiple args 1", "mutliple args 2", __LINE__); + + l[0].type = r[0].type = 't'; + l[0].val.t = r[0].val.t = 1; + l[1].type = 'f'; + r[1].type = 'd'; //different types + l[1].val.f = 1.0f; + r[1].val.d = 1.0; + + cmp_gt(l, r, 2, 2, NULL, "multiple args 3", "multiple args 4", __LINE__); +} + +void different_sizes() +{ + l[0].type = l[1].type = 'i'; + l[0].val.i = l[1].val.i = 42; + + cmp_gt(l, l, 2, 1, NULL, "size-2-array", "size-1-array", __LINE__); +} + +int main() +{ + ints(); + special(); + floats(); + doubles(); + huge_ints(); + timestamps(); + midi(); + blobs(); + arrays(); + ranges(); + different_types(); + multiple_args(); + different_sizes(); + + return test_summary(); +} diff -Nru zynaddsubfx-3.0.3/rtosc/test/arg-val-math.c zynaddsubfx-3.0.4/rtosc/test/arg-val-math.c --- zynaddsubfx-3.0.3/rtosc/test/arg-val-math.c 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/arg-val-math.c 2018-09-09 00:40:57.000000000 +0000 @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include "common.h" + +typedef int (*unary_arg_func_ptr)(rtosc_arg_val_t*); +typedef int (*binary_arg_func_ptr)(const rtosc_arg_val_t *, + const rtosc_arg_val_t *, + rtosc_arg_val_t*); + +const rtosc_cmp_options tolerance = { 0.00000000001 }; + +/* + helper functions +*/ +void test_binary(char type, binary_arg_func_ptr op, char opchar, + double arg1, double arg2, double exp) +{ + rtosc_arg_val_t a1, a2, res, e; + rtosc_arg_val_from_double(&a1, type, arg1); + rtosc_arg_val_from_double(&a2, type, arg2); + rtosc_arg_val_from_double(&e, type, exp); + + (*op)(&a1, &a2, &res); + int cmp_res = rtosc_arg_vals_eq(&e, &res, 1, 1, &tolerance); + + char str[16]; + snprintf(str, 16, "'%c' %c '%c'", type, opchar, type); + assert_int_eq(1, cmp_res, str, __LINE__); +} + +void test_unary(char type, unary_arg_func_ptr op, char opchar, + double arg, double exp) +{ + rtosc_arg_val_t a, e; + rtosc_arg_val_from_double(&a, type, arg); + rtosc_arg_val_from_double(&e, type, exp); + + (*op)(&a); + int cmp_res = rtosc_arg_vals_eq(&e, &a, 1, 1, &tolerance); + + char str[16]; + snprintf(str, 16, "%c'%c'", opchar, type); + assert_int_eq(1, cmp_res, str, __LINE__); +} + +void test_add(char type, double arg1, double arg2, double exp) +{ + test_binary(type, rtosc_arg_val_add, '+', arg1, arg2, exp); + test_binary(type, rtosc_arg_val_sub, '-', exp, arg1, arg2); +} + +void test_mult(char type, double arg1, double arg2, double exp) +{ + test_binary(type, rtosc_arg_val_mult, '*', arg1, arg2, exp); + if(arg2 != 0.0) + test_binary(type, rtosc_arg_val_div, '/', exp, arg1, arg2); +} + +void test_to_int(char type, double arg) +{ + rtosc_arg_val_t a; + int res; + + rtosc_arg_val_from_int(&a, type, arg); + rtosc_arg_val_to_int(&a, &res); + + char str[20]; + snprintf(str, 20, "(int)'%c'", type); + assert_int_eq(arg, res, str, __LINE__); + + rtosc_arg_val_from_double(&a, type, arg); + rtosc_arg_val_to_int(&a, &res); + + snprintf(str, 20, "(int)(double)'%c'", type); + assert_int_eq(arg, res, str, __LINE__); +} + +void test_null(char type) +{ + rtosc_arg_val_t a, e; + rtosc_arg_val_null(&a, type); + rtosc_arg_val_from_double(&e, type, 0.0); + + int cmp_res = rtosc_arg_vals_eq(&e, &a, 1, 1, NULL); + + char str[16]; + snprintf(str, 16, "(0)'%c'", type); + assert_int_eq(1, cmp_res, str, __LINE__); +} + +void special_null_test() +{ + rtosc_arg_val_t a; + rtosc_arg_val_null(&a, 't'); + assert_int_eq(0, a.val.t, "(0)'t'", __LINE__); + + rtosc_arg_val_null(&a, 's'); + assert_null(a.val.s, "(0)'s'", __LINE__); + + rtosc_arg_val_null(&a, 'r'); + assert_int_eq(0, a.val.i, "(0)'r'", __LINE__); + + rtosc_arg_val_null(&a, 'T'); + assert_int_eq(a.val.T, 0, "(0)'T'", __LINE__); + assert_char_eq(a.type, 'F', "(0)'T' (type)", __LINE__); + + rtosc_arg_val_null(&a, 'S'); + assert_null(a.val.s, "(0)'S'", __LINE__); + + rtosc_arg_val_null(&a, 'F'); + assert_int_eq(a.val.T, 0, "(0)'F'", __LINE__); + assert_char_eq(a.type, 'F', "(0)'F' (type)", __LINE__); +} + +/* + all tests +*/ +int main() +{ + test_add('T', 0, 0, 0); + test_add('T', 0, 1, 1); + test_add('T', 1, 0, 1); + test_add('T', 1, 1, 0); + test_add('i', 42, -123, -81); + test_add('c', 'A', ' ', 'a'); + test_add('h', 4000000000, 4000000000, 8000000000); + test_add('f', 1.23, -100, -98.77); + test_add('d', 1.234523452345, -1.111111111111, 0.1234123412341234); + + // avoid division by '0' in test_mult('T', 0, 0, 0): + test_binary('T', rtosc_arg_val_mult, '*', 0, 0, 0); + test_binary('T', rtosc_arg_val_mult, '*', 0, 1, 0); + test_mult('T', 1, 0, 0); + test_mult('T', 1, 1, 1); + test_mult('i', -6, -7, 42); + test_mult('c', '\t', '\a', '?'); + test_mult('h', 3000000000, 2, 6000000000); + test_mult('f', 0.01, 100, 1); + test_mult('f', 1000000000000000000, -0.000000000000000001, -1); + test_mult('d', 0.1111111, 0.1111111, 0.01234567654321); + + unary_arg_func_ptr op = rtosc_arg_val_negate; + test_unary('T', op, '-', 1, 0); + test_unary('T', op, '-', 0, 1); + test_unary('F', op, '-', 1, 0); + test_unary('F', op, '-', 0, 1); + test_unary('i', op, '-', 123, -123); + test_unary('h', op, '-', 8000000000, -8000000000); + test_unary('f', op, '-', -0.1234, 0.1234); + test_unary('f', op, '-', 0.0f, 0.0f); + test_unary('d', op, '-', 0.1234123412341234, -0.1234123412341234); + + op = rtosc_arg_val_round; + test_unary('T', op, '~', 1, 1); + test_unary('T', op, '~', 0, 0); + test_unary('F', op, '~', 1, 1); + test_unary('F', op, '~', 0, 0); + test_unary('i', op, '~', 123, 123); + test_unary('c', op, '~', -123, -123); + test_unary('h', op, '~', -8000000000, -8000000000); + test_unary('f', op, '~', -0.1234, 0); + test_unary('f', op, '~', 9.99f, 9); + test_unary('f', op, '~', 9.9999f, 10); + test_unary('d', op, '~', 1234.1234123412341234, 1234); + test_unary('d', rtosc_arg_val_round, '~', 9999.9999, 10000); + + test_to_int('T', 0); + test_to_int('T', 1); + test_to_int('F', 0); + test_to_int('F', 1); + test_to_int('i', 42); + test_to_int('c', ' '); + test_to_int('h', -80); + test_to_int('f', .123f); + test_to_int('d', -.123f); + + test_null('i'); + test_null('c'); + test_null('h'); + test_null('f'); + test_null('d'); + + special_null_test(); + + // rtosc_arg_val_range_arg is being (indirectly) + // tested in the pretty-format tests + + return test_summary(); +} + diff -Nru zynaddsubfx-3.0.3/rtosc/test/bundles.c zynaddsubfx-3.0.4/rtosc/test/bundles.c --- zynaddsubfx-3.0.3/rtosc/test/bundles.c 2015-10-23 14:47:50.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/bundles.c 2019-03-03 15:19:10.000000000 +0000 @@ -68,5 +68,5 @@ assert_int_eq(1, rtosc_bundle_timetag(buffer_c), "Verify rtosc_bundle_timetag() Works", __LINE__); - return global_err ? EXIT_FAILURE : EXIT_SUCCESS; + return test_summary(); } diff -Nru zynaddsubfx-3.0.3/rtosc/test/cmp-arg-vals.c zynaddsubfx-3.0.4/rtosc/test/cmp-arg-vals.c --- zynaddsubfx-3.0.3/rtosc/test/cmp-arg-vals.c 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/cmp-arg-vals.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,407 +0,0 @@ -#include -#include -#include "common.h" - -enum { - gt, - lt, - eq -}; - -rtosc_arg_val_t l[3], r[3]; // left, right -const int tc_len = 1024; -char tc_full[1024]; // descr. for full testcase name - -/* - helper functions - */ -void cmp_1(int exp, - rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt, - const char* ldesc, const char* rdesc, int line) -{ - printf("cmp1(%c, %c)\n", lhs->type, rhs->type); - int res = rtosc_arg_vals_cmp(lhs, rhs, lsize, rsize, opt); - - strncpy(tc_full, ldesc, tc_len); - const char* sgn = "ERROR"; - switch(exp) - { - case gt: sgn = " >(3way)"; break; - case eq: sgn = "==(3way)"; break; - case lt: sgn = " <(3way)"; break; - } - - snprintf(tc_full, tc_len, "\"%s\" %s \"%s\"", ldesc, sgn, rdesc); - - switch(exp) - { - case gt: assert_int_eq(1, res > 0, tc_full, line); break; - case eq: assert_int_eq(1, res == 0, tc_full, line); break; - case lt: assert_int_eq(1, res < 0, tc_full, line); break; - } - - int exp_eq = (exp == eq); - - sgn = exp_eq ? "==(bool)" : "!=(bool)"; - snprintf(tc_full, tc_len, "\"%s\" %s \"%s\"", ldesc, sgn, rdesc); - - res = rtosc_arg_vals_eq(lhs, rhs, lsize, rsize, opt); - assert_int_eq(exp_eq, res, tc_full, line); -} - -void cmp_gt(rtosc_arg_val_t* lhs, rtosc_arg_val_t* rhs, - size_t lsize, size_t rsize, - const rtosc_cmp_options* opt, - const char* ldesc, const char* rdesc, int line) -{ - cmp_1(gt, lhs, rhs, lsize, rsize, opt, ldesc, rdesc, line); - cmp_1(lt, rhs, lhs, rsize, lsize, opt, rdesc, ldesc, line); - cmp_1(eq, lhs, lhs, lsize, lsize, opt, ldesc, ldesc, line); - cmp_1(eq, rhs, rhs, rsize, rsize, opt, rdesc, rdesc, line); -} - -/* - tests - */ - -void ints() -{ - l[0].type = r[0].type = 'i'; - l[0].val.i = 12345; - r[0].val.i = 42; - - cmp_gt(l, r, 1, 1, NULL, "12345", "42", __LINE__); -} - -void asn_special(rtosc_arg_val_t* l, rtosc_arg_val_t* r, char type) -{ - l->type = l->val.T = r->type = r->val.T = type; -} - -void special() -{ - asn_special(l, r, 'N'); - cmp_1(eq, l, r, 1, 1, NULL, "nil", "nil", __LINE__); - asn_special(l, r, 'F'); - cmp_1(eq, l, r, 1, 1, NULL, "false", "false", __LINE__); - asn_special(l, r, 'T'); - cmp_1(eq, l, r, 1, 1, NULL, "true", "true", __LINE__); - asn_special(l, r, 'I'); - cmp_1(eq, l, r, 1, 1, NULL, "inf", "inf", __LINE__); -} - -void floats() -{ - l[0].type = r[0].type = 'f'; - l[0].val.f = 1.0f; - r[0].val.f = -1.0f; - - cmp_gt(l, r, 1, 1, NULL, "1.0f", "-1.0f", __LINE__); - - rtosc_cmp_options very_tolerant = { 2.0 }; - - cmp_1(eq, l, r, 1, 1, &very_tolerant, - "1.0f", "-1.0f (2.0 tolerance)", __LINE__); - cmp_1(eq, r, l, 1, 1, &very_tolerant, - "-1.0f", "1.0f (2.0 tolerance)", __LINE__); -} - -void doubles() -{ - l[0].type = r[0].type = 'd'; - l[0].val.d = 123456790.123456789; - r[0].val.d = 123456789.0; - - cmp_gt(l, r, 1, 1, NULL, - "larger double value", "smaller double value", __LINE__); - - l[0].type = r[0].type = 'd'; - l[0].val.d = 1.0f; - r[0].val.d = -1.0f; - - cmp_gt(l, r, 1, 1, NULL, "1.0", "-1.0", __LINE__); - - rtosc_cmp_options very_tolerant = { 2.0 }; - - cmp_1(eq, l, r, 1, 1, &very_tolerant, - "1.0", "-1.0 (2.0 tolerance)", __LINE__); - cmp_1(eq, r, l, 1, 1, &very_tolerant, - "-1.0", "1.0 (2.0 tolerance)", __LINE__); -} - -void huge_ints() -{ - l[0].type = r[0].type = 'h'; - l[0].val.h = 5000000000; - r[0].val.h = -5000000000; - - cmp_gt(l, r, 1, 1, NULL, "5000000000", "-5000000000", __LINE__); -} - -void timestamps() -{ - l[0].type = r[0].type = 't'; - l[0].val.t = 0xFFFF0000; - r[0].val.t = 0x00000002; - - cmp_gt(l, r, 1, 1, NULL, "future", "nearly epoch", __LINE__); - - l[0].val.t = 0; // epoch - r[0].val.t = 1; // immediately - cmp_gt(l, r, 1, 1, NULL, "immediately", "epoch", __LINE__); - - l[0].val.t = 0xFFFFFFFF; // future - cmp_gt(l, r, 1, 1, NULL, "immediately", "future", __LINE__); -} - -void midi() -{ - l[0].type = r[0].type = 'm'; - l[0].val.m[0] = l[0].val.m[1] = l[0].val.m[2] = 0; l[0].val.m[3] = 1; - r[0].val.m[0] = r[0].val.m[1] = r[0].val.m[2] = r[0].val.m[3] = 0; - - cmp_gt(l, r, 1, 1, NULL, "MIDI event 1", "MIDI event 2", __LINE__); -} - -void strings() -{ - l[0].type = l[1].type = 's'; - - l[0].val.s = "rtosc rtosc rtosc"; - l[1].val.s = "rtosc"; - r[0].val.s = "rt0sc"; - r[1].val.s = ""; - - cmp_gt(l, r, 1, 1, NULL, "one string", "another string", __LINE__); - cmp_gt(l, l+1, 1, 1, NULL, "one string", "subset string", __LINE__); - cmp_gt(r, r+1, 1, 1, NULL, "normal string", "empty string", __LINE__); -} - -void blobs() -{ - l[0].type = 'b'; - l[0].val.b.len = 14; - l[0].val.b.data = (uint8_t*)"rtosc_is_awful"; - - l[1].type = 'b'; - l[1].val.b.len = 0; - l[1].val.b.data = (uint8_t*)""; - - r[0].type = 'b'; - r[0].val.b.len = 16; - r[0].val.b.data = (uint8_t*)"rtosc_is_awesome"; - - r[1].type = 'b'; - r[1].val.b.len = 11; - r[1].val.b.data = (uint8_t*)"rtosc_is_aw"; - - cmp_gt(l, r, 1, 1, NULL, "one blob", "another blob", __LINE__); - cmp_gt(r, r+1, 1, 1, NULL, "one blob", "subset blob", __LINE__); - cmp_gt(l, l+1, 1, 1, NULL, "normal blob", "empty blob", __LINE__); -} - -void arrays() -{ - // fill three arrays, both with the ints 1, 2, 3 resp. 1, 2, 2 - rtosc_arg_val_t la[16], ra[16]; // left array, right array - la[0].type = ra[0].type = 'a'; - la[0].val.a.type = ra[0].val.a.type = 'i'; - la[0].val.a.len = ra[0].val.a.len = 3; - la[1].type = la[2].type = la[3].type = - ra[1].type = ra[2].type = ra[3].type = 'i'; - la[1].val.i = 1; la[2].val.i = 2; la[3].val.i = 3; - ra[1].val.i = 1; ra[2].val.i = 2; ra[3].val.i = 2; - - // same size - cmp_gt(la, ra, 4, 4, NULL, "[1 2 3]", "[1 2 2]", __LINE__); - - // different size - ra[0].val.a.len = 2; - ra[3].val.i = 3; - cmp_gt(la, ra, 4, 4, NULL, "[1 2 3]", "[1 2] 3", __LINE__); -} - -void ranges() -{ - rtosc_arg_val_t l[8], r[8]; - - // generate range 1 ... 5 inside of an array - l[0].type = 'a'; - l[0].val.a.len = 3; // number of arg_val_t inside - l[0].val.a.type = 'i'; - l[1].type = '-'; - l[1].val.r.has_delta = 1; - l[1].val.r.num = 5; - l[2].type = 'i'; - l[2].val.i = 1; - l[3].type = 'i'; - l[3].val.i = 1; - - r[0].type = 'a'; - r[0].val.a.len = 5; - r[0].val.a.type = 'i'; - for(size_t i = 0; i < 5; ++i) { - r[i+1].type = 'i'; - r[i+1].val.i = i+1; - } - - cmp_1(eq, l+1, r+1, 3, 5, NULL, - "range 1 ... 5", "integers 1 2 3 4 5", __LINE__); - - cmp_1(eq, l, r, 4, 6, NULL, - "[1 ... 5]", "[1 2 3 4 5]", __LINE__); - - // let l+1 = 1 ... 5 6 7 - // and r+1 = 1 2 3 4 ... 7 - l[4].val.i = 6; - l[5].val.i = 7; - l[4].type = l[5].type = 'i'; - r[4].type = '-'; - r[4].val.r.has_delta = 1; - r[4].val.r.num = 4; - r[5].type = 'i'; - r[5].val.i = 1; - r[6].type = 'i'; - r[6].val.i = 4; - - cmp_1(eq, l+1, r+1, 5, 6, NULL, - "1 ... 5 6 7", "1 2 3 ... 7", __LINE__); - - // keep l+1 = 1 ... 5 6 7 - // and let r+1 = 1 2 3 4 ... - r[4].val.r.num = 0; - //r[6].type = 'x'; - - cmp_1(eq, l+1, r+1, 5, 6, NULL, - "1 ... 5 6 7", "1 2 3 4 ...", __LINE__); - - // let l+1 = 1 ... 5 6 6 - // and keep r+1 = 1 2 3 4 ... - l[5].val.i = 6; - // careful! r and l are swapped! - cmp_gt(r+1, l+1, 6, 5, NULL, "infinite range", "integers", __LINE__); - - // let l+1 = 1 ... 7 - // and keep r+1 = 1 2 3 4 ... - l[1].val.r.num = 7; - cmp_1(eq, l+1, r+1, 3, 6, NULL, "finite range", "infinite range", __LINE__); - - // let l = 1 2 3 ... - // and r = 1 2 ... - l[0].type = l[1].type = 'i'; - l[0].val.i = 1; l[1].val.i = 2; - l[2].type = '-'; - l[2].val.r.has_delta = 1; - l[2].val.r.num = 0; - l[3].type = l[4].type = 'i'; - l[3].val.i = 3; l[4].val.i = 1; - - r[0].type = 'i'; - r[0].val.i = 1; - r[1].type = '-'; - r[1].val.r.has_delta = 1; - r[1].val.r.num = 0; - r[2].type = r[3].type = 'i'; - r[2].val.i = 1; r[3].val.i = 2; - - cmp_1(eq, l, r, 5, 4, NULL, - "infinite range", "infinite_range", __LINE__); - - // let l = 1 1 1 - // and r = 1 1 ... - l[1].val.i = 1; - l[2].type = 'i'; - l[2].val.i = 1; - r[1].val.r.has_delta = 0; - r[2].val.i = 1; - - cmp_1(eq, l, r, 3, 3, NULL, - "numbers", "infinite_range (no delta)", __LINE__); - - // let l = 1 - // and keep r = 1 1 ... - cmp_1(eq, l, r, 1, 3, NULL, - "nothing", "infinite_range (no delta)", __LINE__); - - // let l = 1 ... - // and keep r = 1 1 ... - l[0].type = '-'; - l[0].val.r.num = 0; - l[0].val.r.has_delta = 0; - l[1].type = 'i'; - l[1].val.i = 1; - cmp_1(eq, l, r, 2, 3, NULL, - "infinite_range (no delta)", "infinite_range (no delta)", __LINE__); - - - // keep l = 1 ... - // and let r = 1 ... - r[0].type = '-'; - r[0].val.r.num = 0; - r[0].val.r.has_delta = 0; - r[1].type = 'i'; - r[1].val.i = 1; - cmp_1(eq, l, r, 2, 2, NULL, "1 ...", "1 ...", __LINE__); -} - -void different_types() -{ - l[0].type = 'i'; - l[0].val.i = 0; - r[0].type = 'h'; - r[0].val.h = 1; - - // 1 > 0, but 'i' > 'h' (alphabetically) - cmp_gt(l, r, 1, 1, NULL, "int type", "huge int type", __LINE__); -} - -void multiple_args() -{ - l[0].type = r[0].type = 'i'; - l[0].val.i = r[0].val.i = 42; - l[1].type = r[1].type = 'T'; - l[1].val.T = r[1].val.T = 'I'; - l[2].type = r[2].type = 's'; - l[2].val.s = "1"; - r[2].val.s = "0"; //different values - - cmp_gt(l, r, 3, 3, NULL, "multiple args 1", "mutliple args 2", __LINE__); - - l[0].type = r[0].type = 't'; - l[0].val.t = r[0].val.t = 1; - l[1].type = 'f'; - r[1].type = 'd'; //different types - l[1].val.f = 1.0f; - r[1].val.d = 1.0; - - cmp_gt(l, r, 2, 2, NULL, "multiple args 3", "multiple args 4", __LINE__); -} - -void different_sizes() -{ - l[0].type = l[1].type = 'i'; - l[0].val.i = l[1].val.i = 42; - - cmp_gt(l, l, 2, 1, NULL, "size-2-array", "size-1-array", __LINE__); -} - -int main() -{ - ints(); - special(); - floats(); - doubles(); - huge_ints(); - timestamps(); - midi(); - blobs(); - arrays(); - ranges(); - different_types(); - multiple_args(); - different_sizes(); - - return test_summary(); -} diff -Nru zynaddsubfx-3.0.3/rtosc/test/default-value.cpp zynaddsubfx-3.0.4/rtosc/test/default-value.cpp --- zynaddsubfx-3.0.3/rtosc/test/default-value.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/default-value.cpp 2018-03-11 15:05:07.000000000 +0000 @@ -0,0 +1,482 @@ +#include +#include +#include +#include +#include + +#include "common.h" + +using namespace rtosc; + +void port_sugar() +{ + const char* presets_str = rPresetsAt(1, 3, 2); + assert_str_eq(":default 1", presets_str, + "Port sugar for presets (1)", __LINE__); + assert_str_eq("=3", presets_str+11, + "Port sugar for presets (2)", __LINE__); + assert_str_eq(":default 2", presets_str+14, + "Port sugar for presets (3)", __LINE__); + assert_str_eq("=2", presets_str+25, + "Port sugar for presets (4)", __LINE__); + + const char* multi_str = rPresetAtMulti(false, 0, 2); + assert_str_eq(":default 0", multi_str, + "Port sugar for presets (1)", __LINE__); + assert_str_eq("=false", multi_str+11, + "Port sugar for presets (2)", __LINE__); + assert_str_eq(":default 2", multi_str+18, + "Port sugar for presets (3)", __LINE__); + assert_str_eq("=false", multi_str+29, + "Port sugar for presets (4)", __LINE__); +} + +static const Ports ports = { + {"A::i", rDefault(64) rDoc("..."), NULL, NULL }, + {"B#2::i", rOpt(-2, Master) rOpt(-1, Off) + rOptions(Part1, Part2) rDefault(Off), NULL, NULL }, + {"C::T", rDefault(false), NULL, NULL}, + {"D::f", rDefault(1.0), NULL, NULL}, + {"E::i", "", NULL, NULL} +}; + +void canonical_values() +{ + const Ports ports = { + { "A::ii:cc:SS", rOpt(1, one) rOpt(2, two) rOpt(3, three), NULL, NULL } + }; + + rtosc_arg_val_t av[3]; + av[0].type = 'S'; + av[0].val.s = "two"; + av[1].type = 'i'; + av[1].val.i = 42; + av[2].type = 'S'; + av[2].val.s = "three"; + + const Port* p = ports.apropos("A"); + const char* port_args = strchr(p->name, ':'); + + assert_int_eq(1, canonicalize_arg_vals(av, 3, port_args, p->meta()), + "One argument was not converted", __LINE__); + + assert_char_eq(av[0].type, 'i', + "value was converted to correct canonical type", __LINE__); + assert_int_eq(av[0].val.i, 2, + "value was converted to correct canonical type", __LINE__); + assert_char_eq(av[1].type, 'i', + "value was not converted to canonical since " + "it was already canonical", __LINE__); + assert_char_eq(av[2].type, 'S', + "value was not converted to canonical since " + "there was no recipe left in the port args", __LINE__); + +} + +void simple_default_values() +{ + assert_str_eq(get_default_value("A", ports, NULL), "64", + "get integer default value", __LINE__); + assert_str_eq(get_default_value("B0", ports, NULL), "Off", + "get integer array default value with options", __LINE__); + assert_str_eq(get_default_value("B1", ports, NULL), "Off", + "get integer array default value with options", __LINE__); + assert_str_eq(get_default_value("C", ports, NULL), "false", + "get boolean default value", __LINE__); + assert_str_eq(get_default_value("D", ports, NULL), "1.0", + "get float default value", __LINE__); + assert_null(get_default_value("E", ports, NULL), + "get default value where there's none", __LINE__); +} + +struct Envelope +{ + static const rtosc::Ports& ports; + + bool read_or_store(const char* m, RtData& d, size_t& ref) + { + if(*rtosc_argument_string(m)) { + ref = rtosc_argument(m, 0).i; + return true; + } + else + { + d.reply(d.loc, "i", (int)ref); + return false; + } + } + + std::size_t sustain, attack_rate; + int scale_type; //!< 0=log, 1=lin + size_t env_type; + int array[4]; + + // constructor; sets all values to default + Envelope() : scale_type(1), env_type(0) + { + update_env_type_dependencies(); + } + + void update_env_type_dependencies() + { + switch(env_type) + { + case 0: + sustain = 30; + attack_rate = 40; + memset(array, 0, 4*sizeof(int)); + break; + case 1: + sustain = 127; + attack_rate = 127; + for(std::size_t i = 0; i < 4; ++i) + array[i] = i; + break; + } + } +}; + +#define rObject Envelope +// preset 0: sustain value low (30), attack slow (40) +// preset 1: all knobs high +static const Ports envelope_ports = { + /* + * usual parameters + */ + {"sustain::i", rProp(parameter) rDefaultDepends(env_type) + rMap(default 0, 30) rMap(default 1, 127), NULL, + [](const char* m, RtData& d) { + Envelope* obj = static_cast(d.obj); + obj->read_or_store(m, d, obj->sustain); }}, + {"attack_rate::i", rProp(parameter) rDefaultDepends(env_type) + rMap(default 0, 40) rDefault(127), NULL, + [](const char* m, RtData& d) { + Envelope* obj = static_cast(d.obj); + obj->read_or_store(m, d, obj->attack_rate); }}, + rOption(scale_type, rProp(parameter) rOpt(0, logarithmic), rOpt(1, linear), + rDefault(linear), "scale type"), + // array: env_type 0: 0 0 0 0 + // env_type 1: 0 1 2 3 + rArrayI(array, 4, rDefaultDepends(env_type), + /*rPreset(0, 0),*/ + rPreset(0, [4x0]), + rPreset(1, [0 1 2 3]), // bash the whole array to 0 for preset 1 + rDefault(3), // all not yet specified values are set to 3 + "some bundle"), + // port without rDefault macros + // must not be noted for the savefile + {"paste:b", rProp(parameter), NULL, + [](const char* , RtData& ) { assert(false); } }, + /* + * envelope type (~preset) + */ + {"env_type::i", rProp(parameter) rDefault(0), NULL, + [](const char* m, RtData& d) { + Envelope* obj = static_cast(d.obj); + if(obj->read_or_store(m, d, obj->env_type)) { + obj->update_env_type_dependencies(); + }}} +}; +#undef rObject + +const rtosc::Ports& Envelope::ports = envelope_ports; + +void envelope_types() +{ + // by default, the envelope has preset 0, i.e. + // sustain value low (30), attack slow (40) + assert_str_eq("30", get_default_value("sustain::i", envelope_ports, NULL), + "get default value without runtime (1)", __LINE__); + assert_str_eq("40", get_default_value("attack_rate::i", envelope_ports, + NULL), + "get default value without runtime (2)", __LINE__); + + Envelope e1, e2; + + e1.sustain = 40; // != envelope 0's default + e2.sustain = 0; // != envelope 1's default + e2.attack_rate = 127; // = envelope 1's default + e2.env_type = 1; // != default + e2.scale_type = 0; // != default + for(size_t i = 0; i < 4; ++i) + e2.array[i] = 3-i; + + assert_str_eq("30", get_default_value("sustain::i", envelope_ports, &e1), + "get default value with runtime (1)", __LINE__); + assert_str_eq("40", get_default_value("attack_rate::i", envelope_ports, + &e1), + "get default value with runtime (2)", __LINE__); + + assert_str_eq("127", get_default_value("sustain::i", envelope_ports, &e2), + "get default value with runtime (3)", __LINE__); + assert_str_eq("127", + get_default_value("attack_rate::i", envelope_ports, &e2 ), + "get default value with runtime (4)", __LINE__); + + assert_str_eq("/sustain 40", + get_changed_values(envelope_ports, &e1).c_str(), + "get changed values where none are changed", __LINE__); + const char* changed_e2 = "/sustain 0\n/scale_type logarithmic\n" + "/array [3 2 1 0]\n/env_type 1"; + assert_str_eq(changed_e2, + get_changed_values(envelope_ports, &e2).c_str(), + "get changed values where three are changed", __LINE__); + + // restore values to envelope from a savefile + // this is indirectly related to default values + Envelope e3; + // note: setting the envelope type does overwrite all other values + // this means that "/sustain 60" has to be executed after + // "/env_type 1", even though the order in the savefile is different + // dispatch_printed_messages() executes these messages in the + // correct order. + int num = dispatch_printed_messages("%this is a savefile\n" + "/sustain 60 /env_type 1\n" + "/attack_rate 10 % a useless comment\n" + "/scale_type 0", + envelope_ports, &e3); + assert_int_eq(4, num, + "restore values from savefile - correct number", __LINE__); + assert_int_eq(60, e3.sustain, "restore values from savefile (1)", __LINE__); + assert_int_eq(1, e3.env_type, "restore values from savefile (2)", __LINE__); + assert_int_eq(10, e3.attack_rate, + "restore values from savefile (3)", __LINE__); + assert_int_eq(0, e3.scale_type, + "restore values from savefile (4)", __LINE__); + + num = dispatch_printed_messages("/sustain 60 /env_type $1", + envelope_ports, &e3); + assert_int_eq(-13, num, + "restore values from a corrupt savefile (3)", __LINE__); + + std::string appname = "default-values-test", + appver_str = "0.0.1"; + rtosc_version appver = rtosc_version { 0, 0, 1 }; + std::string savefile = save_to_file(envelope_ports, &e2, + appname.c_str(), + appver); + char cur_rtosc_buf[12]; + rtosc_version cur_rtosc = rtosc_current_version(); + rtosc_version_print_to_12byte_str(&cur_rtosc, cur_rtosc_buf); + std::string exp_savefile = "% RT OSC v"; + exp_savefile += cur_rtosc_buf; + exp_savefile += " savefile\n" + "% " + appname + " v" + appver_str + "\n"; + exp_savefile += changed_e2; + + assert_str_eq(exp_savefile.c_str(), savefile.c_str(), + "save testfile", __LINE__); + + Envelope e2_restored; // e2 will be loaded from the savefile + + int rval = load_from_file(exp_savefile.c_str(), + envelope_ports, &e2_restored, + appname.c_str(), appver); + + assert_int_eq(4, rval, + "load savefile from file, 4 messages read", __LINE__); + + auto check_restored = [](int i1, int i2, const char* member) { + std::string tcs = "restore "; tcs += member; tcs += " from file"; + assert_int_eq(i1, i2, tcs.c_str(), __LINE__); + }; + + check_restored(e2.sustain, e2_restored.sustain, "sustain value"); + check_restored(e2.attack_rate, e2_restored.attack_rate, "attack rate"); + check_restored(e2.scale_type, e2_restored.scale_type, "scale type"); + check_restored(e2.array[0], e2_restored.array[0], "array[0]"); + check_restored(e2.array[1], e2_restored.array[1], "array[1]"); + check_restored(e2.array[2], e2_restored.array[2], "array[2]"); + check_restored(e2.array[3], e2_restored.array[3], "array[3]"); + check_restored(e2.env_type, e2_restored.env_type, "envelope type"); +} + +void presets() +{ + // for presets, it would be exactly the same, + // with only the env_type port being named as "preset" port. +} + +struct SavefileTest +{ + static const rtosc::Ports& ports; + + int new_param; + bool very_old_version; + int further_param; +}; + +#define rObject SavefileTest +static const Ports savefile_test_ports = { + {"new_param:i", "", NULL, + [](const char* m, RtData& d) { + SavefileTest* obj = static_cast(d.obj); + obj->new_param = rtosc_argument(m, 0).i; }}, + {"very_old_version:T:F", "", NULL, + [](const char* m, RtData& d) { + SavefileTest* obj = static_cast(d.obj); + obj->very_old_version = rtosc_argument(m, 0).T; }}, + {"further_param:i", "", NULL, + [](const char* m, RtData& d) { + SavefileTest* obj = static_cast(d.obj); + obj->further_param = rtosc_argument(m, 0).i; }} +}; +#undef rObject + +const rtosc::Ports& SavefileTest::ports = savefile_test_ports; + +void savefiles() +{ + int rval = load_from_file("% NOT AN RT OSC v0.0.1 savefile\n" + // => error: it should be "RT OSC" + "% my_application v0.0.1", + savefile_test_ports, NULL, + "my_application", rtosc_version {1, 2, 3}); + assert_int_eq(-1, rval, + "reject file that has an invalid first line", __LINE__); + + rval = load_from_file("% RT OSC v0.0.1 savefile\n" + "% not_my_application v0.0.1", + // => error: application name mismatch + savefile_test_ports, NULL, + "my_application", rtosc_version {1, 2, 3}); + assert_int_eq(-25, rval, + "reject file from another application", __LINE__); + + struct my_dispatcher_t : public rtosc::savefile_dispatcher_t + { + + int modify(char* portname, + size_t maxargs, size_t nargs, rtosc_arg_val_t* args, + bool round2) + { + int newsize = nargs; + + if(round2) + { + if(rtosc_version_cmp(rtosc_filever, + rtosc_version{0, 0, 4}) < 0) + /* version < 0.0.4 ? */ + { + if(rtosc_version_cmp(rtosc_filever, + rtosc_version{0, 0, 3}) < 0) + { + if(rtosc_version_cmp(rtosc_filever, + rtosc_version{0, 0, 2}) < 0) + { + { + // dispatch an additional message + char buffer[32]; + rtosc_message(buffer, 32, + "/very_old_version", "T"); + operator()(buffer); + } + + if(nargs == 0 && maxargs >= 1) + { + memcpy(portname+1, "new", 3); + args[0].val.i = 42; + args[0].type = 'i'; + newsize = 1; + } + else // wrong argument count or not enough space + newsize = abort; + } + else // i.e. version = 0.0.2 + newsize = discard; + } + else // i.e. version = 0.0.3 + newsize = abort; + } + /* for versions >= 0.0.4, we just let "/old_param" pass */ + /* in order to get a dispatch error */ + } + else { + // discard "/old_param" in round 1, since nothing depends on it + newsize = discard; + } + + return newsize; + } + + int on_dispatch(size_t, char* portname, + size_t maxargs, size_t nargs, rtosc_arg_val_t* args, + bool round2, dependency_t dependency) + { + return (portname[1] == 'o' && !strcmp(portname, "/old_param")) + ? modify(portname, maxargs, nargs, args, round2) + : default_response(nargs, round2, dependency); + } + }; + + my_dispatcher_t my_dispatcher; + SavefileTest sft; + + auto reset_savefile = [](SavefileTest& sft) { + sft.new_param = 0; sft.very_old_version = false; + sft.further_param = 0; + }; +#define MAKE_TESTFILE(ver) "% RT OSC " ver " savefile\n" \ + "% savefiletest v1.2.3\n" \ + "/old_param\n" \ + "/further_param 123" + + reset_savefile(sft); + rval = load_from_file(MAKE_TESTFILE("v0.0.1"), + savefile_test_ports, &sft, + "savefiletest", rtosc_version {1, 2, 3}, + &my_dispatcher); + assert_int_eq(2, rval, "savefile: 2 messages read for v0.0.1", __LINE__); + assert_int_eq(42, sft.new_param, "port renaming works", __LINE__); + assert_true(sft.very_old_version, + "additional messages work", __LINE__); + assert_int_eq(123, sft.further_param, + "further parameter is being dispatched for v0.0.1", __LINE__); + + reset_savefile(sft); + rval = load_from_file(MAKE_TESTFILE("v0.0.2"), + savefile_test_ports, &sft, + "savefiletest", rtosc_version {1, 2, 3}, + &my_dispatcher); + assert_int_eq(2, rval, "savefile: 2 messages read for v0.0.2", __LINE__); + assert_int_eq(0, sft.new_param, "message discarding works", __LINE__); + assert_int_eq(123, sft.further_param, + "further parameter is being dispatched for v0.0.2", __LINE__); + + reset_savefile(sft); + // test with v0.0.3 + // abort explicitly (see savefile dispatcher implementation) + rval = load_from_file(MAKE_TESTFILE("v0.0.3"), + savefile_test_ports, &sft, + "savefiletest", rtosc_version {1, 2, 3}, + &my_dispatcher); + assert_int_eq(-59, rval, "savefile: 1 error for v0.0.3", __LINE__); + assert_int_eq(123, sft.further_param, + "no further parameter is being dispatched for v0.0.3", + __LINE__); + // test with v0.0.4 + // abort implicitly (the port is unknown) + rval = load_from_file(MAKE_TESTFILE("v0.0.4"), + savefile_test_ports, &sft, + "savefiletest", rtosc_version {1, 2, 3}, + &my_dispatcher); + assert_int_eq(-59, rval, "savefile: 1 error for v0.0.4", __LINE__); + assert_int_eq(123, sft.further_param, + "no further parameter is being dispatched for v0.0.4", + __LINE__); + +#undef MAKE_TESTFILE +} + +int main() +{ + port_sugar(); + + canonical_values(); + simple_default_values(); + envelope_types(); + presets(); + savefiles(); + + return test_summary(); +} diff -Nru zynaddsubfx-3.0.3/rtosc/test/default-values.cpp zynaddsubfx-3.0.4/rtosc/test/default-values.cpp --- zynaddsubfx-3.0.3/rtosc/test/default-values.cpp 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/default-values.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,480 +0,0 @@ -#include -#include -#include - -#include "common.h" - -using namespace rtosc; - -void port_sugar() -{ - const char* presets_str = rPresetsAt(1, 3, 2); - assert_str_eq(":default 1", presets_str, - "Port sugar for presets (1)", __LINE__); - assert_str_eq("=3", presets_str+11, - "Port sugar for presets (2)", __LINE__); - assert_str_eq(":default 2", presets_str+14, - "Port sugar for presets (3)", __LINE__); - assert_str_eq("=2", presets_str+25, - "Port sugar for presets (4)", __LINE__); - - const char* multi_str = rPresetAtMulti(false, 0, 2); - assert_str_eq(":default 0", multi_str, - "Port sugar for presets (1)", __LINE__); - assert_str_eq("=false", multi_str+11, - "Port sugar for presets (2)", __LINE__); - assert_str_eq(":default 2", multi_str+18, - "Port sugar for presets (3)", __LINE__); - assert_str_eq("=false", multi_str+29, - "Port sugar for presets (4)", __LINE__); -} - -static const Ports ports = { - {"A::i", rDefault(64) rDoc("..."), NULL, NULL }, - {"B#2::i", rOpt(-2, Master) rOpt(-1, Off) - rOptions(Part1, Part2) rDefault(Off), NULL, NULL }, - {"C::T", rDefault(false), NULL, NULL}, - {"D::f", rDefault(1.0), NULL, NULL}, - {"E::i", "", NULL, NULL} -}; - -void canonical_values() -{ - const Ports ports = { - { "A::ii:cc:SS", rOpt(1, one) rOpt(2, two) rOpt(3, three), NULL, NULL } - }; - - rtosc_arg_val_t av[3]; - av[0].type = 'S'; - av[0].val.s = "two"; - av[1].type = 'i'; - av[1].val.i = 42; - av[2].type = 'S'; - av[2].val.s = "three"; - - const Port* p = ports.apropos("A"); - const char* port_args = strchr(p->name, ':'); - - assert_int_eq(1, canonicalize_arg_vals(av, 3, port_args, p->meta()), - "One argument was not converted", __LINE__); - - assert_char_eq(av[0].type, 'i', - "value was converted to correct canonical type", __LINE__); - assert_int_eq(av[0].val.i, 2, - "value was converted to correct canonical type", __LINE__); - assert_char_eq(av[1].type, 'i', - "value was not converted to canonical since " - "it was already canonical", __LINE__); - assert_char_eq(av[2].type, 'S', - "value was not converted to canonical since " - "there was no recipe left in the port args", __LINE__); - -} - -void simple_default_values() -{ - assert_str_eq(get_default_value("A", ports, NULL), "64", - "get integer default value", __LINE__); - assert_str_eq(get_default_value("B0", ports, NULL), "Off", - "get integer array default value with options", __LINE__); - assert_str_eq(get_default_value("B1", ports, NULL), "Off", - "get integer array default value with options", __LINE__); - assert_str_eq(get_default_value("C", ports, NULL), "false", - "get boolean default value", __LINE__); - assert_str_eq(get_default_value("D", ports, NULL), "1.0", - "get float default value", __LINE__); - assert_null(get_default_value("E", ports, NULL), - "get default value where there's none", __LINE__); -} - -struct Envelope -{ - static const rtosc::Ports& ports; - - bool read_or_store(const char* m, RtData& d, size_t& ref) - { - if(*rtosc_argument_string(m)) { - ref = rtosc_argument(m, 0).i; - return true; - } - else - { - d.reply(d.loc, "i", (int)ref); - return false; - } - } - - std::size_t sustain, attack_rate; - int scale_type; //!< 0=log, 1=lin - size_t env_type; - int array[4]; - - // constructor; sets all values to default - Envelope() : scale_type(1), env_type(0) - { - update_env_type_dependencies(); - } - - void update_env_type_dependencies() - { - switch(env_type) - { - case 0: - sustain = 30; - attack_rate = 40; - memset(array, 0, 4*sizeof(int)); - break; - case 1: - sustain = 127; - attack_rate = 127; - for(std::size_t i = 0; i < 4; ++i) - array[i] = i; - break; - } - } -}; - -#define rObject Envelope -// preset 0: sustain value low (30), attack slow (40) -// preset 1: all knobs high -static const Ports envelope_ports = { - /* - * usual parameters - */ - {"sustain::i", rProp(parameter) rDefaultDepends(env_type) - rMap(default 0, 30) rMap(default 1, 127), NULL, - [](const char* m, RtData& d) { - Envelope* obj = static_cast(d.obj); - obj->read_or_store(m, d, obj->sustain); }}, - {"attack_rate::i", rProp(parameter) rDefaultDepends(env_type) - rMap(default 0, 40) rDefault(127), NULL, - [](const char* m, RtData& d) { - Envelope* obj = static_cast(d.obj); - obj->read_or_store(m, d, obj->attack_rate); }}, - rOption(scale_type, rProp(parameter) rOpt(0, logarithmic), rOpt(1, linear), - rDefault(linear), "scale type"), - // array: env_type 0: 0 0 0 0 - // env_type 1: 0 1 2 3 - rArrayI(array, 4, rDefaultDepends(env_type), - /*rPreset(0, 0),*/ - rPreset(0, [4x0]), - rPreset(1, [0 1 2 3]), // bash the whole array to 0 for preset 1 - rDefault(3), // all not yet specified values are set to 3 - "some bundle"), - // port without rDefault macros - // must not be noted for the savefile - {"paste:b", rProp(parameter), NULL, - [](const char* , RtData& ) { assert(false); } }, - /* - * envelope type (~preset) - */ - {"env_type::i", rProp(parameter) rDefault(0), NULL, - [](const char* m, RtData& d) { - Envelope* obj = static_cast(d.obj); - if(obj->read_or_store(m, d, obj->env_type)) { - obj->update_env_type_dependencies(); - }}} -}; -#undef rObject - -const rtosc::Ports& Envelope::ports = envelope_ports; - -void envelope_types() -{ - // by default, the envelope has preset 0, i.e. - // sustain value low (30), attack slow (40) - assert_str_eq("30", get_default_value("sustain::i", envelope_ports, NULL), - "get default value without runtime (1)", __LINE__); - assert_str_eq("40", get_default_value("attack_rate::i", envelope_ports, - NULL), - "get default value without runtime (2)", __LINE__); - - Envelope e1, e2; - - e1.sustain = 40; // != envelope 0's default - e2.sustain = 0; // != envelope 1's default - e2.attack_rate = 127; // = envelope 1's default - e2.env_type = 1; // != default - e2.scale_type = 0; // != default - for(size_t i = 0; i < 4; ++i) - e2.array[i] = 3-i; - - assert_str_eq("30", get_default_value("sustain::i", envelope_ports, &e1), - "get default value with runtime (1)", __LINE__); - assert_str_eq("40", get_default_value("attack_rate::i", envelope_ports, - &e1), - "get default value with runtime (2)", __LINE__); - - assert_str_eq("127", get_default_value("sustain::i", envelope_ports, &e2), - "get default value with runtime (3)", __LINE__); - assert_str_eq("127", - get_default_value("attack_rate::i", envelope_ports, &e2 ), - "get default value with runtime (4)", __LINE__); - - assert_str_eq("/sustain 40", - get_changed_values(envelope_ports, &e1).c_str(), - "get changed values where none are changed", __LINE__); - const char* changed_e2 = "/sustain 0\n/scale_type logarithmic\n" - "/array [3 2 1 0]\n/env_type 1"; - assert_str_eq(changed_e2, - get_changed_values(envelope_ports, &e2).c_str(), - "get changed values where three are changed", __LINE__); - - // restore values to envelope from a savefile - // this is indirectly related to default values - Envelope e3; - // note: setting the envelope type does overwrite all other values - // this means that "/sustain 60" has to be executed after - // "/env_type 1", even though the order in the savefile is different - // dispatch_printed_messages() executes these messages in the - // correct order. - int num = dispatch_printed_messages("%this is a savefile\n" - "/sustain 60 /env_type 1\n" - "/attack_rate 10 % a useless comment\n" - "/scale_type 0", - envelope_ports, &e3); - assert_int_eq(4, num, - "restore values from savefile - correct number", __LINE__); - assert_int_eq(60, e3.sustain, "restore values from savefile (1)", __LINE__); - assert_int_eq(1, e3.env_type, "restore values from savefile (2)", __LINE__); - assert_int_eq(10, e3.attack_rate, - "restore values from savefile (3)", __LINE__); - assert_int_eq(0, e3.scale_type, - "restore values from savefile (4)", __LINE__); - - num = dispatch_printed_messages("/sustain 60 /env_type $1", - envelope_ports, &e3); - assert_int_eq(-13, num, - "restore values from a corrupt savefile (3)", __LINE__); - - std::string appname = "default-values-test", - appver_str = "0.0.1"; - rtosc_version appver = rtosc_version { 0, 0, 1 }; - std::string savefile = save_to_file(envelope_ports, &e2, - appname.c_str(), - appver); - char cur_rtosc_buf[12]; - rtosc_version cur_rtosc = rtosc_current_version(); - rtosc_version_print_to_12byte_str(&cur_rtosc, cur_rtosc_buf); - std::string exp_savefile = "% RT OSC v"; - exp_savefile += cur_rtosc_buf; - exp_savefile += " savefile\n" - "% " + appname + " v" + appver_str + "\n"; - exp_savefile += changed_e2; - - assert_str_eq(exp_savefile.c_str(), savefile.c_str(), - "save testfile", __LINE__); - - Envelope e2_restored; // e2 will be loaded from the savefile - - int rval = load_from_file(exp_savefile.c_str(), - envelope_ports, &e2_restored, - appname.c_str(), appver); - - assert_int_eq(4, rval, - "load savefile from file, 4 messages read", __LINE__); - - auto check_restored = [](int i1, int i2, const char* member) { - std::string tcs = "restore "; tcs += member; tcs += " from file"; - assert_int_eq(i1, i2, tcs.c_str(), __LINE__); - }; - - check_restored(e2.sustain, e2_restored.sustain, "sustain value"); - check_restored(e2.attack_rate, e2_restored.attack_rate, "attack rate"); - check_restored(e2.scale_type, e2_restored.scale_type, "scale type"); - check_restored(e2.array[0], e2_restored.array[0], "array[0]"); - check_restored(e2.array[1], e2_restored.array[1], "array[1]"); - check_restored(e2.array[2], e2_restored.array[2], "array[2]"); - check_restored(e2.array[3], e2_restored.array[3], "array[3]"); - check_restored(e2.env_type, e2_restored.env_type, "envelope type"); -} - -void presets() -{ - // for presets, it would be exactly the same, - // with only the env_type port being named as "preset" port. -} - -struct SavefileTest -{ - static const rtosc::Ports& ports; - - int new_param; - bool very_old_version; - int further_param; -}; - -#define rObject SavefileTest -static const Ports savefile_test_ports = { - {"new_param:i", "", NULL, - [](const char* m, RtData& d) { - SavefileTest* obj = static_cast(d.obj); - obj->new_param = rtosc_argument(m, 0).i; }}, - {"very_old_version:T:F", "", NULL, - [](const char* m, RtData& d) { - SavefileTest* obj = static_cast(d.obj); - obj->very_old_version = rtosc_argument(m, 0).T; }}, - {"further_param:i", "", NULL, - [](const char* m, RtData& d) { - SavefileTest* obj = static_cast(d.obj); - obj->further_param = rtosc_argument(m, 0).i; }} -}; -#undef rObject - -const rtosc::Ports& SavefileTest::ports = savefile_test_ports; - -void savefiles() -{ - int rval = load_from_file("% NOT AN RT OSC v0.0.1 savefile\n" - // => error: it should be "RT OSC" - "% my_application v0.0.1", - savefile_test_ports, NULL, - "my_application", rtosc_version {1, 2, 3}); - assert_int_eq(-1, rval, - "reject file that has an invalid first line", __LINE__); - - rval = load_from_file("% RT OSC v0.0.1 savefile\n" - "% not_my_application v0.0.1", - // => error: application name mismatch - savefile_test_ports, NULL, - "my_application", rtosc_version {1, 2, 3}); - assert_int_eq(-25, rval, - "reject file from another application", __LINE__); - - struct my_dispatcher_t : public rtosc::savefile_dispatcher_t - { - - int modify(char* portname, - size_t maxargs, size_t nargs, rtosc_arg_val_t* args, - bool round2) - { - int newsize = nargs; - - if(round2) - { - if(rtosc_version_cmp(rtosc_filever, - rtosc_version{0, 0, 4}) < 0) - /* version < 0.0.4 ? */ - { - if(rtosc_version_cmp(rtosc_filever, - rtosc_version{0, 0, 3}) < 0) - { - if(rtosc_version_cmp(rtosc_filever, - rtosc_version{0, 0, 2}) < 0) - { - { - // dispatch an additional message - char buffer[32]; - rtosc_message(buffer, 32, - "/very_old_version", "T"); - operator()(buffer); - } - - if(nargs == 0 && maxargs >= 1) - { - memcpy(portname+1, "new", 3); - args[0].val.i = 42; - args[0].type = 'i'; - newsize = 1; - } - else // wrong argument count or not enough space - newsize = abort; - } - else // i.e. version = 0.0.2 - newsize = discard; - } - else // i.e. version = 0.0.3 - newsize = abort; - } - /* for versions >= 0.0.4, we just let "/old_param" pass */ - /* in order to get a dispatch error */ - } - else { - // discard "/old_param" in round 1, since nothing depends on it - newsize = discard; - } - - return newsize; - } - - int on_dispatch(size_t, char* portname, - size_t maxargs, size_t nargs, rtosc_arg_val_t* args, - bool round2, dependency_t dependency) - { - return (portname[1] == 'o' && !strcmp(portname, "/old_param")) - ? modify(portname, maxargs, nargs, args, round2) - : default_response(nargs, round2, dependency); - } - }; - - my_dispatcher_t my_dispatcher; - SavefileTest sft; - - auto reset_savefile = [](SavefileTest& sft) { - sft.new_param = 0; sft.very_old_version = false; - sft.further_param = 0; - }; -#define MAKE_TESTFILE(ver) "% RT OSC " ver " savefile\n" \ - "% savefiletest v1.2.3\n" \ - "/old_param\n" \ - "/further_param 123" - - reset_savefile(sft); - rval = load_from_file(MAKE_TESTFILE("v0.0.1"), - savefile_test_ports, &sft, - "savefiletest", rtosc_version {1, 2, 3}, - &my_dispatcher); - assert_int_eq(2, rval, "savefile: 2 messages read for v0.0.1", __LINE__); - assert_int_eq(42, sft.new_param, "port renaming works", __LINE__); - assert_true(sft.very_old_version, - "additional messages work", __LINE__); - assert_int_eq(123, sft.further_param, - "further parameter is being dispatched for v0.0.1", __LINE__); - - reset_savefile(sft); - rval = load_from_file(MAKE_TESTFILE("v0.0.2"), - savefile_test_ports, &sft, - "savefiletest", rtosc_version {1, 2, 3}, - &my_dispatcher); - assert_int_eq(2, rval, "savefile: 2 messages read for v0.0.2", __LINE__); - assert_int_eq(0, sft.new_param, "message discarding works", __LINE__); - assert_int_eq(123, sft.further_param, - "further parameter is being dispatched for v0.0.2", __LINE__); - - reset_savefile(sft); - // test with v0.0.3 - // abort explicitly (see savefile dispatcher implementation) - rval = load_from_file(MAKE_TESTFILE("v0.0.3"), - savefile_test_ports, &sft, - "savefiletest", rtosc_version {1, 2, 3}, - &my_dispatcher); - assert_int_eq(-59, rval, "savefile: 1 error for v0.0.3", __LINE__); - assert_int_eq(123, sft.further_param, - "no further parameter is being dispatched for v0.0.3", - __LINE__); - // test with v0.0.4 - // abort implicitly (the port is unknown) - rval = load_from_file(MAKE_TESTFILE("v0.0.4"), - savefile_test_ports, &sft, - "savefiletest", rtosc_version {1, 2, 3}, - &my_dispatcher); - assert_int_eq(-59, rval, "savefile: 1 error for v0.0.4", __LINE__); - assert_int_eq(123, sft.further_param, - "no further parameter is being dispatched for v0.0.4", - __LINE__); - -#undef MAKE_TESTFILE -} - -int main() -{ - port_sugar(); - - canonical_values(); - simple_default_values(); - envelope_types(); - presets(); - savefiles(); - - return test_summary(); -} diff -Nru zynaddsubfx-3.0.3/rtosc/test/nested-bundles.c zynaddsubfx-3.0.4/rtosc/test/nested-bundles.c --- zynaddsubfx-3.0.3/rtosc/test/nested-bundles.c 2015-10-23 14:47:50.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/nested-bundles.c 2019-03-03 15:19:10.000000000 +0000 @@ -81,12 +81,12 @@ "Verify Bundle 2's Subelements", __LINE__); assert_str_eq("bundle", rtosc_argument(rtosc_bundle_fetch(rtosc_bundle_fetch(buffer_d, 1), 1), 0).s, "Verify Nested Message's Integrety", __LINE__); - + //Verify the failure behavior when a bad length is provided assert_int_eq(2, rtosc_bundle_elements(buffer_d, len-1), "Verify Aparent Bundles With Truncated Length", __LINE__); assert_int_eq(0, rtosc_message_length(buffer_d, len-1), "Verify Bad Message Is Detected With Truncation", __LINE__); - return global_err ? EXIT_FAILURE : EXIT_SUCCESS; + return test_summary(); } diff -Nru zynaddsubfx-3.0.3/rtosc/test/null-messages.c zynaddsubfx-3.0.4/rtosc/test/null-messages.c --- zynaddsubfx-3.0.3/rtosc/test/null-messages.c 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/null-messages.c 2019-03-03 15:19:10.000000000 +0000 @@ -7,5 +7,5 @@ //Verify that given a null buffer, it does not segfault assert_int_eq(20, rtosc_message(0,0,"/page/poge","TIF"), "Build A Message With NULL Buffer", __LINE__); - return 0; + return test_summary(); } diff -Nru zynaddsubfx-3.0.3/rtosc/test/osc-spec.c zynaddsubfx-3.0.4/rtosc/test/osc-spec.c --- zynaddsubfx-3.0.3/rtosc/test/osc-spec.c 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/osc-spec.c 2019-03-03 15:19:10.000000000 +0000 @@ -33,7 +33,7 @@ /* * The OSC 1.0 spec provides two example messages at * http://opensoundcontrol.org/spec-1_0-examples - * + * * This test verifies that these messages are identically serialized */ int main() @@ -50,5 +50,5 @@ "Creating Multi Argument Message From OSC Spec", __LINE__); assert_hex_eq((char*)message_two, buffer, sizeof(message_two), sz, "Validating Binary Representation of Message 2", __LINE__); - return 0; + return test_summary(); } diff -Nru zynaddsubfx-3.0.3/rtosc/test/performance.cpp zynaddsubfx-3.0.4/rtosc/test/performance.cpp --- zynaddsubfx-3.0.3/rtosc/test/performance.cpp 2015-10-23 14:47:50.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/performance.cpp 2018-03-11 15:05:07.000000000 +0000 @@ -3,6 +3,13 @@ #include #include #include +#include +#include +#include + +#ifdef HAVE_LIBLO +#include +#endif #include #include @@ -12,7 +19,28 @@ { (void) data; (void) d; -}; +} + +#ifdef HAVE_LIBLO +int liblo_do_nothing(const char *path, const char *types, + lo_arg **argv, int argc, lo_message msg, void *user_data) +{ + (void) path; + (void) types; + (void) argv; + (void) argc; + (void) msg; + (void) user_data; + return 0; /* = message has been handled */ +} + +int liblo_count(const char *path, const char *types, + lo_arg **argv, int argc, lo_message msg, void *user_data) +{ + ++*((int*)user_data); + return liblo_do_nothing(path, types, argv, argc, msg, user_data); +} +#endif #define dummy(x) {#x, NULL, NULL, do_nothing} #define dummyT(x) {#x"::T:F", NULL, NULL, do_nothing} @@ -23,7 +51,7 @@ //TODO Consider performance penalty of argument specifier //chains -//55 elements long (assuming I can count properly +//55 elements long (assuming I can count properly) Ports port_table = { dummy(oscil/), dummy(mod-oscil/), @@ -84,9 +112,28 @@ char events[20][1024]; char loc_buffer[1024]; - + +static void liblo_error_cb(int i, const char *m, const char *loc) +{ + fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc); +} + +void print_results(const char* libname, + clock_t t_on, clock_t t_off, int repeats) +{ + double seconds = (t_off - t_on) * 1.0 / CLOCKS_PER_SEC; + double ns_per_dispatch = seconds*1e9/(repeats*20.0); + assert(ns_per_dispatch < 100000.00); // fit the field width + + printf("%s Performance: %8.2f seconds for the test\n", libname, seconds); + printf("%s Performace: %8.2f ns per dispatch\n", libname, ns_per_dispatch); +} + int main() { + /* + * create all the messages + */ rtosc_message(events[0], 1024, "PFMDetune", "i", 23); rtosc_message(events[1], 1024, "oscil/blam", "c", 23); rtosc_message(events[2], 1024, "PFilterEnabled", "T"); @@ -112,8 +159,11 @@ d.obj = d.loc = loc_buffer; d.matches = 0; + /* + * run RTOSC + */ int repeats = 200000; - int t_on = clock(); // timer before calling func + clock_t t_on = clock(); // timer before calling func for(int j=0; j<200000; ++j) { for(int i=0; i<20; ++i){ port_table.dispatch(events[i], d); @@ -122,8 +172,125 @@ //printf("Matches: %d\n", d.matches); assert(d.matches == 3600000); int t_off = clock(); // timer when func returns + print_results("RTOSC", t_on, t_off, repeats); - double seconds = (t_off - t_on) * 1.0 / CLOCKS_PER_SEC; - printf("RTOSC Performance: %f seconds for the test\n", seconds); - printf("RTOSC Performace: %f ns per dispatch\n", seconds*1e9/(repeats*20.0)); +#ifdef HAVE_LIBLO + /* + * prepare LIBLO message data + */ + struct liblo_message_prepared + { + char* memory; + size_t exact_size; + }; + std::vector lo_messages; + + int max = sizeof(events)/sizeof(events[0]); + lo_messages.resize(max); + for(int i = 0; i < max; ++i) + { + lo_messages[i].memory = events[i]; + lo_messages[i].exact_size = rtosc_message_length(events[i], 1024); + } + + /* + * prepare LIBLO port info + */ + struct liblo_port_info_t + { + std::string name; + std::vector typespecs; + bool accept_all = false; + }; + std::vector liblo_port_info; + + int port_size = port_table.ports.size(); + liblo_port_info.resize(port_size); + std::vector::iterator pinf = + liblo_port_info.begin(); + for(const Port& port : port_table) + { + for(const char* p = port.name; *p && *p != ':'; ++p) + pinf->name.push_back(*p); + + // version of strchr() which will return end of string on miss + auto m_strchr = [&](const char* s, char c) -> const char* + { + for(; *s && *s != c; ++s) ; + return s; + }; + + const char* itr = m_strchr(port.name, ':'), *next; + if(!*itr) + pinf->accept_all = true; + else for(; *itr; itr = next) + { + next = m_strchr(itr+1, ':'); + if(itr[1]) + pinf->typespecs.emplace_back(itr+1, next - (itr+1)); + } + ++pinf; + } + + // liblo does not support trees, so messages like 'oscil/blam' won't + // dispatch at ports like 'oscil/'. we need to help them a bit... + for(const liblo_message_prepared& lmp : lo_messages) + if(strchr(lmp.memory, '/')) + { + liblo_port_info_t pinf; + pinf.name = lmp.memory; + pinf.typespecs.push_back(""); + pinf.accept_all = true; + liblo_port_info.push_back(pinf); + } + + auto add_methods = [&](const std::vector& port_info, + lo_server server, lo_method_handler handler, + void* user_data = nullptr) + { + for(const liblo_port_info_t& pinf : port_info) + { + if(pinf.accept_all && pinf.name[pinf.name.length()-1] != '/') + lo_server_add_method(server, pinf.name.c_str(), nullptr, + handler, user_data); + else for(const std::string& spec : pinf.typespecs) + lo_server_add_method(server, pinf.name.c_str(), spec.c_str(), + handler, user_data); + } + }; + + /* + * test LIBLO + */ + int counter = 0; + lo_server test_server = lo_server_new(nullptr, liblo_error_cb); + add_methods(liblo_port_info, test_server, liblo_count, &counter); + + for(int i=0; i<20; ++i) + { + int ok = lo_server_dispatch_data(test_server, lo_messages[i].memory, + lo_messages[i].exact_size); + assert(ok); + (void)ok; + } + assert(counter == 18); + + /* + * run LIBLO + */ + lo_server server = lo_server_new(nullptr, liblo_error_cb); + add_methods(liblo_port_info, server, liblo_do_nothing); + + t_on = clock(); // timer before calling func + for(int j=0; j<200000; ++j) { + for(int i=0; i<20; ++i){ + lo_server_dispatch_data(server, lo_messages[i].memory, + lo_messages[i].exact_size); + } + } + t_off = clock(); // timer when func returns + print_results("LIBLO", t_on, t_off, repeats); +#endif + + return EXIT_SUCCESS; } diff -Nru zynaddsubfx-3.0.3/rtosc/test/port-checker.cpp zynaddsubfx-3.0.4/rtosc/test/port-checker.cpp --- zynaddsubfx-3.0.3/rtosc/test/port-checker.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/port-checker.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,788 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "port-checker.h" + +namespace rtosc { + +static constexpr const issue_t _m_issue_types_arr[(int)issue::number] = +{ +{ + issue::duplicate_parameter, + "multiple parameters with same name", + "parameters are duplicates", + severity::hint +},{ + issue::parameter_not_queryable, + "parameter not queryable", + "parameters can not be queried", + severity::error +},{ + issue::parameter_not_replied, + "parameter not replied", + "parameters are not replied", + severity::error +},{ + issue::parameter_not_broadcast, + "parameter not broadcast", + "parameters are not broadcast", + severity::warning +},{ + issue::option_port_not_si, + "option port not \"S\" or \"i\"", + "parameter using \"enumerated\" property do not accept \"i\" or \"S\"", + severity::error +},{ + issue::rdefault_missing, + "rDefault missing", + "parameters are missing default values", + severity::warning, +},{ + issue::rdefault_multiple, + "multiple rDefault", + "parameters have multiple mappings for rDefault", + severity::error, +},{ + issue::rpreset_multiple, + "multiple rPreset", + "parameters have multiple rPreset with the same number", + severity::error, +},{ + issue::rdefault_without_rparameter, + "rDefault without rparameter", + "ports have rDefault, but not rParameter", + severity::hint, +},{ + issue::invalid_default_format, + "invalid default format", + "parameters have default values which could not be parsed", + severity::error +},{ + issue::bundle_size_not_matching_rdefault, + "bundle size not matching rDefault", + "bundle parameters have rDefault array with different size", + severity::error +},{ + issue::rdefault_not_infinite, + "rDefault not infinite", + "parameters should use ellipsis instead of fixed sizes", + severity::hint +},{ + issue::default_cannot_canonicalize, + "cannot canoncicalize defaults", + "parameters have default values with non-existing enumeration values", + severity::error +},{ + issue::duplicate_mapping, + "duplicate mapping", + "ports have the same mapping twice (rOptions()?)", + severity::error +},{ + issue::enabled_port_not_replied, + "enabled-port did not reply", + "ports have enabled-ports which do not reply", + severity::error +},{ + issue::enabled_port_bad_reply, + "bad reply from enabled-port", + "ports have enabled-ports which did not reply \"T\" or \"F\"", + severity::error +} +}; + +static void liblo_error_cb(int i, const char *m, const char *loc) +{ + std::cerr << "liblo :-( " << i << "-" << m << "@" << loc << std::endl; +} + +int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data) +{ + static_cast(data)->on_recv(path, types, + argv, argc, msg); + return 0; +} + +void port_checker::server::on_recv(const char *path, const char *types, + lo_arg **argv, int argc, lo_message msg) +{ + (void)argv; +// std::cout << "on_recv: " << path << ", " << waiting << std::endl; +// for(const char** exp_path = exp_paths; *exp_path; +// ++exp_path, ++_replied_path) +// std::cout << " - exp: " << *exp_path << std::endl; + if(waiting && exp_paths && exp_paths[0]) + { + _replied_path = 0; + for(const char** exp_path = exp_paths; *exp_path; + ++exp_path, ++_replied_path) + if(!strcmp(*exp_path, path)) + { + size_t len = lo_message_length(msg, path); + *last_buffer = std::vector(len); + size_t written; + lo_message_serialise(msg, path, last_buffer->data(), &written); + if(written > last_buffer->size()) // ouch... + throw std::runtime_error("can not happen, " + "lo_message_length has been used"); + + last_args->resize(argc); + for(int i = 0; i < argc; ++i) + { + (*last_args)[i].val = rtosc_argument(last_buffer->data(), i); + (*last_args)[i].type = types[i]; + } + + waiting = false; + break; + } + } +} + +void port_checker::server::init(const char* target_url) +{ + target = lo_address_new_from_url(target_url); + if(!target || !lo_address_get_url(target) || + !lo_address_get_port(target) || + lo_address_get_protocol(target) != LO_UDP) + throw std::runtime_error("invalid address"); + + srv = lo_server_new_with_proto(nullptr, LO_UDP, liblo_error_cb); + if(srv == nullptr) + throw std::runtime_error("Could not create lo server"); + lo_server_add_method(srv, nullptr, nullptr, handle_st, this); + + rtosc_arg_val_t hi[2]; + hi[0].type = hi[1].type = 's'; + hi[0].val.s = hi[1].val.s = ""; + send_msg("/path-search", 2, hi); + std::vector reply; + std::vector buf; + wait_for_reply(&buf, &reply, "/paths"); +} + +bool port_checker::send_msg(const char* address, + size_t nargs, const rtosc_arg_val_t* args) +{ + return sender.send_msg(address, nargs, args); +} + +bool port_checker::server::send_msg(const char* address, + size_t nargs, const rtosc_arg_val_t* args) +{ + char buffer[2048]; + int len = rtosc_avmessage(buffer, sizeof(buffer), address, nargs, args); + int res = 0; + lo_message msg = lo_message_deserialise(buffer, len, &res); + if(msg == nullptr) { + std::cout << "liblo error code: " << res << std::endl; + throw std::runtime_error("could not deserialize message"); + } + // std::cout << "send message: " << address << ", args:" << std::endl; + // lo_message_pp(msg); + + res = lo_send_message_from(target, srv, buffer, msg); + if(res == -1) + throw std::runtime_error("Could not send message"); + return true; +} + +bool port_checker::server::_wait_for_reply(std::vector* buffer, + std::vector * args, + int unused) +{ + (void)unused; + last_args = args; + last_buffer = buffer; + + // allow up to 1000 ports = 1s. never wait more than 0.05 seconds + const int timeout_initial = 50; + int tries_left = 1000, timeout = timeout_initial; + while(tries_left-->1 && timeout-->1 && waiting) + { + int n = lo_server_recv_noblock(srv, 1 /* 0,001 seconds */); + if(n) + timeout = timeout_initial; + // message will be dispatched to the server's callback + } + waiting = true; // prepare for next round + + return tries_left && timeout; +} + +// loc: path in which that port is, must not end on '/' +// port: only for printing issues +bool port_checker::port_is_enabled(const char* loc, const char* port, + const char* metadata) +{ + Port::MetaContainer meta(metadata); + const char* enable_port_rel = meta["enabled by"]; + bool rval = true; + if(enable_port_rel) + { + std::string enable_port = "/"; + enable_port += loc; + enable_port += enable_port_rel; + + const char* collapsed_loc = Ports::collapsePath(&enable_port[0]); + send_msg(collapsed_loc, 0, nullptr); + + std::vector args; + std::vector strbuf; + if(sender.wait_for_reply(&strbuf, &args, collapsed_loc)) { + if(args.size() == 1 && + (args[0].type == 'T' || args[0].type == 'F')) { + rval = (args[0].type == 'T'); + } + else + m_issues.emplace(issue::enabled_port_bad_reply, + "/" + std::string(loc) + port); + } else + m_issues.emplace(issue::enabled_port_not_replied, + "/" + std::string(loc) + port); + } + return rval; +} + +void alternate_arg_val(rtosc_arg_val_t& a) +{ + switch(a.type) + { + case 'h': a.val.h = a.val.h ? 0 : 1; break; + case 't': a.val.t = a.val.t + 1; break; + case 'd': a.val.d = a.val.d ? 0.0 : 1.0; break; + case 'c': a.val.i = a.val.i ? 0 : 'x'; break; + case 'i': + case 'r': a.val.i = a.val.i ? 0 : 1; break; + case 'm': a.val.m[3] = a.val.m[3] ? 0 : 1; break; + case 'S': + case 's': a.val.s = a.val.s ? "" : "non-empty"; break; + case 'b': assert(a.val.b.len); + a.val.b.data[0] = a.val.b.data[0] ? 0 : 1; break; + case 'f': a.val.f = a.val.f ? 0.0f : 1.0f; break; + case 'T': a.val.T = 0; a.type = 'F'; break; + case 'F': a.val.T = 1; a.type = 'T'; break; + } +} + +void port_checker::check_port(const char* loc, const char* portname, + const char* metadata, int meta_len, + bool check_defaults) +{ + const char* port_args = strchr(portname, ':'); + if(!port_args) + port_args = portname + strlen(portname); + std::string full_path = "/"; full_path += loc; full_path += portname; + std::string::size_type arg_pos = full_path.find(':'); + if(arg_pos != std::string::npos) + { + full_path.resize(arg_pos); + + std::string::size_type hash_pos = full_path.find('#'); + int bundle_size = 0; + if(hash_pos != std::string::npos) + { + bundle_size = atoi(full_path.data() + hash_pos + 1); + // .../port#16... -> .../port0 + full_path[hash_pos] = '0'; + full_path.resize(hash_pos+1); + } + + rtosc::Port::MetaContainer meta(metadata); + if(*metadata && meta_len) + { + bool is_parameter = false, is_enumerated = false; + int n_default_vals = 0; + + std::map presets; + std::map mappings; // for rOptions() + std::map mapping_values; + std::vector default_values; + + for(const auto x : meta) + { + if(!strcmp(x.title, "parameter")) + is_parameter = true; + if(!strcmp(x.title, "enumerated")) + is_enumerated = true; + if(!strcmp(x.title, "no defaults")) + check_defaults = false; + else if(!strcmp(x.title, "default")) { + // x.value; + ++n_default_vals; + default_values.push_back(x.value); + } + else if(!strncmp(x.title, "default ", strlen("default ")) && + strcmp(x.title + strlen("default "), "depends")) { + ++presets[x.title + strlen("default ")]; + default_values.push_back(x.value); + } + else if(!strncmp(x.title, "map ", 4)) { + ++mappings[atoi(x.title + 4)]; + ++mapping_values[x.value]; + } + } + + auto raise = [this, loc, portname](issue issue_type) { + m_issues.emplace(issue_type, "/" + std::string(loc) + portname); + }; + + for(auto pr : mapping_values) + if(pr.second > 1) { + raise(issue::duplicate_mapping); + break; + } + + for(std::string pretty : default_values) + { + int nargs = rtosc_count_printed_arg_vals(pretty.c_str()); + if(nargs <= 0) + raise(issue::invalid_default_format); + else + { + char pretty_strbuf[8096]; + rtosc_arg_val_t avs[nargs]; + rtosc_scan_arg_vals(pretty.c_str(), avs, nargs, + pretty_strbuf, sizeof(pretty_strbuf)); + + { + int errs_found = canonicalize_arg_vals(avs, + nargs, + port_args, + meta); + if(errs_found) + raise(issue::default_cannot_canonicalize); + else + { + if(avs[0].type == 'a') + { + // How many elements are in the array? + int arrsize = 0; + int cur = 0; + int incsize = 0; + bool infinite = false; + for(rtosc_arg_val_t* ptr = avs + 1; + ptr - (avs+1) < avs[0].val.a.len; + ptr += incsize, arrsize += cur) + { + switch(ptr->type) + { + case '-': + cur = ptr->val.r.num; + incsize = 2 + ptr->val.r.has_delta; + if(!cur) infinite = true; + break; + case 'a': + cur = 1; + incsize = ptr->val.a.len; + break; + default: + cur = 1; + incsize = 1; + break; + } + } + if(!infinite) { + raise(issue::rdefault_not_infinite); + if(arrsize != bundle_size) + raise(issue::bundle_size_not_matching_rdefault); + } + } + } + } + } + } + + if(is_parameter) + { + // check metadata + if(check_defaults && !n_default_vals && presets.empty()) + raise(issue::rdefault_missing); + else { + if(n_default_vals > 1) + raise(issue::rdefault_multiple); + + for(auto pr : presets) + if(pr.second > 1) { + raise(issue::rpreset_multiple); + break; + } + } + + if(is_enumerated && + (!strstr(portname, ":i") || !strstr(portname, ":S"))) { + raise(issue::option_port_not_si); + } + + // send and reply... + // first, get some useful values + send_msg(full_path.c_str(), 0, nullptr); + std::vector args1; + std::vector strbuf1; + int res = sender.wait_for_reply(&strbuf1, &args1, + full_path.c_str()); + + if(res) + { + // alternate the values... + for(rtosc_arg_val_t& a : args1) + alternate_arg_val(a); + // ... and send them back + send_msg(full_path.c_str(), args1.size(), args1.data()); + args1.clear(); + strbuf1.clear(); + res = sender.wait_for_reply(&strbuf1, &args1, + full_path.c_str(), + "/undo_change"); + + if(!res) + raise(issue::parameter_not_replied); + else { + if(sender.replied_path() == 1 /* i.e. undo_change */) { + // some apps may reply with undo_change, some may + // already catch those... if we get one: retry + res = sender.wait_for_reply(&strbuf1, &args1, + full_path.c_str()); + } + + if(res) + { + res = other.wait_for_reply(&strbuf1, &args1, + full_path.c_str()); + if(!res) + raise(issue::parameter_not_broadcast); + } + else raise(issue::parameter_not_replied); + } + } + else { + raise(issue::parameter_not_queryable); + } + } + else { + if(default_values.size()) + raise(issue::rdefault_without_rparameter); + } + } + } +} + +void port_checker::print_evaluation() const +{ + auto sev_str = [](severity s) -> const char* { + return s == severity::error ? "**ERROR**" + : s == severity::warning ? "**WARNING**" + : "**HINT**"; + }; + std::cout << "# Port evaluation" << std::endl; + + issue last_issue = issue::number; + for(const std::pair& p : m_issues) + { + if(last_issue != p.first) + { + std::cout << std::endl; + const issue_t& it = m_issue_types.at(p.first); + std::cout << sev_str(it.sev) << ":" << std::endl + << "The following " << it.msg << ":" << std::endl; + last_issue = p.first; + } + std::cout << "* " << p.second << std::endl; + } + std::cout << std::endl; +} + +std::set port_checker::issues_not_covered() const +{ + std::set not_covered; + for(const auto& t : m_issue_types) + not_covered.insert(t.second.issue_id); + for(const auto& p : m_issues) + not_covered.erase(p.first); + return not_covered; +} + +bool port_checker::coverage() const +{ + return issues_not_covered().empty(); +} + +void port_checker::print_coverage(bool coverage_report) const +{ + if(coverage_report) { + std::cout << "# Issue Coverage" << std::endl << std::endl; + std::cout << "Checking for issues not covered..." << std::endl; + } + else { + std::cout << "# Issues that did not occur" << std::endl << std::endl; + } + std::set not_covered; + for(const auto& t : m_issue_types) + not_covered.insert(t.second.issue_id); + for(const auto& p : m_issues) + not_covered.erase(p.first); + if(not_covered.empty()) + std::cout << (coverage_report ? "Tests found for all issue types" + : "None") + << std::endl << std::endl; + else + { + std::cout << (coverage_report ? "The following issue types have not " + "been tested" + : "No ports are affected by") + << std::endl; + for(const issue& i : not_covered) + std::cout << "* " << m_issue_types.at(i).shortmsg << std::endl; + std::cout << std::endl; + } +} + +void port_checker::print_not_affected() const +{ + print_coverage(false); +} + +const std::set &port_checker::skipped() const +{ + return m_skipped; +} + +void port_checker::print_skipped() const +{ + std::cout << "# Skipped ports" << std::endl << std::endl; + std::cout << "The following ports have been skipped" << std::endl; + std::cout << "This usually means they have been disabled" << std::endl; + for(const std::string& s : skipped()) + std::cout << "* " << s << std::endl; + std::cout << std::endl; +} + +void port_checker::print_statistics() const +{ + double time = finish_time - start_time; + + std::cout << "# Statistics" << std::endl << std::endl; + std::cout << "Ports:" << std::endl; + std::cout << "* checked (and not disabled): " << ports_checked << std::endl; + std::cout << "* disabled: " << m_skipped.size() << std::endl + << std::endl; + + std::cout << "Time (incl. IO wait):" << std::endl; + std::cout << "* total: " << time << "s" << std::endl; + std::cout << std::setprecision(3) + << "* per port (checked or disabled): " + << (time / (ports_checked + m_skipped.size()))*1000.0 << "ms" + << std::endl + << std::endl; +} + +const std::map &port_checker::issue_types() const { + return m_issue_types; } + +const std::multimap &port_checker::issues() const { + return m_issues; } + +int port_checker::errors_found() const +{ + int errors = 0; + for(const std::pair& p : m_issues) + errors += (m_issue_types.at(p.first).sev == severity::error); + return errors; +} + +void port_checker::m_sanity_checks(std::vector& issue_type_missing) const +{ + for(int i = 0; i < (int)issue::number; ++i) + { + bool found = false; + for(const auto& it : _m_issue_types_arr) + if((int)it.issue_id == i) + found = true; + if(!found) + issue_type_missing.push_back(i); + } +} + +bool port_checker::sanity_checks() const +{ + std::vector issue_type_missing; + m_sanity_checks(issue_type_missing); + return issue_type_missing.empty(); +} + +bool port_checker::print_sanity_checks() const +{ + std::cout << "# Sanity checks" << std::endl << std::endl; + std::cout << "Checking port-checker itself for issues..." << std::endl; + std::vector issue_type_missing; + m_sanity_checks(issue_type_missing); + if(issue_type_missing.size()) + { + std::cout << "* Issue types missing description:" << std::endl; + for(int i : issue_type_missing) + std::cout << " - Enumerated issue with number " << i << std::endl; + } + else + std::cout << "* All issue types have descriptions" << std::endl; + + std::cout << std::endl; + return issue_type_missing.empty(); +} + +void port_checker::do_checks(char* loc, int loc_size, bool check_defaults) +{ + char* old_loc = loc + strlen(loc); +// std::cout << "Checking Ports: \"" << loc << "\"..." << std::endl; + + + rtosc_arg_val_t query_args[2]; + query_args[0].type = query_args[1].type = 's'; + query_args[0].val.s = loc; + query_args[1].val.s = ""; + send_msg("/path-search", 2, query_args); + std::vector args; + std::vector strbuf; + + int res = sender.wait_for_reply(&strbuf, &args, "/paths"); + if(!res) + throw std::runtime_error("no reply from path-search"); + if(args.size() % 2) + throw std::runtime_error("bad reply from path-search"); + + bool self_disabled = false; // disabled by an rSelf() ? + size_t port_max = args.size() >> 1; + // std::cout << "found " << port_max << " subports" << std::endl; + for(size_t port_no = 0; port_no < port_max; ++port_no) + { + if(args[port_no << 1].type != 's' || + args[(port_no << 1) + 1].type != 'b') + throw std::runtime_error("Invalid \"paths\" reply: bad types"); + + if(!strncmp(args[port_no << 1].val.s, "self:", 5)) { + rtosc_blob_t& blob = args[(port_no << 1) + 1].val.b; + const char* metadata = (const char*)blob.data; + if(!port_is_enabled(loc, args[port_no << 1].val.s, metadata)) + self_disabled = true; + } + } + if(self_disabled) + m_skipped.insert("/" + std::string(loc)); + else + ++ports_checked; + + std::map port_count; + + if(!self_disabled) + for(size_t port_no = 0; port_no < port_max; ++port_no) + { + const char* portname = args[port_no << 1].val.s; + int portlen = strlen(portname); + rtosc_blob_t& blob = args[(port_no << 1) + 1].val.b; + const char* metadata = (const char*)blob.data; + int32_t meta_len = blob.len; + if(!metadata) + metadata = ""; + bool has_subports = portname[portlen-1] == '/'; +/* std::cout << "port /" << loc << portname + << " (" << port_no << "/" << port_max + << "), has subports: " << std::boolalpha << has_subports + << std::endl;*/ + + if(port_is_enabled(loc, portname, metadata)) + { + Port::MetaContainer meta(metadata); + + if(meta.find("parameter") != meta.end()) + { + std::string portname_noargs = portname; + portname_noargs.resize(portname_noargs.find(':')); + ++port_count[portname_noargs]; + } + + if(has_subports) + { + // statistics: port may still be disabled, see above + if(loc_size > portlen) + { + strcpy(old_loc, portname); + char* hashsign = strchr(old_loc, '#'); + if(hashsign) + { + // #16\0 => 0\0 + *hashsign = '0'; + *++hashsign = '/'; + *++hashsign = 0; + } + + bool next_check_defaults = + (meta.find("no defaults") == meta.end()); + + do_checks(loc, loc_size - portlen, next_check_defaults); + } + else + throw std::runtime_error("portname too long"); + } + else { + ++ports_checked; + check_port(loc, portname, metadata, meta_len, check_defaults); + } + } + else + m_skipped.insert("/" + std::string(loc) + portname); + *old_loc = 0; + } + + for(const auto& pr : port_count) + { + if(pr.second > 1) + m_issues.emplace(issue::duplicate_parameter, + "/" + std::string(loc) + pr.first); + } +} + +bool port_checker::operator()(const char* url) +{ + for(const issue_t& it : _m_issue_types_arr) + m_issue_types.emplace(it.issue_id, it); + + start_time = time(NULL); + + unsigned i = 0; + for(; i < strlen(url); ++i) + if(!isdigit(url[i])) + break; + + if(i == strlen(url)) + { + sendtourl = "osc.udp://127.0.0.1:"; + sendtourl += url; + sendtourl += "/"; + } + else + sendtourl = url; + + sender.init(sendtourl.c_str()); + other.init(sendtourl.c_str()); + + char loc_buffer[4096] = { 0 }; + do_checks(loc_buffer, sizeof(loc_buffer)); + + finish_time = time(NULL); + return true; +} + +} + diff -Nru zynaddsubfx-3.0.3/rtosc/test/port-checker.h zynaddsubfx-3.0.4/rtosc/test/port-checker.h --- zynaddsubfx-3.0.3/rtosc/test/port-checker.h 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/port-checker.h 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2017-2018 Johannes Lorenz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * @file port-checker.h + * Header providing the port_checker class + * + * @test port-checker.c + */ + +#include +#include +#include +#include +#include +#include + +#ifndef RTOSC_PORT_CHECKER_H +#define RTOSC_PORT_CHECKER_H + +namespace rtosc { + +enum class issue +{ + // general: + duplicate_parameter, + // callbacks: + parameter_not_queryable, + parameter_not_replied, + parameter_not_broadcast, + // port metadata: + option_port_not_si, + duplicate_mapping, + enabled_port_not_replied, + enabled_port_bad_reply, + // default values: + rdefault_missing, + rdefault_multiple, + rpreset_multiple, + rdefault_without_rparameter, + invalid_default_format, + bundle_size_not_matching_rdefault, + rdefault_not_infinite, + default_cannot_canonicalize, + + number +}; + +enum class severity +{ + error, //!< Wrong and must be fixed + warning, //!< Wrong and could be fixed + hint //!< Maybe wrong +}; + +struct issue_t +{ + issue issue_id; + const char* shortmsg; + const char* msg; + severity sev; +}; + +//! Class to check all ports of a remote (UDP) controlled app +class port_checker +{ + // statistical data + time_t start_time, finish_time; + unsigned ports_checked = 0; //!< ports checked and not disabled + + friend int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data); + + //! issue types + std::map m_issue_types; + //! issues found: type and port each + std::multimap m_issues; + //! ports that have been skipped, usually because the have been disabled + std::set m_skipped; + + //! URL of the app + std::string sendtourl; + + //! One instance of server connected to the app + //! We will have two servers (one for send/reply and one + //! for catching broadcasts) + class server + { + friend int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data); + + volatile bool waiting = true; + + lo_server srv; + lo_address target; + + constexpr static int max_exp_paths = 15; + const char *exp_paths[max_exp_paths+1]; + + // variables from the last expected reply + int _replied_path; + std::vector* last_buffer; + std::vector* last_args; + + bool _wait_for_reply(std::vector *buffer, + std::vector*args, int unused); + + template + bool _wait_for_reply(std::vector *buffer, + std::vector* args, + int n, const char* path0, Args ...more_paths) { + exp_paths[n] = path0; + exp_paths[n+1] = nullptr; + return _wait_for_reply(buffer, args, 1+n, more_paths...); + } + + void on_recv(const char *path, const char *types, + lo_arg **argv, int argc, lo_message msg); + + public: + //! Return which of the expected paths from wait_for_reply() has + //! has been received + int replied_path() const { return _replied_path; } + void init(const char *target_url); + bool send_msg(const char *address, + size_t nargs, const rtosc_arg_val_t *args); + + //! Wait for a reply matching any of the C strings from + //! "exp_paths...". Any other received messages are discarded. + //! @param buffer Pointer to vector where the address will be + //! written + //! @param args Pointer to vector where args will be put + template + bool wait_for_reply(std::vector* buffer, + std::vector* args, + Args ...exp_paths) { + return _wait_for_reply(buffer, args, 0, exp_paths...); + } + }; + + server sender; //!< send and check replies + server other; //!< check broadcasts + + //! send a message via the sender + bool send_msg(const char *address, + size_t nargs, const rtosc_arg_val_t *args); + /** + * Execute all checks recursively under a given OSC path + * @param loc the OSC root path for the recursive checks + * @param loc_size size of the buffer located at @p loc + * @param check_defaults Whether default values should be checked in this + * port and all subports + */ + void do_checks(char *loc, int loc_size, bool check_defaults = true); + //! Check a port which has no subports + void check_port(const char *loc, const char *portname, + const char *metadata, int meta_len, bool check_defaults); + bool port_is_enabled(const char *loc, const char *port, + const char *metadata); + void m_sanity_checks(std::vector &issue_type_missing) const; + std::set issues_not_covered() const; + +public: + //! Let the port checker connect to url and find issues for all ports + //! @param url URL in format osc.udp://xxx.xxx.xxx.xxx:ppppp/, or just + //! ppppp (which means osc.udp://127.0.0.1:ppppp/) + bool operator()(const char* url); + + //! Return issue types + const std::map& issue_types() const; + //! Return issues found: type and port each + const std::multimap& issues() const; + //! Print a listing of all ports with issues in Markdown format + void print_evaluation() const; + //! Return the number of serious issues found + int errors_found() const; + + //! Check if the port checker itself has issues + bool sanity_checks() const; + //! Print if the port checker itself has issues + bool print_sanity_checks() const; + + //! Return if port checker has at least one port failing for each issue type + bool coverage() const; + //! Print if port checker has at least one port failing for each issue type + //! @note Only used for testing the port checker itself inside rtosc! + void print_coverage(bool coverage_report = true) const; + //! Print a list of error types that were OK for all checked ports + void print_not_affected() const; + + //! Return ports that have been skipped, + //! usually because the have been disabled + const std::set &skipped() const; + //! Print ports that have been skipped, + //! usually because the have been disabled + void print_skipped() const; + + //! Print statistics like number of ports and time consumed + void print_statistics() const; +}; + +} + +#endif // RTOSC_PORT_CHECKER diff -Nru zynaddsubfx-3.0.3/rtosc/test/port-checker-main.cpp zynaddsubfx-3.0.4/rtosc/test/port-checker-main.cpp --- zynaddsubfx-3.0.3/rtosc/test/port-checker-main.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/port-checker-main.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "port-checker.h" + +void usage(const char* progname) +{ + std::cout << "usage: " << progname << " " << std::endl; + std::cout << "Tests ports of the app running on , if the app" + << std::endl + << "has a \"path-search\" port conforming to rtosc::path_search()" + << std::endl + << "The URL must be in format osc.udp://xxx.xxx.xxx.xxx:ppppp/," + << std::endl + << "or just ppppp (which means osc.udp://127.0.0.1:ppppp/)" + << std::endl; +} + +int main(int argc, char** argv) +{ + if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + usage(*argv); + return EXIT_SUCCESS; + } + + int rval = EXIT_SUCCESS; + + try { + rtosc::port_checker checker; + checker(argv[1]); + + if(!checker.print_sanity_checks()) + rval = EXIT_FAILURE; + checker.print_evaluation(); + if(checker.errors_found()) + rval = EXIT_FAILURE; + checker.print_not_affected(); + checker.print_skipped(); + checker.print_statistics(); + } + catch(const std::exception& e) { + std::cout << "**Error caught**: " << e.what() << std::endl; + rval = EXIT_FAILURE; + } + + std::cout << "# Port checker test summary" << std::endl << std::endl; + std::cout << (rval == EXIT_SUCCESS ? "**SUCCESS!**" : "**FAILURE!**") + << std::endl; + std::cout << std::endl; + + return rval; +} + diff -Nru zynaddsubfx-3.0.3/rtosc/test/port-checker-testapp.cpp zynaddsubfx-3.0.4/rtosc/test/port-checker-testapp.cpp --- zynaddsubfx-3.0.3/rtosc/test/port-checker-testapp.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/port-checker-testapp.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,336 @@ +#include +#include +#include + +#include +#include +#include + +//! @file port-checker-testapp.cpp +//! Application that is used when port-checker-tester checks the port-checker +//! Many similarities to MiddleWare, but we don't have an RT thread + +static void liblo_error_cb(int i, const char *m, const char *loc) +{ + std::cerr << "liblo :-( " << i << "-" << m << "@" << loc << std::endl; +} + +static int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data); + +class port_checker_tester +{ + class pc_data_obj : public rtosc::RtData + { + port_checker_tester* pc; + int buffersize = 4*4096; + public: + pc_data_obj(port_checker_tester* pc) : pc(pc) + { + loc_size = 1024; + loc = new char[loc_size]; + memset(loc, 0, loc_size); + buffer = new char[buffersize]; + memset(buffer, 0, buffersize); + obj = pc; + forwarded = false; + } + + ~pc_data_obj(void) + { + delete[] loc; + delete[] buffer; + } + + virtual void reply(const char *path, const char *args, ...) override + { + //std::cout << "reply building" << path << std::endl; + va_list va; + va_start(va,args); + rtosc_vmessage(buffer,4*4096,path,args,va); + reply(buffer); + va_end(va); + } + virtual void replyArray(const char *path, const char *args, + rtosc_arg_t *argd) override + { + //std::cout << "reply building" << path << std::endl; + rtosc_amessage(buffer,4*4096,path,args,argd); + reply(buffer); + } + + //! reply to the sender + virtual void reply(const char *msg) override{ + pc->send_to_current_remote(msg, buffersize); + } + + //! broadcast to all senders + virtual void broadcast(const char *msg) override { + //std::cout << "broadcast building" << msg << std::endl; + pc->broadcast_to_remote(msg, buffersize); + } + + virtual void chain(const char *msg) override + { + assert(false); + } + + virtual void chain(const char *path, const char *args, ...) override + { + assert(false); + } + + virtual void forward(const char *) override + { + assert(false); + } + + bool forwarded; + private: + char *buffer; + pc_data_obj *mwi; + }; + + lo_server srv; + char recv_buffer[2048]; + std::string last_url; + std::set known_remotes; + + friend int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data); + void on_recv(const char* path, const char*, lo_arg**, int, lo_message msg); + void on_recv(const char* msg); + void send_to_remote(const char *rtmsg, int msgsize, const char* dest); + void send_to_current_remote(const char* rtmsg, int msgsize); + void broadcast_to_remote(const char* rtmsg, int msgsize); + bool alive = true; + +public: + + int rdefault_without_rparameter = 0; + int roption_without_ics = 0; + int double_rdefault = 0; + int double_rpreset = 0; + int rpreset_not_in_roptions = 0; + int perfect_param_1 = 0; + int duplicate_mapping = 0; + int no_rdefault = 0; + int bundle_size[16]; + int perfect_param_2[16]; + int perfect_param_3 = 0; + int invalid_rdefault; + int duplicate_param; + + void add_url(const char* url); + void run(); + + port_checker_tester(const char *preferred_port); +}; + +#define rObject port_checker_tester +static const rtosc::Ports test_ports = { + {"echo:ss", 0, 0, [](const char* msg, rtosc::RtData& d) { + const char* type = rtosc_argument(msg, 0).s; + const char* url = rtosc_argument(msg, 1).s; + d.reply("/echo", "ss", type, url); + }}, + {"duplicate_mapping::i:S", rOptions(same, same), NULL, + rParamICb(duplicate_mapping)}, + {"rdefault_without_rparameter_A::i", rDefault(0), NULL, + rParamICb(rdefault_without_rparameter)}, + {"invalid_rdefault::i", rProp(parameter) rDefault($$$), NULL, + rParamICb(invalid_rdefault)}, + {"rdefault_without_rparameter_B::i", rPresets(0,1,2), NULL, + rParamICb(rdefault_without_rparameter)}, + {"no_query::i", rProp(parameter) rDefault(0), nullptr, + [](const char* msg, rtosc::RtData& d) {} }, + {"no_reply_A::i", rProp(parameter) rDefault(0), NULL, + [](const char* msg, rtosc::RtData& d) { + const char *args = rtosc_argument_string(msg); + if(!*args) { d.reply(d.loc, "i", 0); } + } + }, + {"no_reply_B::i", rProp(parameter) rDefault(0), NULL, + [](const char* msg, rtosc::RtData& d) { + const char *args = rtosc_argument_string(msg); + if(!*args) { + d.reply(d.loc, "i", 0); + } else { + int var = rtosc_argument(msg, 0).i; + d.reply("/undo_change", "s" "i" "i", d.loc, + (var==0) ? 1 : 0, var); + // reply is missing here + } + } + }, + {"no_broadcast::i", rProp(parameter) rDefault(0), NULL, + [](const char* msg, rtosc::RtData& d) { + const char *args = rtosc_argument_string(msg); + if(!*args) { + d.reply(d.loc, "i", 0); + } else { + int var = rtosc_argument(msg, 0).i; + d.reply("/undo_change", "s" "i" "i", d.loc, + (var==0) ? 1 : 0, var); + d.reply(d.loc, "i", var); // this should be d.broadcast... + } + } + }, + {"roption_without_ics::i:c", rProp(enumerated) rProp(parameter) rDefault(0), + nullptr, rOptionCb(roption_without_ics) + }, + rParamI(no_rdefault, ""), + rParamI(double_rdefault, rDefault(0) rDefault(0), ""), + rParamI(double_rpreset, rPreset(0, 1), rPreset(1, 0), rPreset(0, 1), ""), + rArrayI(bundle_size, 16, rDefault([15x2]), ""), + rOption(rpreset_not_in_roptions, rOptions(one, two, three), + rPresetsAt(2, one, does_not_exist, one), rDefault(two), ""), + + rParamI(duplicate_param, rNoDefaults, ""), + rParamI(duplicate_param, rNoDefaults, ""), + + rOption(perfect_param_1, rOptions(one, two, three), + rPresetsAt(2, one, three, one), rDefault(two), ""), + rArrayI(perfect_param_2, 16, rDefault([1 2 3...]), ""), + rParamI(perfect_param_3, rNoDefaults, ""), // no rDefault, but that's OK here + // correct enabled-condition + rEnabledCondition(not_enabled, false), + // invalid enabled-condition: returns integer + {"not_enabled_bad:", rProp(internal), NULL, + [](const char* , rtosc::RtData& d){d.reply(d.loc, "i", 42); }}, + // bad parameter, but does not occur since it is disabled: + {"invisible_param::i", rEnabledByCondition(not_enabled), NULL, + [](const char* , rtosc::RtData& ){}}, + {"enabled_port_not_existing::i", rEnabledByCondition(not_existing), NULL, + [](const char* , rtosc::RtData& ){}}, + {"enabled_port_bad_reply::i", rEnabledByCondition(not_enabled_bad), NULL, + [](const char* , rtosc::RtData& ){}} +}; +#undef rObject + +int handle_st(const char *path, const char *types, lo_arg **argv, + int argc, lo_message msg, void *data) +{ + static_cast(data)->on_recv(path, types, + argv, argc, msg); + return 0; +} + +int main(int argc, char** argv) +{ + const char* preferred_port = (argc >= 2) ? argv[1] : nullptr; + port_checker_tester tester(preferred_port); + tester.run(); + + return EXIT_SUCCESS; +} + + +void port_checker_tester::on_recv(const char *path, const char *, + lo_arg **, int, lo_message msg) +{ + lo_address addr = lo_message_get_source(msg); + if(addr) { + const char *tmp = lo_address_get_url(addr); + if(tmp != last_url) { + add_url(last_url.c_str()); + //mw->transmitMsg("/echo", "ss", "OSC_URL", tmp); + last_url = tmp; + } + free((void*)tmp); + } + + // TODO: here and in MiddleWare.cpp: memset() required for liblo? + memset(recv_buffer, 0, sizeof(recv_buffer)); + lo_message_serialise(msg, path, recv_buffer, nullptr); + on_recv(recv_buffer); +} + +void port_checker_tester::on_recv(const char *msg) { + if(!strcmp(msg, "/path-search") && + !strcmp("ss", rtosc_argument_string(msg))) + { + + char buffer[1024*20]; + std::size_t length = + rtosc::path_search(test_ports, msg, 128, buffer, sizeof(buffer)); + if(length) { + lo_message msg = lo_message_deserialise((void*)buffer, + length, NULL); + lo_address addr = lo_address_new_from_url(last_url.c_str()); + if(addr) + lo_send_message(addr, buffer, msg); + lo_address_free(addr); + lo_message_free(msg); + } + } + else if(msg[0]=='/' && strrchr(msg, '/')[1]) { + pc_data_obj d(this); + test_ports.dispatch(msg, d, true); + //std::cout << "matches: " << d.matches << std::endl; + } +} + +void port_checker_tester::send_to_remote(const char *rtmsg, int msgsize, + const char *dest) +{ + //std::cout << "send_to_remote: " << rtmsg << " -> " << dest << std::endl; + if(!rtmsg || rtmsg[0] != '/' || !rtosc_message_length(rtmsg, -1)) { + std::cout << "[Warning] Invalid message in sendToRemote <" << rtmsg + << ">..." << std::endl; + return; + } + + if(*dest) { + size_t len = rtosc_message_length(rtmsg, msgsize); + lo_message msg = lo_message_deserialise((void*)rtmsg, len, nullptr); + if(!msg) { + std::cout << "[ERROR] OSC to <" << rtmsg + << "> Failed To Parse In Liblo" << std::endl; + return; + } + + //Send to known url + lo_address addr = lo_address_new_from_url(dest); + if(addr) + lo_send_message(addr, rtmsg, msg); + lo_address_free(addr); + lo_message_free(msg); + } +} + +void port_checker_tester::send_to_current_remote(const char *rtmsg, + int msgsize) { + //std::cout << "sending to last url: " << last_url << std::endl; + send_to_remote(rtmsg, msgsize, last_url.c_str()); +} + +void port_checker_tester::broadcast_to_remote(const char *rtmsg, int msgsize) { + for(auto rem:known_remotes) + send_to_remote(rtmsg, msgsize, rem.c_str()); +} + +void port_checker_tester::run() +{ + while(alive) + { + int n = lo_server_recv_noblock(srv, 1000 /* 1000 ms = 1 second */); + (void)n; + // message will be dispatched to the server's callback + } +} + +void port_checker_tester::add_url(const char *url) { + known_remotes.emplace(url); +} + +port_checker_tester::port_checker_tester(const char* preferred_port) +{ + srv = lo_server_new_with_proto(preferred_port, LO_UDP, liblo_error_cb); + if(srv == nullptr) + throw std::runtime_error("Could not create lo server"); + lo_server_add_method(srv, NULL, NULL, handle_st, this); + std::cerr << "lo server running on " << lo_server_get_port(srv) + << std::endl; +} + diff -Nru zynaddsubfx-3.0.3/rtosc/test/port-checker-tester.cpp zynaddsubfx-3.0.4/rtosc/test/port-checker-tester.cpp --- zynaddsubfx-3.0.3/rtosc/test/port-checker-tester.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/port-checker-tester.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,104 @@ +#include +#include +#include + +#include "common.h" +#include "port-checker.h" + +//! @file port-checker-tester.cpp +//! This tester tests port checker +//! It's being connected to port-checker-testapp + +using issue = rtosc::issue; + +std::multimap get_exp() +{ + // test expectations + // note: if you add new ports, please try to only let each new port + // occur in at most one category + + std::multimap exp; + + exp.emplace(issue::duplicate_parameter, "/duplicate_param"); + + exp.emplace(issue::parameter_not_queryable, "/no_query::i"); + exp.emplace(issue::parameter_not_replied, "/no_reply_A::i"); + exp.emplace(issue::parameter_not_replied, "/no_reply_B::i"); + exp.emplace(issue::parameter_not_broadcast, "/no_broadcast::i"); + + exp.emplace(issue::option_port_not_si, "/roption_without_ics::i:c"); + exp.emplace(issue::duplicate_mapping, "/duplicate_mapping::i:S"); + exp.emplace(issue::enabled_port_not_replied, + "/enabled_port_not_existing::i"); + exp.emplace(issue::enabled_port_bad_reply, "/enabled_port_bad_reply::i"); + + exp.emplace(issue::rdefault_missing, "/no_rdefault::i"); + exp.emplace(issue::rdefault_multiple, "/double_rdefault::i"); + exp.emplace(issue::rpreset_multiple, "/double_rpreset::i"); + exp.emplace(issue::rdefault_without_rparameter, + "/rdefault_without_rparameter_A::i"); + exp.emplace(issue::rdefault_without_rparameter, + "/rdefault_without_rparameter_B::i"); + exp.emplace(issue::invalid_default_format, "/invalid_rdefault::i"); + exp.emplace(issue::bundle_size_not_matching_rdefault, + "/bundle_size#16::i"); + exp.emplace(issue::rdefault_not_infinite, "/bundle_size#16::i"); + exp.emplace(issue::default_cannot_canonicalize, + "/rpreset_not_in_roptions::i:c:S"); + + return exp; +} + + +void usage(const char* progname) +{ + std::cout << "usage: " << progname << " " << std::endl; + std::cout << "Starts a port checker and tests it" + << std::endl + << "Must be used together with port-checker-testapp" + << std::endl + << "Don't start directly, use test-port-checker.rb" + << std::endl; +} + +int main(int argc, char** argv) +{ + if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + usage(*argv); + return EXIT_SUCCESS; + } + + bool exceptions_thrown = false; + + try { + rtosc::port_checker checker; + checker(argv[1]); + + assert_true(checker.sanity_checks(), "Port checker sanity", __LINE__); + // we keep it clean, but if you ever need help: + // checker.print_evaluation(); + assert_true(checker.coverage(), "All issues have test ports", __LINE__); + + std::multimap exp = get_exp(); // see above + const std::multimap& res = checker.issues(); + + assert_int_eq(exp.size(), res.size(), + "Expected number of issues is correct", __LINE__); + assert_true(exp == res, + "Issues are as expected", __LINE__); + + std::set exp_skipped; + exp_skipped.insert("/invisible_param::i"); + assert_true(exp_skipped == checker.skipped(), "Skipped port are as" + "expected", __LINE__); + } + catch(const std::exception& e) { + // std::cout << "**Error caught**: " << e.what() << std::endl; + exceptions_thrown = true; + } + + assert_true(!exceptions_thrown, "No exceptions thrown", __LINE__); + + return test_summary(); +} + diff -Nru zynaddsubfx-3.0.3/rtosc/test/pretty-format.c zynaddsubfx-3.0.4/rtosc/test/pretty-format.c --- zynaddsubfx-3.0.3/rtosc/test/pretty-format.c 2017-11-19 17:29:34.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/pretty-format.c 2018-09-09 00:40:57.000000000 +0000 @@ -3,6 +3,8 @@ #include #include "common.h" +rtosc_arg_val_t scanned[32]; + /** * @brief check_alt like check, but specify an alternative expectation * @see check() @@ -20,32 +22,36 @@ char tc_full[tc_len]; // descr. for full testcase name int strbuflen = 256; char strbuf[strbuflen]; + memset(strbuf, 0x7f, strbuflen); /* init with rubbish */ int num = rtosc_count_printed_arg_vals(arg_val_str); - assert(num > 0); - rtosc_arg_val_t scanned[num]; + assert(num < 32); + // note: when using this, the next step is usually + // rtosc_arg_val_t scanned[num]; + // in this case, however, we need the variable globally accessible size_t rd = rtosc_scan_arg_vals(arg_val_str, scanned, num, strbuf, strbuflen); strcpy(tc_full, "scan \""); - strncat(tc_full, tc_base, tc_len); + strncat(tc_full, tc_base, tc_len-7); strncat(tc_full, "\" (read exactly the input string)", tc_len - strlen(tc_full)); assert_int_eq(strlen(arg_val_str), rd, tc_full, line); size_t len = 128; char printed[len]; + memset(printed, 0x7f, len); /* init with rubbish */ size_t written = rtosc_print_arg_vals(scanned, num, printed, len, opt, 0); const char* exp_print = _exp_print ? _exp_print : arg_val_str; strcpy(tc_full, "print \""); - strncat(tc_full, tc_base, tc_len); + strncat(tc_full, tc_base, tc_len-8); strncat(tc_full, "\" (value = value before scan)", tc_len - strlen(tc_full)); assert_str_eq(exp_print, printed, tc_full, line); strcpy(tc_full, "print \""); - strncat(tc_full, tc_base, tc_len); + strncat(tc_full, tc_base, tc_len-8); strncat(tc_full, "\" (return value check)", tc_len - strlen(tc_full)); assert_int_eq(strlen(exp_print), written, tc_full, line); } @@ -280,8 +286,10 @@ check_alt("3 ... 4", &uncompressed, "a very short range, uncompressed", __LINE__, "3 4"); check("3 ... 4", &simplefloats, "a very short range, compressed", __LINE__); - check("2.00 1.40 ... -0.40 -1.00", &simplefloats, - "a simple float range", __LINE__); + check_alt("2.00 1.40 ... -0.40 -1.00", &simplefloats, + "a simple float range", __LINE__, + "2.00 1.40 0.80 ... -0.40 -1.00"); // TODO: bug: input != output + // fix in later commit? check_alt("'z' 'x' ... 'r'", &uncompressed, "a simple downward char range", __LINE__, "'z' 'x'\n" @@ -301,10 +309,10 @@ " 15 17\n" " 19"); check_alt("[ 1 ... 3 ]", &uncompressed, - "range with delta 1 in an array (1)", __LINE__, + "range with delta 1 in an array", __LINE__, "[1 2 3]"); check_alt("[3...0]", &uncompressed, - "range with delta 1 in an array (2)", __LINE__, + "range with delta -1 in an array", __LINE__, "[3 2 1 0]"); /* @@ -315,6 +323,11 @@ __LINE__, "[0.420\n 0.420]"); check_alt("2x[0 1]", &uncompressed, "range of arrays", __LINE__, "[0 1] [0\n 1]"); + check("[127 104 64 106 7x64 101 64 64 64 92 112x64]", NULL, + "long array", __LINE__); + check_alt("[127 104 64 106 64 64 64 64 64 64 64 101 64 64 64 92 112x64]", NULL, + "long array", __LINE__, "[127 104 64 106 7x64 101 64 64 64 92 112x64]"); + /* combined */ @@ -332,17 +345,96 @@ #endif /* - endless ranges + infinite ranges */ - check("[1 ...]", NULL, "delta-less infinite range (1)", __LINE__); - check("[\"Next Effect\"S ...]", NULL, + check("[1 ... ]", NULL, "delta-less infinite range (1)", __LINE__); + check("[\"Next Effect\"S ... ]", NULL, "delta-less infinite range (2)", __LINE__); check_alt("[false...]", NULL, "delta-less infinite range (3)", __LINE__, - "[false ...]"); - check("[1 0 0 ...]", NULL, "delta-less infinite range (4)", __LINE__); - check("[0 1 ...]", NULL, "infinite range with delta", __LINE__); - check("[true false false ...]", NULL, + "[false ... ]"); + check("[1 0 0 ... ]", NULL, "delta-less infinite range (4)", __LINE__); + check("[0 1 ... ]", NULL, "infinite range with delta", __LINE__); + check("[true false false ... ]", NULL, "endless range after \"true false false\"", __LINE__); + check("[[0 1] ... ]", NULL, "range of arrays", __LINE__); + check("[true false ... ]", NULL, "alternating true-false-range", __LINE__); + assert('-' == scanned[2].type); + assert_int_eq(0, scanned[2].val.r.num, + "true-false range is inifinite", __LINE__); + assert_int_eq(1, scanned[2].val.r.has_delta, + "true-false range has delta", __LINE__); +} + +// most tests here are reverse to those in ranges() +void scan_ranges() +{ + /* + no range conversion + */ + rtosc_print_options uncompressed = ((rtosc_print_options) { false, 3, " ", + 80, false }); + rtosc_print_options simplefloats = ((rtosc_print_options) { false, 2, " ", + 80, true }); + check("0 1 2 3", NULL, "too less args for range conversion", __LINE__); + check("0.00 1.00 2.00 3.00 4.00", &simplefloats, + "wrong type for range conversion", __LINE__); + check("0 1 2 3 4", &uncompressed, + "no range conversion due to opts", __LINE__); + + /* + with delta + */ + check_alt("1 2 3 4 5 6 7", NULL, + "convert to simple upwards integer range", __LINE__, + "1 ... 7"); + check_alt("-3 -4 -5 -6 -7", NULL, + "convert to simple downwards integer range", __LINE__, + "-3 ... -7"); + check_alt("'z' 'x' 'v' 't' 'r'", NULL, + "convert to simple downward char range", __LINE__, + "'z' 'x' ... 'r'"); + check_alt("[4 3 2 1 0]", NULL, + "convert to downward range in an array", __LINE__, + "[4 ... 0]"); + check_alt("1 3 5 7 9 13 15 17 19 21", NULL, + "convert to two almost subsequent ranges", __LINE__, + "1 3 ... 9 13 15 ... 21"); + check_alt("[ 1 2 3 4 5 ]", NULL, + "convert to range with delta 1 in an array", __LINE__, + "[1 ... 5]"); + check_alt("[5 4 3 2 1 0]", NULL, + "convert to range with delta -1 in an array", __LINE__, + "[5 ... 0]"); + + /* + without delta + */ + check_alt("0 0 0 0 0", NULL, + "convert to range of 5 zeros", __LINE__, "5x0"); + check_alt("[ 0h 0h 0h 0h 0h ]", NULL, + "convert to array with range of 5 huge ints", __LINE__, "[5x0h]"); + check_alt("[0 1] [0 1] [0 1] [0 1] [0 1]", NULL, + "convert to range of arrays", __LINE__, "5x[0 1]"); + /* + combined + */ + check_alt("0.5 0.5 0.5 0.5 0.5 'a' 'b' 'c' 'd' 'e'", NULL, + "convert to combined ranges", __LINE__, + "5x0.50 (0x1p-1) 'a' ... 'e'"); + /* combined, but not yet implemented: */ +#if 0 + check_alt("-6 -6 -6 -6 -6 -6 -5 -4 ... 0", NULL, + "convert to combined ranges (2)", __LINE__, + "6x-6 -5 ... 0"); + check_alt("'a' 'b' 'c' 'd' 'e' 'f' 'f' 'f' 'f' 'f' 'f'", NULL, + "convert to combined ranges (3)", __LINE__, + "'a' ... 'f' 6x'f'"); +#endif + + /* + endless ranges + */ + // (you cannot convert fixed size ranges to endless ranges) } void fail_at_arg(const char* arg_val_str, int exp_fail, int line) @@ -353,21 +445,24 @@ int num = rtosc_count_printed_arg_vals(arg_val_str); strcpy(tc_full, "find 1st invalid arg in \""); - strncat(tc_full, arg_val_str, tc_len); + strncat(tc_full, arg_val_str, tc_len-25); strncat(tc_full, "\"", tc_len); assert_int_eq(exp_fail, -num, tc_full, line); } void messages() { + int num = rtosc_count_printed_arg_vals_of_msg("not beginning with a '/'"); assert_int_eq(-1, num, "return -1 if the message does not start" "with a slash", __LINE__); int strbuflen = 256; char strbuf[strbuflen]; + memset(strbuf, 0x7f, strbuflen); /* init with rubbish */ int msgbuflen = 256; char msgbuf[msgbuflen]; + memset(msgbuf, 0x7f, msgbuflen); /* init with rubbish */ const char* input = "%this is a savefile\n" "/noteOn 0 0 0 % a noteOn message"; @@ -383,6 +478,7 @@ size_t len = 128; char printed[len]; + memset(printed, 0x7f, len); /* init with rubbish */ rtosc_print_options shortline = ((rtosc_print_options) { true, 3, " ", 7, true }); size_t written = rtosc_print_message("/noteOn", scanned, num, @@ -558,6 +654,7 @@ scan_and_print_single(); scan_and_print_mulitple(); arrays(); + scan_ranges(); ranges(); messages(); diff -Nru zynaddsubfx-3.0.3/rtosc/test/test-port-checker.rb zynaddsubfx-3.0.4/rtosc/test/test-port-checker.rb --- zynaddsubfx-3.0.3/rtosc/test/test-port-checker.rb 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/test-port-checker.rb 2019-03-03 15:19:10.000000000 +0000 @@ -0,0 +1,16 @@ +#!/usr/bin/ruby + +require 'open3' + +# start zyn, grep the lo server port, and connect the port checker to it +Open3.popen3(Dir.pwd + "/port-checker-testapp") do |stdin, stdout, stderr, wait_thr| + pid = wait_thr[:pid] + while line=stderr.gets do + # print "line: " + line; + if /^lo server running on (\d+)$/.match(line) then + rval = system(Dir.pwd + "/port-checker-tester 'osc.udp://localhost:" + $1 + "/'") + Process.kill("KILL", pid) + exit rval + end + end +end diff -Nru zynaddsubfx-3.0.3/rtosc/test/test-walker.cpp zynaddsubfx-3.0.4/rtosc/test/test-walker.cpp --- zynaddsubfx-3.0.3/rtosc/test/test-walker.cpp 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/test-walker.cpp 2018-03-11 15:05:07.000000000 +0000 @@ -1,5 +1,6 @@ #include #include +#include #include #include using namespace rtosc; diff -Nru zynaddsubfx-3.0.3/rtosc/test/typed-template-test.cpp zynaddsubfx-3.0.4/rtosc/test/typed-template-test.cpp --- zynaddsubfx-3.0.3/rtosc/test/typed-template-test.cpp 2015-10-23 14:47:50.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/typed-template-test.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -76,5 +76,5 @@ assert_true(m5, "Check Type Match", __LINE__); assert_false(m6, "Check Type Conflict", __LINE__); - return global_err ? EXIT_FAILURE : EXIT_SUCCESS; + return test_summary(); }; diff -Nru zynaddsubfx-3.0.3/rtosc/test/undo-test.cpp zynaddsubfx-3.0.4/rtosc/test/undo-test.cpp --- zynaddsubfx-3.0.3/rtosc/test/undo-test.cpp 2017-09-07 18:44:16.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/undo-test.cpp 2019-03-03 15:19:10.000000000 +0000 @@ -46,7 +46,8 @@ } void reply(const char *path, const char *args, ...) { - if(strcmp(path, "undo_change") || !enable) + //printf("# reply <%s, %s>\n", path, args); + if(strcmp(path, "/undo_change") || !enable) return; va_list va; @@ -99,7 +100,7 @@ assert_int_eq(7, o.b, "Verify Redo Has Returned To Altered State", __LINE__); - return 0; + return test_summary(); } diff -Nru zynaddsubfx-3.0.3/rtosc/test/util.c zynaddsubfx-3.0.4/rtosc/test/util.c --- zynaddsubfx-3.0.3/rtosc/test/util.c 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/rtosc/test/util.c 2018-09-09 00:40:57.000000000 +0000 @@ -0,0 +1,34 @@ +#include "../src/util.h" +#include "common.h" + +void test_fast_strcpy() +{ + char* src = "rtosc is a good library"; + char dest[32]; + memset(dest, 0, sizeof(dest)); + dest[strlen(src)+1] = 'x'; + + fast_strcpy(dest, src, 32); + assert_str_eq(src, dest, + "fast_strcpy() copies at most bytes", __LINE__); + + fast_strcpy(dest, src, 32); + assert_char_eq('x', dest[strlen(src)+1], + "fast_strcpy() does not pad the dest with zeros", __LINE__); + + fast_strcpy(dest, src, 6); + assert_str_eq("rtosc", dest, + "fast_strcpy() copies at most bytes", __LINE__); +} + +/* + all tests +*/ +int main() +{ + test_fast_strcpy(); + + return test_summary(); +} + + diff -Nru zynaddsubfx-3.0.3/src/CMakeLists.txt zynaddsubfx-3.0.4/src/CMakeLists.txt --- zynaddsubfx-3.0.3/src/CMakeLists.txt 2017-10-31 01:40:45.000000000 +0000 +++ zynaddsubfx-3.0.4/src/CMakeLists.txt 2019-02-23 15:43:37.000000000 +0000 @@ -96,6 +96,8 @@ SET (PluginLibDir "lib" CACHE STRING "Install directory for plugin libraries PREFIX/PLUGIN_LIB_DIR/{lv2,vst}") SET (DemoMode FALSE CACHE BOOL "Enable 10 minute silence") +SET (ZynFusionDir "" CACHE STRING "Developers only: zest binary's dir; useful if fusion is not system-instealled.") +mark_as_advanced(FORCE ZynFusionDir) # Now, handle the incoming settings and set define flags/variables based # on this @@ -180,6 +182,8 @@ option (BuildForAMD_X86_64 "Build for AMD x86_64 system" OFF) option (BuildForCore2_X86_64 "Build for Intel Core2 x86_64 system" OFF) option (BuildForDebug "Include gdb debugging support" OFF) +option (IncludeWhatYouUse "Check for useless includes" OFF) +mark_as_advanced(IncludeWhatYouUse) set(CMAKE_BUILD_TYPE "Release") @@ -211,6 +215,19 @@ set (BuildOptionsDebug "-std=c++11 -O0 -g3 -ggdb -Wall -Wno-unused-parameter -Wpointer-arith" CACHE STRING "Debug build flags") +if(${CMAKE_VERSION} VERSION_LESS "3.3.0") + set(IwyuErr "disabled (cmake < 3.3.0)") +else() + if(IncludeWhatYouUse) + find_program(IwyuPath NAMES include-what-you-use iwyu) + if(NOT IwyuPath) + set(IwyuErr "package NOT found") + endif() + else() + set(IwyuErr "disabled (IncludeWhatYouUse=OFF)") + endif() +endif() + ########### Settings dependant code ########### # From here on, the setting variables have been prepared so concentrate # on the actual compiling. @@ -430,6 +447,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/zyn-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/zyn-version.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/zyn-config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/zyn-config.h) link_directories(${AUDIO_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${FFTW_LIBRARY_DIRS} ${MXML_LIBRARY_DIRS} ${FLTK_LIBRARY_DIRS} ${NTK_LIBRARY_DIRS}) @@ -477,6 +496,8 @@ wsock32 "-static" iphlpapi "-static" winpthread) +elseif(APPLE) + set(PTHREAD_LIBRARY pthread) else() set(PLATFORM_LIBRARIES rt) set(PTHREAD_LIBRARY pthread) @@ -490,6 +511,17 @@ ${PTHREAD_LIBRARY} rtosc rtosc-cpp) +if(IwyuErr) + message (STATUS "Include what you use: ${IwyuErr}") +else() + set(IwyuPathAndOptions + ${IwyuPath} + -Xiwyu + --no_comments) + set_property(TARGET zynaddsubfx_core PROPERTY CXX_INCLUDE_WHAT_YOU_USE + ${IwyuPathAndOptions}) +endif() + if(CompileTests) add_subdirectory(Tests) endif(CompileTests) diff -Nru zynaddsubfx-3.0.3/src/Containers/NotePool.cpp zynaddsubfx-3.0.4/src/Containers/NotePool.cpp --- zynaddsubfx-3.0.3/src/Containers/NotePool.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Containers/NotePool.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -94,7 +94,7 @@ //return either the first unused descriptor or the last valid descriptor which //matches note/sendto -static int getMergeableDescriptor(uint8_t note, uint8_t sendto, bool legato, +static int getMergeableDescriptor(note_t note, uint8_t sendto, bool legato, NotePool::NoteDescriptor *ndesc) { int desc_id = 0; @@ -151,7 +151,7 @@ return cnt; } -void NotePool::insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato) +void NotePool::insertNote(note_t note, uint8_t sendto, SynthDescriptor desc, bool legato) { //Get first free note descriptor int desc_id = getMergeableDescriptor(note, sendto, legato, ndesc); @@ -181,7 +181,7 @@ insertLegatoNote(d.note, d.sendto, s); } -void NotePool::insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc) +void NotePool::insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor desc) { assert(desc.note); try { @@ -193,10 +193,10 @@ }; //There should only be one pair of notes which are still playing -void NotePool::applyLegato(LegatoParams &par) +void NotePool::applyLegato(note_t note, LegatoParams &par) { for(auto &desc:activeDesc()) { - desc.note = par.midinote; + desc.note = note; for(auto &synth:activeNotes(desc)) try { synth.note->legatonote(par); @@ -206,7 +206,7 @@ } } -void NotePool::makeUnsustainable(uint8_t note) +void NotePool::makeUnsustainable(note_t note) { for(auto &desc:activeDesc()) { if(desc.note == note) { @@ -243,17 +243,17 @@ int NotePool::getRunningNotes(void) const { - bool running[256] = {0}; - for(auto &desc:activeDesc()) { - //printf("note!(%d)\n", desc.note); - if(desc.playing() || desc.sustained()) - running[desc.note] = true; - } - + bool running[256] = {}; int running_count = 0; - for(int i=0; i<256; ++i) - running_count += running[i]; + for(auto &desc:activeDesc()) { + if(desc.playing() == false && desc.sustained() == false) + continue; + if(running[desc.note] != false) + continue; + running[desc.note] = true; + running_count++; + } return running_count; } void NotePool::enforceKeyLimit(int limit) @@ -313,7 +313,7 @@ kill(d); } -void NotePool::killNote(uint8_t note) +void NotePool::killNote(note_t note) { for(auto &d:activeDesc()) { if(d.note == note) diff -Nru zynaddsubfx-3.0.3/src/Containers/NotePool.h zynaddsubfx-3.0.4/src/Containers/NotePool.h --- zynaddsubfx-3.0.3/src/Containers/NotePool.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Containers/NotePool.h 2019-02-23 15:43:37.000000000 +0000 @@ -19,18 +19,19 @@ namespace zyn { +typedef uint8_t note_t; //Global MIDI note definition + struct LegatoParams; class NotePool { public: - typedef uint8_t note_t; //Currently this wastes a ton of bits due ot the legatoMirror flag struct NoteDescriptor { //acceptable overlap after 2 minutes //run time at 48kHz 8 samples per buffer //19 bit minimum uint32_t age; - uint8_t note; + note_t note; uint8_t sendto; //max of 16 kit elms and 3 kit items per uint8_t size; @@ -117,13 +118,13 @@ NotePool(void); //Operations - void insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato=false); - void insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc); + void insertNote(note_t note, uint8_t sendto, SynthDescriptor desc, bool legato=false); + void insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor desc); void upgradeToLegato(void); - void applyLegato(LegatoParams &par); + void applyLegato(note_t note, LegatoParams &par); - void makeUnsustainable(uint8_t note); + void makeUnsustainable(note_t note); bool full(void) const; bool synthFull(int sdesc_count) const; diff -Nru zynaddsubfx-3.0.3/src/Containers/ScratchString.cpp zynaddsubfx-3.0.4/src/Containers/ScratchString.cpp --- zynaddsubfx-3.0.3/src/Containers/ScratchString.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Containers/ScratchString.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -1,4 +1,5 @@ #include "ScratchString.h" +#include "../Misc/Util.h" #include #include @@ -22,7 +23,7 @@ ScratchString::ScratchString(const char *str) { if(str) - strncpy(c_str, str, SCRATCH_SIZE); + fast_strcpy(c_str, str, SCRATCH_SIZE); else memset(c_str, 0, sizeof(c_str)); } @@ -30,7 +31,7 @@ ScratchString ScratchString::operator+(const ScratchString s) { ScratchString ss; - strncpy(ss.c_str, c_str, SCRATCH_SIZE); + fast_strcpy(ss.c_str, c_str, SCRATCH_SIZE); strncat(ss.c_str, s.c_str, SCRATCH_SIZE-strlen(c_str)); return ss; } diff -Nru zynaddsubfx-3.0.3/src/DSP/SVFilter.cpp zynaddsubfx-3.0.4/src/DSP/SVFilter.cpp --- zynaddsubfx-3.0.3/src/DSP/SVFilter.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/DSP/SVFilter.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -26,6 +26,12 @@ namespace zyn { +enum FilterInterpolationType { + INTERPOLATE_EXTREME = 0x01, + INTERPOLATE_NON_ZERO, + INTERPOLATE_NONE +}; + SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, unsigned char Fstages, unsigned int srate, int bufsize) :Filter(srate, bufsize), @@ -34,7 +40,7 @@ freq(Ffreq), q(Fq), gain(1.0f), - needsinterpolation(false), + needsinterpolation(INTERPOLATE_NONE), firsttime(true) { if(stages >= MAX_FILTER_STAGES) @@ -100,6 +106,8 @@ } } + + void SVFilter::computefiltercoefs(void) { par.f = freq / samplerate_f * 4.0f; @@ -127,8 +135,14 @@ //if the frequency is changed fast, it needs interpolation if((rap > 3.0f) || nyquistthresh) { //(now, filter and coefficients backup) if(!firsttime) - needsinterpolation = true; + needsinterpolation = INTERPOLATE_EXTREME; + ipar = par; + } else if(rap != 1.0) { + if (!firsttime) + needsinterpolation = INTERPOLATE_NON_ZERO; ipar = par; + } else { + needsinterpolation = INTERPOLATE_NONE; } freq = frequency; computefiltercoefs(); @@ -168,8 +182,7 @@ computefiltercoefs(); } -void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par) -{ +float *SVFilter::getfilteroutfortype(SVFilter::fstage &x) { float *out = NULL; switch(type) { case 0: @@ -188,11 +201,20 @@ out = &x.low; warnx("Impossible SVFilter type encountered [%d]", type); } + return out; +} +void SVFilter::singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2) +{ + float *out = getfilteroutfortype(x); for(int i = 0; i < buffersize; ++i) { - x.low = x.low + par.f * x.band; - x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band; - x.band = par.f * x.high + x.band; + float p = i / buffersize_f; + float f = par1.f + (par2.f - par1.f) * p; + float q = par1.q + (par2.q - par1.q) * p; + float q_sqrt = sqrtf(q); + x.low = x.low + f * x.band; + x.high = q_sqrt * smp[i] - x.low - q * x.band; + x.band = f * x.high + x.band; x.notch = x.high + x.low; smp[i] = *out; } @@ -209,23 +231,38 @@ // xl = pf*pfxh*z(-1)/(1-z(-1))^2 -void SVFilter::filterout(float *smp) + +void SVFilter::singlefilterout(float *smp, SVFilter::fstage &x, SVFilter::parameters &par) { - for(int i = 0; i < stages + 1; ++i) - singlefilterout(smp, st[i], par); + float *out = getfilteroutfortype(x); + for(int i = 0; i < buffersize; ++i) { + x.low = x.low + par.f * x.band; + x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band; + x.band = par.f * x.high + x.band; + x.notch = x.high + x.low; + smp[i] = *out; + } +} - if(needsinterpolation) { +void SVFilter::filterout(float *smp) +{ + if (needsinterpolation == INTERPOLATE_EXTREME) { float ismp[buffersize]; + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par); memcpy(ismp, smp, bufferbytes); - for(int i = 0; i < stages + 1; ++i) singlefilterout(ismp, st[i], ipar); - for(int i = 0; i < buffersize; ++i) { float x = i / buffersize_f; smp[i] = ismp[i] * (1.0f - x) + smp[i] * x; } - needsinterpolation = false; + } else if (needsinterpolation == INTERPOLATE_NON_ZERO) { + for(int i = 0; i < stages + 1; ++i) + singlefilterout_with_par_interpolation(smp, st[i], ipar, par); + } else { + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par); } for(int i = 0; i < buffersize; ++i) diff -Nru zynaddsubfx-3.0.3/src/DSP/SVFilter.h zynaddsubfx-3.0.4/src/DSP/SVFilter.h --- zynaddsubfx-3.0.3/src/DSP/SVFilter.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/DSP/SVFilter.h 2018-06-09 14:45:00.000000000 +0000 @@ -56,7 +56,9 @@ float f, q, q_sqrt; } par, ipar; + float *getfilteroutfortype(SVFilter::fstage &x); void singlefilterout(float *smp, fstage &x, parameters &par); + void singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2); void computefiltercoefs(void); int type; // The type of the filter (LPF1,HPF1,LPF2,HPF2...) int stages; // how many times the filter is applied (0->1,1->2,etc.) @@ -66,7 +68,8 @@ bool abovenq, //if the frequency is above the nyquist oldabovenq; - bool needsinterpolation, firsttime; + int needsinterpolation; + bool firsttime; }; } diff -Nru zynaddsubfx-3.0.3/src/Effects/Alienwah.cpp zynaddsubfx-3.0.4/src/Effects/Alienwah.cpp --- zynaddsubfx-3.0.3/src/Effects/Alienwah.cpp 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Alienwah.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -42,7 +42,7 @@ "Effect Frequency"), rEffPar(Pfreqrnd, 3, rShort("rand"), rPreset(1, 106) rDefault(0), "Frequency Randomness"), - rEffPar(PLFOtype, 4, rShort("shape"), + rEffParOpt(PLFOtype, 4, rShort("shape"), rOptions(sine, triangle), rPresets(sine, sine, triangle, triangle), "LFO Shape"), rEffPar(PStereo, 5, rShort("stereo"), rPresets(62, 101, 100, 66), diff -Nru zynaddsubfx-3.0.3/src/Effects/Chorus.cpp zynaddsubfx-3.0.4/src/Effects/Chorus.cpp --- zynaddsubfx-3.0.3/src/Effects/Chorus.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Chorus.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -47,7 +47,7 @@ rEffPar(Pfreqrnd, 3, rShort("rand"), rPreset(4, 117) rPreset(6, 34) rPreset(7, 34) rPreset(9, 105) rDefault(0), "Frequency Randomness"), - rEffPar(PLFOtype, 4, rShort("shape"), + rEffParOpt(PLFOtype, 4, rShort("shape"), rOptions(sine, tri), rPresets(sine, sine, tri, sine, sine, sine, tri, tri, tri, sine), "LFO Shape"), diff -Nru zynaddsubfx-3.0.3/src/Effects/Distorsion.cpp zynaddsubfx-3.0.4/src/Effects/Distorsion.cpp --- zynaddsubfx-3.0.3/src/Effects/Distorsion.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Distorsion.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -45,7 +45,7 @@ "Input amplification"), rEffPar(Plevel, 4, rShort("output"), rPresets(70, 75, 80, 62, 75, 75), "Output amplification"), - rEffPar(Ptype, 5, rShort("type"), + rEffParOpt(Ptype, 5, rShort("type"), rOptions(Arctangent, Asymmetric, Pow, Sine, Quantisize, Zigzag, Limiter, Upper Limiter, Lower Limiter, Inverse Limiter, Clip, Asym2, Pow2, sigmoid), diff -Nru zynaddsubfx-3.0.3/src/Effects/DynamicFilter.cpp zynaddsubfx-3.0.4/src/Effects/DynamicFilter.cpp --- zynaddsubfx-3.0.3/src/Effects/DynamicFilter.cpp 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/DynamicFilter.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -42,8 +42,8 @@ "Effect Frequency"), rEffPar(Pfreqrnd, 3, rShort("rand"), rDefault(0), "Frequency Randomness"), - rEffPar(PLFOtype, 4, rShort("shape"), rOptions(sin, tri), rDefault(sin), - "LFO Shape"), + rEffParOpt(PLFOtype, 4, rShort("shape"), rOptions(sin, tri), + rDefault(sin), "LFO Shape"), rEffPar(PStereo, 5, rShort("stereo"), rPresets(64, 80, 50, 64, 96), "Stereo Mode"), rEffPar(Pdepth, 6, rShort("depth"), rPresets(0, 70, 80, 0, 64), diff -Nru zynaddsubfx-3.0.3/src/Effects/Effect.h zynaddsubfx-3.0.4/src/Effects/Effect.h --- zynaddsubfx-3.0.3/src/Effects/Effect.h 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Effect.h 2018-03-11 15:01:27.000000000 +0000 @@ -24,6 +24,12 @@ #define rEffPar(name, idx, ...) \ {STRINGIFY(name) "::i", rProp(parameter) rDefaultDepends(preset) \ DOC(__VA_ARGS__), NULL, rEffParCb(idx)} +#define rEffParOpt(name, idx, ...) \ + {STRINGIFY(name) "::i:c:S", rProp(parameter) rDefaultDepends(preset) \ + rProp(enumerated) DOC(__VA_ARGS__), NULL, \ + rBOIL_BEGIN \ + rCOptionCb_(obj->getpar(idx), obj->changepar(idx, var)) \ + rBOIL_END } #define rEffParTF(name, idx, ...) \ {STRINGIFY(name) "::T:F", rProp(parameter) rDefaultDepends(preset) \ DOC(__VA_ARGS__), NULL, rEffParTFCb(idx)} diff -Nru zynaddsubfx-3.0.3/src/Effects/EffectMgr.cpp zynaddsubfx-3.0.4/src/Effects/EffectMgr.cpp --- zynaddsubfx-3.0.3/src/Effects/EffectMgr.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/EffectMgr.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -107,7 +107,7 @@ d.broadcast(d.loc, "i", eff->getpreset()); //update parameters as well - strncpy(loc, d.loc, 1024); + fast_strcpy(loc, d.loc, sizeof(loc)); char *tail = strrchr(loc, '/'); if(!tail) return; diff -Nru zynaddsubfx-3.0.3/src/Effects/Phaser.cpp zynaddsubfx-3.0.4/src/Effects/Phaser.cpp --- zynaddsubfx-3.0.3/src/Effects/Phaser.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Phaser.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -62,7 +62,7 @@ rEffPar(lfo.Prandomness, 3, rShort("rnd."), rPreset(5, 100), rPreset(7, 5), rPresetsAt(9, 10, 10, 10), rDefault(0), "LFO randomness"), - rEffPar(lfo.PLFOtype, 4, rShort("type"), + rEffParOpt(lfo.PLFOtype, 4, rShort("type"), rPreset(4, tri), rPresetsAt(6, tri, tri), rPreset(11, tri), rDefault(sine), rOptions(sine, tri), "lfo shape"), diff -Nru zynaddsubfx-3.0.3/src/Effects/Reverb.cpp zynaddsubfx-3.0.4/src/Effects/Reverb.cpp --- zynaddsubfx-3.0.3/src/Effects/Reverb.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Effects/Reverb.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -60,7 +60,7 @@ rPresets(83, 71, 78, 78, 71, 106, 77, 71, 78, 64, 88, 77, 74) "Dampening"), //Todo make this a selector - rEffPar(Ptype, 10, rShort("type"), + rEffParOpt(Ptype, 10, rShort("type"), rOptions(Random, Freeverb, Bandwidth), rPresets(Freeverb, Random, Freeverb, Freeverb, Freeverb, Random, Freeverb, Random, Freeverb, Freeverb, Freeverb, Random, diff -Nru zynaddsubfx-3.0.3/src/main.cpp zynaddsubfx-3.0.4/src/main.cpp --- zynaddsubfx-3.0.3/src/main.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/main.cpp 2019-03-10 16:35:15.000000000 +0000 @@ -3,7 +3,7 @@ main.cpp - Main file of the synthesizer Copyright (C) 2002-2005 Nasca Octavian Paul - Copyright (C) 2012-2017 Mark McCurry + Copyright (C) 2012-2019 Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -28,6 +28,9 @@ #include +#include +#include + #include #include #include "Params/PADnoteParameters.h" @@ -37,6 +40,7 @@ #include "Misc/Master.h" #include "Misc/Part.h" #include "Misc/Util.h" +#include "zyn-config.h" #include "zyn-version.h" //Nio System @@ -99,9 +103,9 @@ /* * Program initialisation */ -void initprogram(SYNTH_T synth, Config* config, int prefered_port) +void initprogram(SYNTH_T synth, Config* config, int preferred_port) { - middleware = new MiddleWare(std::move(synth), config, prefered_port); + middleware = new MiddleWare(std::move(synth), config, preferred_port); master = middleware->spawnMaster(); master->swaplr = swaplr; @@ -224,7 +228,7 @@ << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" << endl; cerr - << " Copyright (c) 2009-2017 Mark McCurry [active maintainer]" + << " Copyright (c) 2009-2019 Mark McCurry [active maintainer]" << endl; cerr << "This program is free software (GNU GPL v2 or later) and \n"; cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; @@ -237,14 +241,19 @@ synth.oscilsize = config.cfg.OscilSize; swaplr = config.cfg.SwapStereo; - Nio::preferedSampleRate(synth.samplerate); + Nio::preferredSampleRate(synth.samplerate); synth.alias(); //build aliases sprng(time(NULL)); + // for option entrys with the 3rd member (flag) pointing here, + // getopt_long*() will return 0 and set this flag to the 4th member (val) + int getopt_flag; + /* Parse command-line options */ struct option opts[] = { + // options with single char equivalents { "load", 2, NULL, 'l' }, @@ -291,7 +300,7 @@ "pid-in-client-name", 0, NULL, 'p' }, { - "prefered-port", 1, NULL, 'P', + "preferred-port", 1, NULL, 'P', }, { "output", 1, NULL, 'O' @@ -308,15 +317,31 @@ { "dump-json-schema", 2, NULL, 'D' }, + // options without single char equivalents ("getopt_flag" compulsory) + { + "list-inputs", no_argument, &getopt_flag, 'i' + }, + { + "list-outputs", no_argument, &getopt_flag, 'o' + }, { 0, 0, 0, 0 } }; opterr = 0; - int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; - int prefered_port = -1; + int option_index = 0, opt; + enum class exit_with_t + { + dont_exit, + help, + version, + list_inputs, + list_outputs + }; + exit_with_t exit_with = exit_with_t::dont_exit; + int preferred_port = -1; int auto_save_interval = 0; -int wmidi = -1; + int wmidi = -1; string loadfile, loadinstrument, execAfterInit, loadmidilearn; @@ -342,10 +367,10 @@ switch(opt) { case 'h': - exitwithhelp = 1; + exit_with = exit_with_t::help; break; case 'v': - exitwithversion = 1; + exit_with = exit_with_t::version; break; case 'Y': /* this command a dummy command (has NO effect) and is used because I need for NSIS installer @@ -418,7 +443,7 @@ break; case 'P': if(optarguments) - prefered_port = atoi(optarguments); + preferred_port = atoi(optarguments); break; case 'A': if(optarguments) @@ -452,49 +477,80 @@ if(optarguments) wmidi = atoi(optarguments); break; + case 0: // catch options without single char equivalent + switch(getopt_flag) + { + case 'i': + exit_with = exit_with_t::list_inputs; + break; + case 'o': + exit_with = exit_with_t::list_outputs; + break; + } + break; case '?': cerr << "ERROR:Bad option or parameter.\n" << endl; - exitwithhelp = 1; + exit_with = exit_with_t::help; break; } } synth.alias(); - if(exitwithversion) { - cout << "Version: " << version << endl; - return 0; + switch (exit_with) + { + case exit_with_t::version: + cout << "Version: " << version << endl; + break; + case exit_with_t::help: + cout << "Usage: zynaddsubfx [OPTION]\n\n" + << " -h , --help \t\t\t\t Display command-line help and exit\n" + << " -v , --version \t\t\t Display version and exit\n" + << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" + << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" + << " -M file, --midi-learn=FILE\t\t Loads a .xlz file\n" + << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" + << + " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" + << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" + << " -S , --swap\t\t\t\t Swap Left <--> Right\n" + << + " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" + << " -N , --named\t\t\t\t Postfix IO Name when possible\n" + << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" + << " -A , --auto-save=INTERVAL\t\t Automatically save at interval\n" + << "\t\t\t\t\t (disabled with 0 interval)\n" + << " -p , --pid-in-client-name\t\t Append PID to (JACK) " + "client name\n" + << " -P , --preferred-port\t\t\t Preferred OSC Port\n" + << " -O , --output\t\t\t\t Set Output Engine\n" + << " -I , --input\t\t\t\t Set Input Engine\n" + << " -e , --exec-after-init\t\t Run post-initialization script\n" + << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n" + << " -D , --dump-json-schema=FILE\t\t Dump osc schema (.json) to file\n" + << endl; + break; + case exit_with_t::list_inputs: + case exit_with_t::list_outputs: + { + Nio::init(synth, config.cfg.oss_devs, nullptr); + auto get_func = (getopt_flag == 'i') + ? &Nio::getSources + : &Nio::getSinks; + std::set engines = (*get_func)(); + for(std::string engine : engines) + { + std::transform(engine.begin(), engine.end(), engine.begin(), + ::tolower); + cout << engine << endl; + } + break; + } + default: + break; } - if(exitwithhelp != 0) { - cout << "Usage: zynaddsubfx [OPTION]\n\n" - << " -h , --help \t\t\t\t Display command-line help and exit\n" - << " -v , --version \t\t\t Display version and exit\n" - << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" - << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" - << " -M file, --midi-learn=FILE\t\t Loads a .xlz file\n" - << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" - << - " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" - << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" - << " -S , --swap\t\t\t\t Swap Left <--> Right\n" - << - " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" - << " -N , --named\t\t\t\t Postfix IO Name when possible\n" - << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" - << " -A , --auto-save=INTERVAL\t\t Automatically save at interval\n" - << "\t\t\t\t\t (disabled with 0 interval)\n" - << " -p , --pid-in-client-name\t\t Append PID to (JACK) " - "client name\n" - << " -P , --preferred-port\t\t\t Preferred OSC Port\n" - << " -O , --output\t\t\t\t Set Output Engine\n" - << " -I , --input\t\t\t\t Set Input Engine\n" - << " -e , --exec-after-init\t\t Run post-initialization script\n" - << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n" - << " -D , --dump-json-schema=FILE\t\t Dump osc schema (.json) to file\n" - << endl; - + if(exit_with != exit_with_t::dont_exit) return 0; - } cerr.precision(1); cerr << std::fixed; @@ -503,7 +559,7 @@ cerr << "Internal latency = \t" << synth.dt() * 1000.0f << " ms" << endl; cerr << "ADsynth Oscil.Size = \t" << synth.oscilsize << " samples" << endl; - initprogram(std::move(synth), &config, prefered_port); + initprogram(std::move(synth), &config, preferred_port); bool altered_master = false; if(!loadfile.empty()) { @@ -516,7 +572,7 @@ exit(1); } else { - strncpy(master->last_xmz, filename, XMZ_PATH_MAX); + fast_strcpy(master->last_xmz, filename, XMZ_PATH_MAX); master->last_xmz[XMZ_PATH_MAX-1] = 0; master->applyparameters(); cout << "Master file loaded." << endl; @@ -655,37 +711,58 @@ #ifndef WIN32 gui_pid = fork(); if(gui_pid == 0) { - execlp("zyn-fusion", "zyn-fusion", addr, "--builtin", "--no-hotload", 0); - execlp("./zyn-fusion", "zyn-fusion", addr, "--builtin", "--no-hotload", 0); - + auto exec_fusion = [&addr](const char* path) { + execlp(path, "zyn-fusion", addr, "--builtin", "--no-hotload", 0); }; + if(fusion_dir && *fusion_dir) + { + std::string fusion = fusion_dir; + fusion += "/zest"; + if(access(fusion.c_str(), X_OK)) + fputs("Warning: CMake's ZynFusionDir does not contain a" + "\"zest\" binary - ignoring.", stderr); + else { + const char* cur = getenv("LD_LIBRARY_PATH"); + std::string ld_library_path; + if(cur) { + ld_library_path += cur; + ld_library_path += ":"; + } + ld_library_path += fusion_dir; + setenv("LD_LIBRARY_PATH", ld_library_path.c_str(), 1); + exec_fusion(fusion.c_str()); + } + } + exec_fusion("./zyn-fusion"); + exec_fusion("/opt/zyn-fusion/zyn-fusion"); + exec_fusion("zyn-fusion"); err(1,"Failed to launch Zyn-Fusion"); } #else STARTUPINFO si; -PROCESS_INFORMATION pi; -memset(&si, 0, sizeof(si)); -memset(&pi, 0, sizeof(pi)); -char *why_windows = strrchr(addr, ':'); -char *seriously_why = why_windows + 1; -char start_line[256] = {0}; -if(why_windows) - snprintf(start_line, sizeof(start_line), "zyn-fusion.exe osc.udp://127.0.0.1:%s", seriously_why); -else { - printf("COULD NOT PARSE <%s>\n", addr); - exit(1); -} -printf("[INFO] starting subprocess via <%s>\n", start_line); -if(!CreateProcess(NULL, start_line, -NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) { - printf("Failed to launch Zyn-Fusion...\n"); - exit(1); -} - + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + char *why_windows = strrchr(addr, ':'); + char *seriously_why = why_windows + 1; + char start_line[256] = {0}; + if(why_windows) + snprintf(start_line, sizeof(start_line), "zyn-fusion.exe osc.udp://127.0.0.1:%s", seriously_why); + else { + printf("COULD NOT PARSE <%s>\n", addr); + exit(1); + } + printf("[INFO] starting subprocess via <%s>\n", start_line); + if(!CreateProcess(NULL, start_line, + NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) { + printf("Failed to launch Zyn-Fusion...\n"); + exit(1); + } #endif } #endif printf("[INFO] Main Loop...\n"); + bool already_exited = false; while(Pexitprogram == 0) { #ifndef WIN32 #if USE_NSM @@ -729,13 +806,28 @@ if(!noui) { int status = 0; int ret = waitpid(gui_pid, &status, WNOHANG); - if(ret == gui_pid) + if(ret == gui_pid) { Pexitprogram = 1; + already_exited = true; + } } #endif #endif } - +#ifdef ZEST_GUI +#ifndef WIN32 + if(!already_exited) { + int ret = kill(gui_pid, SIGHUP); + if (ret == -1) { + err(1, "Failed to terminate Zyn-Fusion...\n"); + } + } +#else + (void)already_exited; +#endif +#else + (void)already_exited; +#endif exitprogram(config); return 0; } diff -Nru zynaddsubfx-3.0.3/src/Misc/Allocator.h zynaddsubfx-3.0.4/src/Misc/Allocator.h --- zynaddsubfx-3.0.3/src/Misc/Allocator.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Allocator.h 2018-04-07 03:39:29.000000000 +0000 @@ -61,7 +61,7 @@ T *valloc(size_t len, Ts&&... ts) { T *data = (T*)alloc_mem(len*sizeof(T)); - if(!data) { + if(!data && len != 0) { rollbackTransaction(); throw std::bad_alloc(); } diff -Nru zynaddsubfx-3.0.3/src/Misc/Bank.cpp zynaddsubfx-3.0.4/src/Misc/Bank.cpp --- zynaddsubfx-3.0.3/src/Misc/Bank.cpp 2017-09-07 18:32:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Bank.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -34,6 +34,18 @@ #include #endif +#ifdef __APPLE__ +#include + +static const char* my_dylib_path () { + static Dl_info info; + if (dladdr((const void*) &my_dylib_path, &info)) { + return info.dli_fname; + } + return NULL; +} +#endif + using namespace std; namespace zyn { @@ -377,6 +389,22 @@ } } #endif +#ifdef __APPLE__ + { + const char* path = my_dylib_path (); + if (path && strstr(path, "ZynAddSubFX.dylib") && strlen (path) < 1000) { + char tmp[1024]; + strcpy (tmp, path); + strstr (tmp, "ZynAddSubFX.dylib")[0] = 0; // LV2 + strcat (tmp, "banks"); + scanrootdir(tmp); + strcpy (tmp, path); + strstr (tmp, "ZynAddSubFX.dylib")[0] = 0; + strcat (tmp, "../Resources/banks"); // VST + scanrootdir(tmp); + } + } +#endif //sort the banks sort(banks.begin(), banks.end()); diff -Nru zynaddsubfx-3.0.3/src/Misc/BankDb.cpp zynaddsubfx-3.0.4/src/Misc/BankDb.cpp --- zynaddsubfx-3.0.3/src/Misc/BankDb.cpp 2017-09-07 18:32:32.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/BankDb.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -237,7 +237,11 @@ #ifndef WIN32 int ret = lstat(fname.c_str(), &st); if(ret != -1) +# ifdef __APPLE__ + time = st.st_mtimespec.tv_sec; +# else time = st.st_mtim.tv_sec; +# endif #else int ret = 0; time = rand(); diff -Nru zynaddsubfx-3.0.3/src/Misc/Config.cpp zynaddsubfx-3.0.4/src/Misc/Config.cpp --- zynaddsubfx-3.0.3/src/Misc/Config.cpp 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Config.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -30,7 +30,7 @@ if(!strcmp("", args)) {\ data.reply(loc, "s", obj->name); \ } else { \ - strncpy(obj->name, rtosc_argument(msg, 0).s, length); \ + fast_strcpy(obj->name, rtosc_argument(msg, 0).s, length); \ data.broadcast(loc, "s", obj->name);\ rChangeCb \ } rBOIL_END diff -Nru zynaddsubfx-3.0.3/src/Misc/Master.cpp zynaddsubfx-3.0.4/src/Misc/Master.cpp --- zynaddsubfx-3.0.3/src/Misc/Master.cpp 2017-12-05 01:00:37.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Master.cpp 2019-03-10 16:16:42.000000000 +0000 @@ -114,7 +114,9 @@ }} }; -#define rBegin [](const char *msg, RtData &d) { rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj +#define rBegin [](const char *msg, RtData &d) { \ + (void) msg; \ + rtosc::AutomationMgr &a = *(AutomationMgr*)d.obj #define rEnd } static int extract_num(const char *&msg) @@ -172,11 +174,17 @@ else d.reply(d.loc, a.slots[slot].automations[param].active ? "T" : "F"); rEnd}, - {"path::s", rProp(parameter) rProp(read-only) rDoc("Path of parameter"), 0, + {"path::s", rProp(parameter) rDoc("Path of parameter"), 0, rBegin; int slot = d.idx[1]; int param = d.idx[0]; - d.reply(d.loc, "s", a.slots[slot].automations[param].param_path); + if(!strcmp("s",rtosc_argument_string(msg))) { + a.setSlotSubPath(slot, param, rtosc_argument(msg, 0).s); + a.updateMapping(slot, param); + d.broadcast(d.loc, "s", a.slots[slot].automations[param].param_path); + } + else + d.reply(d.loc, "s", a.slots[slot].automations[param].param_path); rEnd}, {"clear:", rDoc("Clear automation param"), 0, rBegin; @@ -242,9 +250,10 @@ {"midi-cc::i", rProp(parameter) rMap(default, -1) rDoc("Access assigned midi CC slot") , 0, rBegin; int slot = d.idx[0]; - if(rtosc_narguments(msg)) + if(rtosc_narguments(msg)) { a.slots[slot].midi_cc = rtosc_argument(msg, 0).i; - else + d.broadcast(d.loc, "i", a.slots[slot].midi_cc); + } else d.reply(d.loc, "i", a.slots[slot].midi_cc); rEnd}, @@ -298,8 +307,7 @@ if(a.active_slot >= 0) a.createBinding(a.active_slot, rtosc_argument(msg, 0).s, true); rEnd}, - // TODO: remove rNoWalk - {"slot#16/", rNoWalk rDoc("Parameters of individual automation slots"), &slot_ports, + {"slot#16/", rDoc("Parameters of individual automation slots"), &slot_ports, rBegin; (void)a; d.push_index(get_next_int(msg)); @@ -409,8 +417,7 @@ d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]); m->part[i] = p; p->initialize_rt(); - for(int i=0; i<128; ++i) - m->activeNotes[i] = 0; + memset(m->activeNotes, 0, sizeof(m->activeNotes)); }}, {"active_keys:", rProp("Obtain a list of active notes"), 0, rBegin; @@ -428,7 +435,7 @@ ((Master*)d.obj)->setPvolume(limit(rtosc_argument(m,0).i,0,127)); d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}}, {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) - rDefault(80) rDoc("Master Volume"), 0, + rDoc("Master Volume"), 0, [](const char *m, rtosc::RtData &d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); @@ -513,7 +520,8 @@ [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}}, {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0, [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}}, - {"config/", rNoWalk rDoc("Top Level Application Configuration Parameters"), + {"config/", rNoDefaults + rDoc("Top Level Application Configuration Parameters"), &Config::ports, [](const char *, rtosc::RtData &d){d.forward();}}, {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN SNIP @@ -605,6 +613,7 @@ virtual void forward(const char *reason) override { assert(message); + (void) reason; reply("/forward", ""); printf("forwarding '%s'\n", message); forwarded = true; @@ -636,6 +645,7 @@ xml.beginbranch("slot", i); XmlNode params("params"); params["midi-cc"] = to_s(slot.midi_cc); + params["name"] = to_s(slot.name); xml.add(params); for(int j=0; jPrcvchn) { fakepeakpart[npart] = velocity * 2; if(part[npart]->Penabled) - part[npart]->NoteOn(note, velocity, keyshift); + part[npart]->NoteOn(note, velocity, keyshift, note_log2_freq); } } - activeNotes[(int)note] = 1; + activeNotes[note] = 1; + HDDRecorder.triggernow(); } else this->noteOff(chan, note); - HDDRecorder.triggernow(); } /* * Note Off Messages */ -void Master::noteOff(char chan, char note) +void Master::noteOff(char chan, note_t note) { for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) if((chan == part[npart]->Prcvchn) && part[npart]->Penabled) part[npart]->NoteOff(note); - activeNotes[(int)note] = 0; + activeNotes[note] = 0; } /* * Pressure Messages (velocity=0 for NoteOff) */ -void Master::polyphonicAftertouch(char chan, char note, char velocity) +void Master::polyphonicAftertouch(char chan, note_t note, char velocity) { if(velocity) { for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) @@ -1410,8 +1428,7 @@ insefx[nefx]->cleanup(); for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) sysefx[nefx]->cleanup(); - for(int i = 0; i < int(sizeof(activeNotes)/sizeof(activeNotes[0])); ++i) - activeNotes[i] = 0; + memset(activeNotes, 0, sizeof(activeNotes)); vuresetpeaks(); shutup = 0; } diff -Nru zynaddsubfx-3.0.3/src/Misc/Master.h zynaddsubfx-3.0.4/src/Misc/Master.h --- zynaddsubfx-3.0.3/src/Misc/Master.h 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Master.h 2019-02-23 15:43:37.000000000 +0000 @@ -17,7 +17,7 @@ #include "../globals.h" #include "Microtonal.h" #include -#include +#include #include "Time.h" #include "Bank.h" @@ -104,9 +104,12 @@ void putalldata(const char *data); //Midi IN - void noteOn(char chan, char note, char velocity); - void noteOff(char chan, char note); - void polyphonicAftertouch(char chan, char note, char velocity); + void noteOn(char chan, note_t note, char velocity) { + noteOn(chan, note, velocity, note / 12.0f); + }; + void noteOn(char chan, note_t note, char velocity, float note_log2_freq); + void noteOff(char chan, note_t note); + void polyphonicAftertouch(char chan, note_t note, char velocity); void setController(char chan, int type, int par); //void NRPN... diff -Nru zynaddsubfx-3.0.3/src/Misc/Microtonal.cpp zynaddsubfx-3.0.4/src/Misc/Microtonal.cpp --- zynaddsubfx-3.0.3/src/Misc/Microtonal.cpp 2017-09-07 18:36:59.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Microtonal.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -51,6 +51,7 @@ rParamZyn(PAnote, rShort("1/1 midi note"), rDefault(69), "The note for 'A'"), rParamF(PAfreq, rShort("ref freq"), rDefault(440.0f), + rLog(1.0,20000.0), "Frequency of the 'A' note"), rParamZyn(Pscaleshift, rShort("shift"), rDefault(64), "UNDOCUMENTED"), @@ -65,7 +66,7 @@ rParamZyn(Pmapsize, rDefault(12), "Size of key map"), rToggle(Pmappingenabled, rDefault(false), "Mapping Enable"), - rParams(Pmapping, 128, rDefaultMissing, "Mapping of keys"), + rParams(Pmapping, 128, rDefault([0 1 ...]), "Mapping of keys"), rParamZyn(Pglobalfinedetune, rShort("fine"), rDefault(64), "Fine detune for all notes"), @@ -256,8 +257,10 @@ /* * Get the frequency according the note number */ -float Microtonal::getnotefreq(int note, int keyshift) const +float Microtonal::getnotefreq(float note_log2_freq, int keyshift) const { + note_t note = roundf(12.0f * note_log2_freq); + // in this function will appears many times things like this: // var=(a+b*100)%b // I had written this way because if I use var=a%b gives unwanted results when a<0 @@ -271,9 +274,8 @@ powf(2.0f, (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents if(Penabled == 0) //12tET - return powf(2.0f, - (note - PAnote - + keyshift) / 12.0f) * PAfreq * globalfinedetunerap; + return powf(2.0f, note_log2_freq + + ((keyshift - PAnote) / 12.0f)) * PAfreq * globalfinedetunerap; int scaleshift = ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize; diff -Nru zynaddsubfx-3.0.3/src/Misc/Microtonal.h zynaddsubfx-3.0.4/src/Misc/Microtonal.h --- zynaddsubfx-3.0.3/src/Misc/Microtonal.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Microtonal.h 2019-02-23 15:43:37.000000000 +0000 @@ -17,6 +17,7 @@ #include #include #include "../globals.h" +#include "../Containers/NotePool.h" #define MAX_OCTAVE_SIZE 128 #define MICROTONAL_MAX_NAME_LEN 120 @@ -67,7 +68,7 @@ void defaults(); /**Calculates the frequency for a given note */ - float getnotefreq(int note, int keyshift) const; + float getnotefreq(float note_log2_freq, int keyshift) const; //Parameters diff -Nru zynaddsubfx-3.0.3/src/Misc/MiddleWare.cpp zynaddsubfx-3.0.4/src/Misc/MiddleWare.cpp --- zynaddsubfx-3.0.3/src/Misc/MiddleWare.cpp 2017-12-05 01:00:37.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/MiddleWare.cpp 2019-03-10 16:16:42.000000000 +0000 @@ -62,6 +62,26 @@ using std::string; int Pexitprogram = 0; +#ifdef __APPLE__ +#include +#include +#endif + +/* work around missing clock_gettime on OSX */ +static void monotonic_clock_gettime(struct timespec *ts) { +#ifdef __APPLE__ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_MONOTONIC, ts); +#endif +} + /****************************************************************************** * LIBLO And Reflection Code * * * @@ -74,64 +94,6 @@ fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc); } -void path_search(const char *m, const char *url) -{ - using rtosc::Ports; - using rtosc::Port; - - //assumed upper bound of 32 ports (may need to be resized) - char types[256+1]; - rtosc_arg_t args[256]; - size_t pos = 0; - const Ports *ports = NULL; - const char *str = rtosc_argument(m,0).s; - const char *needle = rtosc_argument(m,1).s; - - //zero out data - memset(types, 0, sizeof(types)); - memset(args, 0, sizeof(args)); - - if(!*str) { - ports = &Master::ports; - } else { - const Port *port = Master::ports.apropos(rtosc_argument(m,0).s); - if(port) - ports = port->ports; - } - - if(ports) { - //RTness not confirmed here - for(const Port &p:*ports) { - if(strstr(p.name, needle) != p.name || !p.name) - continue; - types[pos] = 's'; - args[pos++].s = p.name; - types[pos] = 'b'; - if(p.metadata && *p.metadata) { - args[pos].b.data = (unsigned char*) p.metadata; - auto tmp = rtosc::Port::MetaContainer(p.metadata); - args[pos++].b.len = tmp.length(); - } else { - args[pos].b.data = (unsigned char*) NULL; - args[pos++].b.len = 0; - } - } - } - - - //Reply to requester [wow, these messages are getting huge...] - char buffer[1024*20]; - size_t length = rtosc_amessage(buffer, sizeof(buffer), "/paths", types, args); - if(length) { - lo_message msg = lo_message_deserialise((void*)buffer, length, NULL); - lo_address addr = lo_address_new_from_url(url); - if(addr) - lo_send_message(addr, buffer, msg); - lo_address_free(addr); - lo_message_free(msg); - } -} - static int handler_function(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data) { @@ -153,9 +115,26 @@ memset(buffer, 0, sizeof(buffer)); size_t size = 2048; lo_message_serialise(msg, path, buffer, &size); - if(!strcmp(buffer, "/path-search") && !strcmp("ss", rtosc_argument_string(buffer))) { - path_search(buffer, mw->activeUrl().c_str()); - } else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) { + + if(!strcmp(buffer, "/path-search") && + !strcmp("ss", rtosc_argument_string(buffer))) + { + char reply_buffer[1024*20]; + std::size_t length = + rtosc::path_search(Master::ports, buffer, 128, + reply_buffer, sizeof(reply_buffer)); + if(length) { + lo_message msg = lo_message_deserialise((void*)reply_buffer, + length, NULL); + lo_address addr = lo_address_new_from_url(mw->activeUrl().c_str()); + if(addr) + lo_send_message(addr, reply_buffer, msg); + lo_address_free(addr); + lo_message_free(msg); + } + } + else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) + { mw->transmitMsg(rtosc::Ports::collapsePath(buffer)); } @@ -320,10 +299,15 @@ void handleOscil(const char *msg, rtosc::RtData &d) { string obj_rl(d.message, msg); void *osc = get(obj_rl); - assert(osc); - strcpy(d.loc, obj_rl.c_str()); - d.obj = osc; - OscilGen::non_realtime_ports.dispatch(msg, d); + if(osc) + { + strcpy(d.loc, obj_rl.c_str()); + d.obj = osc; + OscilGen::non_realtime_ports.dispatch(msg, d); + } + else + fprintf(stderr, "Warning: trying to access oscil object \"%s\"," + "which does not exist\n", obj_rl.c_str()); } void handlePad(const char *msg, rtosc::RtData &d) { string obj_rl(d.message, msg); @@ -333,18 +317,23 @@ d.matches++; d.reply((obj_rl+"needPrepare").c_str(), "F"); } else { - if(!pad) - return; - strcpy(d.loc, obj_rl.c_str()); - d.obj = pad; - PADnoteParameters::non_realtime_ports.dispatch(msg, d); - if(rtosc_narguments(msg)) { - if(!strcmp(msg, "oscilgen/prepare")) - ; //ignore - else { - d.reply((obj_rl+"needPrepare").c_str(), "T"); + if(pad) + { + strcpy(d.loc, obj_rl.c_str()); + d.obj = pad; + PADnoteParameters::non_realtime_ports.dispatch(msg, d); + if(rtosc_narguments(msg)) { + if(!strcmp(msg, "oscilgen/prepare")) + ; //ignore + else { + d.reply((obj_rl+"needPrepare").c_str(), "T"); + } } } + else + fprintf(stderr, "Warning: trying to access pad synth object " + "\"%s\", which does not exist\n", + obj_rl.c_str()); } } }; @@ -477,7 +466,7 @@ bank.loadbank(bank.banks[par].dir); } - void loadPart(int npart, const char *filename, Master *master) + void loadPart(int npart, const char *filename, Master *master, rtosc::RtData &d) { actual_load[npart]++; @@ -535,7 +524,7 @@ //Give it to the backend and wait for the old part to return for //deallocation parent->transmitMsg("/load-part", "ib", npart, sizeof(Part*), &p); - GUI::raiseUi(ui, "/damage", "s", ("/part"+to_s(npart)+"/").c_str()); + d.broadcast("/damage", "s", ("/part"+to_s(npart)+"/").c_str()); } //Load a new cleared Part instance @@ -904,6 +893,7 @@ struct dirent *fn; std::vector files; + bool has_updir = false; while((fn = readdir(dir))) { #ifndef WIN32 @@ -929,8 +919,14 @@ #endif if(finddir == is_dir && strcmp(".", fn->d_name)) files.push_back(fn->d_name); + + if(!strcmp("..", fn->d_name)) + has_updir = true; } + if(finddir == true && has_updir == false) + files.push_back(".."); + closedir(dir); std::sort(begin(files), end(files)); return files; @@ -1428,14 +1424,14 @@ const int part_id = rtosc_argument(msg,0).i; const char *file = rtosc_argument(msg,1).s; impl.pending_load[part_id]++; - impl.loadPart(part_id, file, impl.master); + impl.loadPart(part_id, file, impl.master, d); rEnd}, {"load-part:is", 0, 0, rBegin; const int part_id = rtosc_argument(msg,0).i; const char *file = rtosc_argument(msg,1).s; impl.pending_load[part_id]++; - impl.loadPart(part_id, file, impl.master); + impl.loadPart(part_id, file, impl.master, d); rEnd}, {"load-part:iss", 0, 0, rBegin; @@ -1443,7 +1439,7 @@ const char *file = rtosc_argument(msg,1).s; const char *name = rtosc_argument(msg,2).s; impl.pending_load[part_id]++; - impl.loadPart(part_id, file, impl.master); + impl.loadPart(part_id, file, impl.master, d); impl.uToB->write(("/part"+to_s(part_id)+"/Pname").c_str(), "s", name); rEnd}, @@ -1453,7 +1449,7 @@ const int slot = rtosc_argument(msg, 0).i + 128*bank.bank_lsb; if(slot < BANK_SIZE) { impl.pending_load[0]++; - impl.loadPart(0, impl.master->bank.ins[slot].filename.c_str(), impl.master); + impl.loadPart(0, impl.master->bank.ins[slot].filename.c_str(), impl.master, d); impl.uToB->write("/part0/Pname", "s", impl.master->bank.ins[slot].name.c_str()); } rEnd}, @@ -1549,7 +1545,7 @@ Bank &bank = impl.master->bank; const int part = rtosc_argument(msg, 0).i; const int program = rtosc_argument(msg, 1).i + 128*bank.bank_lsb; - impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master); + impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master, d); impl.uToB->write(("/part"+to_s(part)+"/Pname").c_str(), "s", impl.master->bank.ins[program].name.c_str()); rEnd}, {"setbank:c", 0, 0, @@ -1634,7 +1630,7 @@ //Setup starting time struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); + monotonic_clock_gettime(&time); start_time_sec = time.tv_sec; start_time_nsec = time.tv_nsec; @@ -1727,9 +1723,9 @@ //Last provided beat //Last acknowledged beat //Current offline status - + struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); + monotonic_clock_gettime(&time); uint32_t now = (time.tv_sec-start_time_sec)*100 + (time.tv_nsec-start_time_nsec)*1e-9*100; int32_t last_ack = master->last_ack; diff -Nru zynaddsubfx-3.0.3/src/Misc/Part.cpp zynaddsubfx-3.0.4/src/Misc/Part.cpp --- zynaddsubfx-3.0.3/src/Misc/Part.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Part.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -27,7 +27,6 @@ #include "../Synth/PADnote.h" #include "../Containers/ScratchString.h" #include "../DSP/FFTwrapper.h" -#include "../Misc/Util.h" #include #include #include @@ -133,7 +132,7 @@ p->Ppolymode = 0; p->Plegatomode = 1; }}}, - {"clear:", rProp(internal) rDoc("Reset Part To Defaults"), 0, + {"clear:", rProp(internal) rDoc("Reset Part To Defaults"), 0, [](const char *, RtData &d) { //XXX todo forward this event for middleware to handle @@ -174,9 +173,9 @@ #define rObject Part::Kit static const Ports kitPorts = { rSelf(Part::Kit, rEnabledBy(Penabled)), - rRecurp(padpars, "Padnote parameters"), - rRecurp(adpars, "Adnote parameters"), - rRecurp(subpars, "Adnote parameters"), + rRecurp(padpars, rEnabledBy(Ppadenabled), "Padnote parameters"), + rRecurp(adpars, rEnabledBy(Padenabled), "Adnote parameters"), + rRecurp(subpars, rEnabledBy(Psubenabled), "Subnote parameters"), rToggle(firstkit, rProp(internal), "If this is the part's first kit"), rToggle(Penabled, rDefaultDepends(firstkit), rPreset(true, true), rPreset(false, false), @@ -240,7 +239,7 @@ interpolation(interpolation) { if(prefix_) - strncpy(prefix, prefix_, sizeof(prefix)); + fast_strcpy(prefix, prefix_, sizeof(prefix)); else memset(prefix, 0, sizeof(prefix)); @@ -276,7 +275,7 @@ Pname = new char[PART_MAX_NAME_LEN]; oldvolumel = oldvolumer = 0.5f; - lastnote = -1; + lastnote = -1; defaults(); assert(partefx[0]); @@ -451,9 +450,10 @@ /* * Note On Messages */ -bool Part::NoteOn(unsigned char note, +bool Part::NoteOn(note_t note, unsigned char velocity, - int masterkeyshift) + int masterkeyshift, + float note_log2_freq) { //Verify Basic Mode and sanity const bool isRunningNote = notePool.existsRunningNote(); @@ -472,6 +472,7 @@ monomemPush(note); monomem[note].velocity = velocity; monomem[note].mkeyshift = masterkeyshift; + monomem[note].note_log2_freq = note_log2_freq; } else if(!monomemEmpty()) monomemClear(); @@ -486,9 +487,9 @@ const float vel = getVelocity(velocity, Pvelsns, Pveloffs); const int partkeyshift = (int)Pkeyshift - 64; const int keyshift = masterkeyshift + partkeyshift; - const float notebasefreq = getBaseFreq(note, keyshift); + const float notebasefreq = getBaseFreq(note_log2_freq, keyshift); - if(notebasefreq < 0) + if(notebasefreq < 0.0f) return false; //Portamento @@ -508,8 +509,8 @@ //Adjust Existing Notes if(doingLegato) { - LegatoParams pars = {notebasefreq, vel, portamento, note, true}; - notePool.applyLegato(pars); + LegatoParams pars = {notebasefreq, vel, portamento, note_log2_freq, true, prng()}; + notePool.applyLegato(note, pars); return true; } @@ -524,7 +525,7 @@ continue; SynthParams pars{memory, ctl, synth, time, notebasefreq, vel, - portamento, note, false}; + portamento, note_log2_freq, false, prng()}; const int sendto = Pkitmode ? item.sendto() : 0; try { @@ -534,7 +535,7 @@ wm, (pre+"kit"+i+"/adpars/").c_str), 0, i}); if(item.Psubenabled) notePool.insertNote(note, sendto, - {memory.alloc(kit[i].subpars, pars), 1, i}); + {memory.alloc(kit[i].subpars, pars, wm, (pre+"kit"+i+"/subpars/").c_str), 1, i}); if(item.Ppadenabled) notePool.insertNote(note, sendto, {memory.alloc(kit[i].padpars, pars, interpolation, wm, @@ -559,7 +560,7 @@ /* * Note Off Messages */ -void Part::NoteOff(unsigned char note) //release the key +void Part::NoteOff(note_t note) //release the key { // This note is released, so we remove it from the list. if(!monomemEmpty()) @@ -571,7 +572,7 @@ if(!ctl.sustain.sustain) { //the sustain pedal is not pushed if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) MonoMemRenote();//Play most recent still active note - else + else notePool.release(desc); } else { //the sustain pedal is pushed @@ -583,9 +584,9 @@ } } -void Part::PolyphonicAftertouch(unsigned char note, - unsigned char velocity, - int masterkeyshift) +void Part::PolyphonicAftertouch(note_t note, + unsigned char velocity, + int masterkeyshift) { (void) masterkeyshift; @@ -728,18 +729,20 @@ // (Made for Mono/Legato). void Part::MonoMemRenote() { - unsigned char mmrtempnote = monomemBack(); // Last list element. + note_t mmrtempnote = monomemBack(); // Last list element. monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...). - NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, - monomem[mmrtempnote].mkeyshift); + NoteOn(mmrtempnote, + monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift, + monomem[mmrtempnote].note_log2_freq); } -float Part::getBaseFreq(int note, int keyshift) const +float Part::getBaseFreq(float note_log2_freq, int keyshift) const { if(Pdrummode) - return 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); + return 440.0f * powf(2.0f, note_log2_freq - (69.0f / 12.0f)); else - return microtonal->getnotefreq(note, keyshift); + return microtonal->getnotefreq(note_log2_freq, keyshift); } float Part::getVelocity(uint8_t velocity, uint8_t velocity_sense, @@ -1054,7 +1057,7 @@ notePool.killAllNotes(); } -void Part::monomemPush(char note) +void Part::monomemPush(note_t note) { for(int i=0; i<256; ++i) if(monomemnotes[i]==note) @@ -1065,7 +1068,7 @@ monomemnotes[0] = note; } -void Part::monomemPop(char note) +void Part::monomemPop(note_t note) { int note_pos=-1; for(int i=0; i<256; ++i) @@ -1078,7 +1081,7 @@ } } -char Part::monomemBack(void) const +note_t Part::monomemBack(void) const { return monomemnotes[0]; } diff -Nru zynaddsubfx-3.0.3/src/Misc/Part.h zynaddsubfx-3.0.4/src/Misc/Part.h --- zynaddsubfx-3.0.3/src/Misc/Part.h 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Part.h 2019-02-23 15:43:37.000000000 +0000 @@ -43,11 +43,15 @@ // Midi commands implemented //returns true when note is successfully applied - bool NoteOn(unsigned char note, + bool NoteOn(note_t note, uint8_t vel, int shift) REALTIME { + return (NoteOn(note, vel, shift, note / 12.0f)); + }; + bool NoteOn(note_t note, unsigned char velocity, - int masterkeyshift) REALTIME; - void NoteOff(unsigned char note) REALTIME; - void PolyphonicAftertouch(unsigned char note, + int masterkeyshift, + float note_log2_freq) REALTIME; + void NoteOff(note_t note) REALTIME; + void PolyphonicAftertouch(note_t note, unsigned char velocity, int masterkeyshift) REALTIME; void AllNotesOff() REALTIME; //panic @@ -159,7 +163,7 @@ private: void MonoMemRenote(); // MonoMem stuff. - float getBaseFreq(int note, int keyshift) const; + float getBaseFreq(float note_log2_freq, int keyshift) const; float getVelocity(uint8_t velocity, uint8_t velocity_sense, uint8_t velocity_offset) const; void verifyKeyMode(void); @@ -177,9 +181,9 @@ bool lastlegatomodevalid; // To keep track of previous legatomodevalid. // MonoMem stuff - void monomemPush(char note); - void monomemPop(char note); - char monomemBack(void) const; + void monomemPush(note_t note); + void monomemPop(note_t note); + note_t monomemBack(void) const; bool monomemEmpty(void) const; void monomemClear(void); @@ -187,6 +191,7 @@ struct { unsigned char velocity; int mkeyshift; // I'm not sure masterkeyshift should be remembered. + float note_log2_freq; } monomem[256]; /* 256 is to cover all possible note values. monomem[] is used in conjunction with the list to diff -Nru zynaddsubfx-3.0.3/src/Misc/PresetExtractor.cpp zynaddsubfx-3.0.4/src/Misc/PresetExtractor.cpp --- zynaddsubfx-3.0.3/src/Misc/PresetExtractor.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/PresetExtractor.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -204,19 +204,16 @@ template std::string doCopy(MiddleWare &mw, string url, string name) { - XMLwrapper xml; - mw.doReadOnlyOp([&xml, url, name, &mw](){ + mw.doReadOnlyOp([url, name, &mw](){ Master *m = mw.spawnMaster(); //Get the pointer - //printf("capture at <%s>\n", (url+"self").c_str()); T *t = (T*)capture(m, url+"self"); assert(t); //Extract Via mxml - //t->add2XML(&xml); t->copy(mw.getPresetsStore(), name.empty()? NULL:name.c_str()); }); - return "";//xml.getXMLdata(); + return ""; } template @@ -249,9 +246,8 @@ template std::string doArrayCopy(MiddleWare &mw, int field, string url, string name) { - XMLwrapper xml; //printf("Getting info from '%s'<%d>\n", url.c_str(), field); - mw.doReadOnlyOp([&xml, url, field, name, &mw](){ + mw.doReadOnlyOp([url, field, name, &mw](){ Master *m = mw.spawnMaster(); //Get the pointer T *t = (T*)capture(m, url+"self"); @@ -259,7 +255,7 @@ t->copy(mw.getPresetsStore(), field, name.empty()?NULL:name.c_str()); }); - return "";//xml.getXMLdata(); + return ""; } template diff -Nru zynaddsubfx-3.0.3/src/Misc/Schema.cpp zynaddsubfx-3.0.4/src/Misc/Schema.cpp --- zynaddsubfx-3.0.3/src/Misc/Schema.cpp 2017-09-07 18:29:51.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Schema.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -1,3 +1,4 @@ +#include #include #include using namespace rtosc; diff -Nru zynaddsubfx-3.0.3/src/Misc/Util.cpp zynaddsubfx-3.0.4/src/Misc/Util.cpp --- zynaddsubfx-3.0.3/src/Misc/Util.cpp 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Util.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -55,6 +55,12 @@ return powf(velocity, x); } +char *fast_strcpy(char *dest, const char *src, size_t buffersize) +{ + *dest = 0; + return strncat(dest, src, buffersize-1); +} + /* * Get the detune in cents */ diff -Nru zynaddsubfx-3.0.3/src/Misc/Util.h zynaddsubfx-3.0.4/src/Misc/Util.h --- zynaddsubfx-3.0.3/src/Misc/Util.h 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/Util.h 2018-06-09 14:45:00.000000000 +0000 @@ -31,6 +31,19 @@ using std::min; using std::max; +/** + * Copy string to another memory location, including the terminating zero byte + * @param dest Destination memory location + * @param src Source string + * @param buffersize Maximal number of bytes that you can write to @p dest + * @return A pointer to @p dest + * @warning @p dest and @p src shall not overlap + * @warning if buffersize is larger than strlen(src)+1, unused bytes in @p dest + * are not overwritten. Secure information may be released. Don't use this if + * you want to send the string somewhere else, e.g. via IPC. + */ +char *fast_strcpy(char *dest, const char *src, size_t buffersize); + //Velocity Sensing function extern float VelF(float velocity, unsigned char scaling); diff -Nru zynaddsubfx-3.0.3/src/Misc/XMLwrapper.cpp zynaddsubfx-3.0.4/src/Misc/XMLwrapper.cpp --- zynaddsubfx-3.0.3/src/Misc/XMLwrapper.cpp 2017-09-07 18:39:19.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Misc/XMLwrapper.cpp 2019-03-10 16:16:42.000000000 +0000 @@ -34,7 +34,7 @@ const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where) { - const char *name = node->value.element.name; + const char *name = mxmlGetElement(node); if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml"))) return NULL; @@ -291,10 +291,10 @@ void XMLwrapper::endbranch() { if(verbose) - cout << "endbranch()" << node << "-" << node->value.element.name + cout << "endbranch()" << node << "-" << mxmlGetElement(node) << " To " - << node->parent << "-" << node->parent->value.element.name << endl; - node = node->parent; + << mxmlGetParent(node) << "-" << mxmlGetElement(mxmlGetParent(node)) << endl; + node = mxmlGetParent(node); } @@ -440,10 +440,10 @@ void XMLwrapper::exitbranch() { if(verbose) - cout << "exitbranch()" << node << "-" << node->value.element.name + cout << "exitbranch()" << node << "-" << mxmlGetElement(node) << " To " - << node->parent << "-" << node->parent->value.element.name << endl; - node = node->parent; + << mxmlGetParent(node) << "-" << mxmlGetElement(mxmlGetParent(node)) << endl; + node = mxmlGetParent(node); } @@ -519,24 +519,24 @@ void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const { ZERO(par, maxstrlen); - const mxml_node_t *tmp = mxmlFindElement(node, - node, - "string", - "name", - name.c_str(), - MXML_DESCEND_FIRST); + mxml_node_t *tmp = mxmlFindElement(node, + node, + "string", + "name", + name.c_str(), + MXML_DESCEND_FIRST); if(tmp == NULL) return; - if(tmp->child == NULL) + if(mxmlGetFirstChild(tmp) == NULL) return; - if(tmp->child->type == MXML_OPAQUE) { - snprintf(par, maxstrlen, "%s", tmp->child->value.element.name); + if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_OPAQUE) { + snprintf(par, maxstrlen, "%s", mxmlGetOpaque(mxmlGetFirstChild(tmp))); return; } - if((tmp->child->type == MXML_TEXT) - && (tmp->child->value.text.string != NULL)) { - snprintf(par, maxstrlen, "%s", tmp->child->value.text.string); + if((mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TEXT) + && (mxmlGetFirstChild(tmp) != NULL)) { + snprintf(par, maxstrlen, "%s", mxmlGetText(mxmlGetFirstChild(tmp),NULL)); return; } } @@ -544,27 +544,38 @@ string XMLwrapper::getparstr(const string &name, const std::string &defaultpar) const { - const mxml_node_t *tmp = mxmlFindElement(node, - node, - "string", - "name", - name.c_str(), - MXML_DESCEND_FIRST); + mxml_node_t *tmp = mxmlFindElement(node, + node, + "string", + "name", + name.c_str(), + MXML_DESCEND_FIRST); - if((tmp == NULL) || (tmp->child == NULL)) + if((tmp == NULL) || (mxmlGetFirstChild(tmp) == NULL)) return defaultpar; - if((tmp->child->type == MXML_OPAQUE) - && (tmp->child->value.element.name != NULL)) - return tmp->child->value.element.name; - - if((tmp->child->type == MXML_TEXT) - && (tmp->child->value.text.string != NULL)) - return tmp->child->value.text.string; + if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_OPAQUE + && (mxmlGetOpaque(mxmlGetFirstChild(tmp)) != NULL)) + return mxmlGetOpaque(mxmlGetFirstChild(tmp)); + + if(mxmlGetType(mxmlGetFirstChild(tmp)) == MXML_TEXT + && (mxmlGetText(mxmlGetFirstChild(tmp),NULL) != NULL)) + return mxmlGetText(mxmlGetFirstChild(tmp),NULL); return defaultpar; } +bool XMLwrapper::hasparreal(const char *name) const +{ + const mxml_node_t *tmp = mxmlFindElement(node, + node, + "par_real", + "name", + name, + MXML_DESCEND_FIRST); + return tmp != nullptr; +} + float XMLwrapper::getparreal(const char *name, float defaultpar) const { const mxml_node_t *tmp = mxmlFindElement(node, @@ -670,15 +681,24 @@ std::vector XMLwrapper::getBranch(void) const { std::vector res; - mxml_node_t *current = node->child; + mxml_node_t *current = mxmlGetFirstChild(node); while(current) { - if(current->type == MXML_ELEMENT) { + if(mxmlGetType(current) == MXML_ELEMENT) { +#if MXML_MAJOR_VERSION == 3 + XmlNode n(mxmlGetElement(current)); + int count = mxmlElementGetAttrCount(current); + const char *name; + for(int i = 0; i < count; ++i) { + n[name] = mxmlElementGetAttrByIndex(current, i, &name); + } +#else auto elm = current->value.element; XmlNode n(elm.name); - for(int i=0; inoteOn(ev.channel, ev.num, ev.value); - else - master->noteOff(ev.channel, ev.num); + master->noteOn(ev.channel, ev.num, ev.value); + break; + + case M_FLOAT_NOTE: + master->noteOn(ev.channel, ev.num, ev.value, ev.log2_freq); break; case M_CONTROLLER: diff -Nru zynaddsubfx-3.0.3/src/Nio/InMgr.h zynaddsubfx-3.0.4/src/Nio/InMgr.h --- zynaddsubfx-3.0.3/src/Nio/InMgr.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Nio/InMgr.h 2019-02-23 15:43:37.000000000 +0000 @@ -19,12 +19,12 @@ namespace zyn { enum midi_type { - M_NOTE = 1, - M_CONTROLLER = 2, - M_PGMCHANGE = 3, - M_PRESSURE = 4 -}; //type=1 for note, type=2 for controller, type=3 for program change -//type=4 for polyphonic aftertouch + M_NOTE = 1, // for note + M_CONTROLLER = 2, // for controller + M_PGMCHANGE = 3, // for program change + M_PRESSURE = 4, // for polyphonic aftertouch + M_FLOAT_NOTE = 5 // for floating point note +}; struct MidiEvent { MidiEvent(); @@ -33,6 +33,7 @@ int num; //note, controller or program number int value; //velocity or controller value int time; //time offset of event (used only in jack->jack case at the moment) + float log2_freq; //type=5 for logarithmic representation of note }; //super simple class to manage the inputs diff -Nru zynaddsubfx-3.0.3/src/Nio/MidiIn.cpp zynaddsubfx-3.0.4/src/Nio/MidiIn.cpp --- zynaddsubfx-3.0.3/src/Nio/MidiIn.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Nio/MidiIn.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -14,15 +14,73 @@ #include "MidiIn.h" #include "../globals.h" #include "InMgr.h" +#include namespace zyn { +MidiIn::MidiIn() +{ + sysex_offset = 0; + memset(sysex_data, 0, sizeof(sysex_data)); +} + +uint8_t MidiIn::midiSysEx(unsigned char data) +{ + if (data & 0x80) { + if (data == 0xF0) { + sysex_offset = 0; /* begin */ + } else if (data == 0xF7) { + return (2); /* end */ + } else { + return (1); /* error */ + } + } else if (sysex_offset >= sizeof(sysex_data)) { + return (1); /* error */ + } + sysex_data[sysex_offset++] = data; + return (0); +} + void MidiIn::midiProcess(unsigned char head, unsigned char num, unsigned char value) { MidiEvent ev; unsigned char chan = head & 0x0f; + + /* SYSEX handling */ + if (head == 0xF0 || sysex_offset != 0) { + uint8_t status = 0; + + status |= midiSysEx(head); + status |= midiSysEx(num); + status |= midiSysEx(value); + + if (status & 1) { + /* error parsing SYSEX */ + sysex_offset = 0; + } else if (status & 2) { + /* message complete */ + + if (sysex_offset >= 10 && + sysex_data[1] == 0x0A && + sysex_data[2] == 0x55) { + ev.type = M_FLOAT_NOTE; + ev.channel = sysex_data[3] & 0x0F; + ev.num = sysex_data[4]; + ev.value = sysex_data[5]; + ev.log2_freq = (sysex_data[6] + + (sysex_data[7] / (128.0f)) + + (sysex_data[8] / (128.0f * 128.0f)) + + (sysex_data[9] / (128.0f * 128.0f * 128.0f)) + ) / 12.0f; + InMgr::getInstance().putEvent(ev); + } + return; /* message complete */ + } else { + return; /* wait for more data */ + } + } switch(head & 0xf0) { case 0x80: //Note Off ev.type = M_NOTE; diff -Nru zynaddsubfx-3.0.3/src/Nio/MidiIn.h zynaddsubfx-3.0.4/src/Nio/MidiIn.h --- zynaddsubfx-3.0.3/src/Nio/MidiIn.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Nio/MidiIn.h 2019-02-23 15:43:37.000000000 +0000 @@ -24,13 +24,19 @@ class MidiIn:public virtual Engine { public: + MidiIn(); + /**Enables or disables driver based upon value*/ virtual void setMidiEn(bool nval) = 0; /**Returns if driver is initialized*/ virtual bool getMidiEn() const = 0; - static void midiProcess(unsigned char head, - unsigned char num, - unsigned char value); + void midiProcess(unsigned char head, + unsigned char num, + unsigned char value); + private: + uint8_t midiSysEx(unsigned char data); + uint8_t sysex_offset; + uint8_t sysex_data[64]; }; } diff -Nru zynaddsubfx-3.0.3/src/Nio/Nio.cpp zynaddsubfx-3.0.4/src/Nio/Nio.cpp --- zynaddsubfx-3.0.3/src/Nio/Nio.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Nio/Nio.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -134,7 +134,7 @@ #if JACK #include -void Nio::preferedSampleRate(unsigned &rate) +void Nio::preferredSampleRate(unsigned &rate) { #if __linux__ //avoid checking in with jack if it's off @@ -157,7 +157,7 @@ } } #else -void Nio::preferedSampleRate(unsigned &) +void Nio::preferredSampleRate(unsigned &) {} #endif diff -Nru zynaddsubfx-3.0.3/src/Nio/Nio.h zynaddsubfx-3.0.4/src/Nio/Nio.h --- zynaddsubfx-3.0.3/src/Nio/Nio.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Nio/Nio.h 2019-02-23 15:43:37.000000000 +0000 @@ -45,8 +45,8 @@ std::string getSource(void); std::string getSink(void); - //Get the prefered sample rate from jack (if running) - void preferedSampleRate(unsigned &rate); + //Get the preferred sample rate from jack (if running) + void preferredSampleRate(unsigned &rate); //Complete Master Swaps to ONLY BE CALLED FROM RT CONTEXT void masterSwap(Master *master); diff -Nru zynaddsubfx-3.0.3/src/Params/ADnoteParameters.cpp zynaddsubfx-3.0.4/src/Params/ADnoteParameters.cpp --- zynaddsubfx-3.0.3/src/Params/ADnoteParameters.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/ADnoteParameters.cpp 2019-03-10 16:16:42.000000000 +0000 @@ -151,14 +151,14 @@ rParamZyn(PFilterVelocityScaleFunction, rShort("v.sense"), rDefault(64), "Filter Velocity Function Shape"), - + //Modulator Stuff - rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, + rOption(PFMEnabled, rShort("mode"), rOptions(none, mix, ring, phase, frequency, pulse), rDefault(none), "Modulator mode"), rParamI(PFMVoice, rShort("voice"), rDefault(-1), "Modulator Oscillator Selection"), - rParamZyn(PFMVolume, rShort("vol."), rDefault(90), - "Modulator Magnitude"), + rParamF(FMvolume, rShort("vol."), rLinear(0.0, 100.0), + rDefault(70.0), "Modulator Magnitude"), rParamZyn(PFMVolumeDamp, rShort("damp."), rDefault(64), "Modulator HF dampening"), rParamZyn(PFMVelocityScaleFunction, rShort("sense"), rDefault(64), @@ -180,6 +180,7 @@ "Modulator Amplitude Envelope"), + //weird stuff for PCoarseDetune {"detunevalue:", rMap(unit,cents) rDoc("Get detune in cents"), NULL, [](const char *, RtData &d) @@ -207,7 +208,7 @@ obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64,63) + {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64,63) rDefault(0) rDoc("Coarse note detune"), NULL, [](const char *msg, RtData &d) { @@ -222,7 +223,17 @@ obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024; } }}, - + {"PFMVolume::i", rShort("vol.") rLinear(0,127) + rDoc("Modulator Magnitude"), NULL, + [](const char *msg, RtData &d) + { + rObject *obj = (rObject *)d.obj; + if (!rtosc_narguments(msg)) + d.reply(d.loc, "i", (int)roundf(127.0f * obj->FMvolume + / 100.0f)); + else + obj->FMvolume = 100.0f * rtosc_argument(msg, 0).i / 127.0f; + }}, //weird stuff for PCoarseDetune {"FMdetunevalue:", rMap(unit,cents) rDoc("Get modulator detune"), NULL, [](const char *, RtData &d) { @@ -305,10 +316,20 @@ //Amplitude rParamZyn(PPanning, rShort("pan"), rDefault(64), "Panning of ADsynth (0 random, 1 left, 127 right)"), - rParamZyn(PVolume, rShort("vol"), rDefault(90), "volume control"), - rParamZyn(PAmpVelocityScaleFunction, rShort("scale"), rDefault(64), - "Volume Velocity Control"), - + rParamF(Volume, rShort("vol"), rLinear(-60.0f,20.0f), + rDefault(-3.75f), "volume control"), + rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), rDefault(64), + "Volume velocity sense"), + {"PVolume::i", rShort("vol.") rLinear(0,127) + rDoc("Volume"), NULL, + [](const char *msg, RtData &d) + { + rObject *obj = (rObject *)d.obj; + if (!rtosc_narguments(msg)) + d.reply(d.loc, "i", (int)roundf(96.0f * (1.0f + obj->Volume/60.0f))); + else + obj->Volume = -60.0f * (1.0f - rtosc_argument(msg, 0).i / 96.0f); + }}, rParamZyn(Fadein_adjustment, rDefault(FADEIN_ADJUSTMENT_SCALE), "Adjustment for anti-pop strategy."), rParamZyn(PPunchStrength, rShort("strength"), rDefault(0), @@ -321,7 +342,7 @@ "Punch Velocity control"), //Filter - rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(64), + rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(0), "Filter Velocity Magnitude"), rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), rDefault(64), "Filter Velocity Function Shape"), @@ -450,7 +471,7 @@ PBandwidth = 64; /* Amplitude Global Parameters */ - PVolume = 90; + Volume = -3.75f; PPanning = 64; //center PAmpVelocityScaleFunction = 64; AmpEnvelope->defaults(); @@ -463,7 +484,7 @@ Hrandgrouping = 0; /* Filter Global Parameters*/ - PFilterVelocityScale = 64; + PFilterVelocityScale = 0; PFilterVelocityScaleFunction = 64; GlobalFilter->defaults(); FilterEnvelope->defaults(); @@ -525,7 +546,7 @@ //I use the internal oscillator (-1) PFMVoice = -1; - PFMVolume = 90; + FMvolume = 70.0; PFMVolumeDamp = 64; PFMDetune = 8192; PFMCoarseDetune = 0; @@ -789,7 +810,7 @@ xml.beginbranch("FM_PARAMETERS"); xml.addpar("input_voice", PFMVoice); - xml.addpar("volume", PFMVolume); + xml.addparreal("volume", FMvolume); xml.addpar("volume_damp", PFMVolumeDamp); xml.addpar("velocity_sensing", PFMVelocityScaleFunction); @@ -829,7 +850,7 @@ xml.addparbool("stereo", PStereo); xml.beginbranch("AMPLITUDE_PARAMETERS"); - xml.addpar("volume", PVolume); + xml.addparreal("volume", Volume); xml.addpar("panning", PPanning); xml.addpar("velocity_sensing", PAmpVelocityScaleFunction); xml.addpar("fadein_adjustment", Fadein_adjustment); @@ -904,7 +925,15 @@ PStereo = xml.getparbool("stereo", PStereo); if(xml.enterbranch("AMPLITUDE_PARAMETERS")) { - PVolume = xml.getpar127("volume", PVolume); + const bool upgrade_3_0_3 = (xml.fileversion() < version_type(3,0,3)) || + (!xml.hasparreal("volume")); + + if (upgrade_3_0_3) { + int vol = xml.getpar127("volume", 0); + Volume = -60.0f * ( 1.0f - vol / 96.0f); + } else { + Volume = xml.getparreal("volume", Volume); + } PPanning = xml.getpar127("panning", PPanning); PAmpVelocityScaleFunction = xml.getpar127("velocity_sensing", PAmpVelocityScaleFunction); @@ -1090,7 +1119,7 @@ RCopy(FilterLfo); copy(PFMVoice); - copy(PFMVolume); + copy(FMvolume); copy(PFMVolumeDamp); copy(PFMVelocityScaleFunction); @@ -1117,7 +1146,7 @@ { copy(PStereo); - copy(PVolume); + copy(Volume); copy(PPanning); copy(PAmpVelocityScaleFunction); @@ -1264,8 +1293,16 @@ } if(xml.enterbranch("FM_PARAMETERS")) { + const bool upgrade_3_0_3 = (xml.fileversion() < version_type(3,0,3)) || + (xml.getparreal("volume", -1) < 0); + PFMVoice = xml.getpar("input_voice", PFMVoice, -1, nvoice - 1); - PFMVolume = xml.getpar127("volume", PFMVolume); + if (upgrade_3_0_3) { + int Pvolume = xml.getpar127("volume", 0); + FMvolume = 100.0f * Pvolume / 127.0f; + } else { + FMvolume = xml.getparreal("volume", FMvolume); + } PFMVolumeDamp = xml.getpar127("volume_damp", PFMVolumeDamp); PFMVelocityScaleFunction = xml.getpar127("velocity_sensing", PFMVelocityScaleFunction); diff -Nru zynaddsubfx-3.0.3/src/Params/ADnoteParameters.h zynaddsubfx-3.0.4/src/Params/ADnoteParameters.h --- zynaddsubfx-3.0.3/src/Params/ADnoteParameters.h 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/ADnoteParameters.h 2019-03-10 16:16:42.000000000 +0000 @@ -20,7 +20,7 @@ namespace zyn { enum FMTYPE { - NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PW_MOD + NONE, MIX, RING_MOD, PHASE_MOD, FREQ_MOD, PW_MOD }; /*****************************************************************/ @@ -64,7 +64,7 @@ 127 - right */ unsigned char PPanning; - unsigned char PVolume; + float Volume; unsigned char PAmpVelocityScaleFunction; @@ -258,7 +258,7 @@ * MODULLATOR PARAMETERS * ****************************/ - /* Modullator Parameters (0=off,1=Morph,2=RM,3=PM,4=FM.. */ + /* Modullator Parameters (0=off,1=Mix,2=RM,3=PM,4=FM.. */ unsigned char PFMEnabled; /* Voice that I use as modullator instead of FMSmp. @@ -270,7 +270,7 @@ OscilGen *FMSmp; /* Modullator Volume */ - unsigned char PFMVolume; + float FMvolume; /* Modullator damping at higher frequencies */ unsigned char PFMVolumeDamp; diff -Nru zynaddsubfx-3.0.3/src/Params/Controller.cpp zynaddsubfx-3.0.4/src/Params/Controller.cpp --- zynaddsubfx-3.0.3/src/Params/Controller.cpp 2017-07-21 18:44:39.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/Controller.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -44,9 +44,9 @@ rToggle(modwheel.exponential, rShort("mdw.exp"), rDefault(false), "Modwheel Exponential Mode"), rToggle(pitchwheel.is_split, rDefault(false), - "If PitchWheel Has unified bendrange or not"), + "If PitchWheel has unified bendrange or not"), rParamI(pitchwheel.bendrange, rShort("pch.d"), rDefault(200), - rLinear(-6400, 6400), + rLinear(-6400, 6400), rUnit(% of semitone), "Range of MIDI Pitch Wheel"), rParamI(pitchwheel.bendrange_down, rDefault(0), "Lower Range of MIDI Pitch Wheel"), diff -Nru zynaddsubfx-3.0.3/src/Params/FilterParams.cpp zynaddsubfx-3.0.4/src/Params/FilterParams.cpp --- zynaddsubfx-3.0.3/src/Params/FilterParams.cpp 2017-12-05 01:00:37.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/FilterParams.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -86,9 +86,9 @@ rParamF(basefreq, rShort("cutoff"), rUnit(Hz), rLog(31.25, 32000), rDefaultDepends(loc), - rPreset(ad_global_filter, 0x1.3d434p+12), - rPreset(ad_voice_filter, 0x1.d48ab6p+8), - rPreset(sub_filter, 0x1.294d3ep+11), + rPreset(ad_global_filter, 32000), + rPreset(ad_voice_filter, 32000), + rPreset(sub_filter, 32000), rPreset(in_effect, 0x1.f3fffcp+9), "Base cutoff frequency"), rParamF(freqtracking, rShort("f.track"), rUnit(%), @@ -355,9 +355,9 @@ switch(loc) { - case ad_global_filter: init(2, 94, 40); break; - case ad_voice_filter: init(2, 50, 60); break; - case sub_filter: init(2, 80, 40); break; + case ad_global_filter: init(2, 127, 40); break; + case ad_voice_filter: init(2, 127, 60); break; + case sub_filter: init(2, 127, 40); break; case in_effect: init(0, 64, 64); break; default: throw std::logic_error("Invalid filter consumer location"); } diff -Nru zynaddsubfx-3.0.3/src/Params/LFOParams.cpp zynaddsubfx-3.0.4/src/Params/LFOParams.cpp --- zynaddsubfx-3.0.3/src/Params/LFOParams.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/LFOParams.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -57,7 +57,7 @@ rDefaultDepends(loc), rDefault(64), rPreset(ad_voice_freq, 0), "Starting Phase"), rOption(PLFOtype, rShort("type"), rOptions(sine, triangle, square, up, down, - exp1, exp2), rDefault(sine), "Shape of LFO"), + exp1, exp2, random), rDefault(sine), "Shape of LFO"), rParamZyn(Prandomness, rShort("a.r."), rSpecial(disable), rDefault(0), "Amplitude Randomness (calculated uniformly at each cycle)"), rParamZyn(Pfreqrand, rShort("f.r."), rSpecial(disable), rDefault(0), diff -Nru zynaddsubfx-3.0.3/src/Params/PADnoteParameters.cpp zynaddsubfx-3.0.4/src/Params/PADnoteParameters.cpp --- zynaddsubfx-3.0.3/src/Params/PADnoteParameters.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/PADnoteParameters.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -63,7 +63,7 @@ "Punch Velocity control"), //Filter - rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(64), + rParamZyn(PFilterVelocityScale, rShort("scale"), rDefault(0), "Filter Velocity Magnitude"), rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), rDefault(64), "Filter Velocity Function Shape"), @@ -231,7 +231,8 @@ rParamI(Pquality.oct, rShort("octaves"), rLinear(0,7), rDefault(3), "Number of octaves to sample (above the first sample"), - {"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, + {"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) + rDefault(500) rDoc("Bandwith Of Harmonics"), NULL, [](const char *msg, rtosc::RtData &d) { PADnoteParameters *p = ((PADnoteParameters*)d.obj); if(rtosc_narguments(msg)) { @@ -269,7 +270,7 @@ d.reply(d.loc, "i", (int)realbw); delete[] tmp;}}, {"harmonic_profile:", rProp(non-realtime) rDoc("UI display of the harmonic profile"), - NULL, [](const char *m, rtosc::RtData &d) { + NULL, [](const char *, rtosc::RtData &d) { PADnoteParameters *p = ((PADnoteParameters*)d.obj); #define RES 512 char types[RES+2] = {0}; @@ -284,6 +285,11 @@ d.replyArray(d.loc, types, args); #undef RES }}, + {"export2wav:s", rDoc("Export padsynth waveforms to .wav files"), + NULL, [](const char *m, rtosc::RtData&d) { + PADnoteParameters *p = ((PADnoteParameters*)d.obj); + p->export2wav(rtosc_argument(m, 0).s); + }}, {"needPrepare:", rDoc("Unimplemented Stub"), NULL, [](const char *, rtosc::RtData&) {}}, }; @@ -401,7 +407,7 @@ PPunchVelocitySensing = 72; /* Filter Global Parameters*/ - PFilterVelocityScale = 64; + PFilterVelocityScale = 0; PFilterVelocityScaleFunction = 64; GlobalFilter->defaults(); FilterEnvelope->defaults(); diff -Nru zynaddsubfx-3.0.3/src/Params/SUBnoteParameters.cpp zynaddsubfx-3.0.4/src/Params/SUBnoteParameters.cpp --- zynaddsubfx-3.0.3/src/Params/SUBnoteParameters.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Params/SUBnoteParameters.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -55,7 +55,7 @@ "Enable for Bandwidth Envelope"), rToggle(PGlobalFilterEnabled, rShort("enable"), rDefault(false), "Enable for Global Filter"), - rParamZyn(PGlobalFilterVelocityScale, rShort("scale"), rDefault(64), + rParamZyn(PGlobalFilterVelocityScale, rShort("scale"), rDefault(0), "Filter Velocity Magnitude"), rParamZyn(PGlobalFilterVelocityScaleFunction, rShort("sense"), rDefault(64), "Filter Velocity Function Shape"), @@ -91,9 +91,9 @@ rParamZyn(Phmagtype, rShort("mag. type"), rOptions(linear, -40dB, -60dB, -80dB, -100dB), rDefault(linear), "Magnitude scale"), - rArray(Phmag, MAX_SUB_HARMONICS, rDefaultMissing, + rArray(Phmag, MAX_SUB_HARMONICS, rDefault([127 0 0 ...]), "Harmonic magnitudes"), - rArray(Phrelbw, MAX_SUB_HARMONICS, rDefaultMissing, + rArray(Phrelbw, MAX_SUB_HARMONICS, rDefault([64 ...]), "Relative bandwidth"), rParamZyn(Pbwscale, rShort("stretch"), rDefault(64), "Bandwidth scaling with frequency"), @@ -108,6 +108,7 @@ {"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, rBegin; + (void) msg; for(int i=0; iPhmag[i] = 0; obj->Phrelbw[i] = 64; @@ -116,6 +117,7 @@ rEnd}, {"detunevalue:", rDoc("Get note detune value"), NULL, rBegin; + (void) msg; d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); rEnd}, //weird stuff for PCoarseDetune @@ -150,6 +152,7 @@ " filter = [frequency, bandwidth, amplitude]"), NULL, rBegin; + (void) msg; //Identify the active harmonics int pos[MAX_SUB_HARMONICS]; @@ -301,7 +304,7 @@ Phmag[0] = 127; PGlobalFilterEnabled = 0; - PGlobalFilterVelocityScale = 64; + PGlobalFilterVelocityScale = 0; PGlobalFilterVelocityScaleFunction = 64; AmpEnvelope->defaults(); diff -Nru zynaddsubfx-3.0.3/src/Plugin/AlienWah/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/AlienWah/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/AlienWah/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/AlienWah/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynAlienWah_vst PROPERTIES OUTPUT_NAME "ZynAlienWah") set_target_properties(ZynAlienWah_vst PROPERTIES PREFIX "") -target_link_libraries(ZynAlienWah_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynAlienWah_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynAlienWah_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynAlienWah_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynAlienWah_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynAlienWah_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynAlienWah_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynAlienWah.lv2/) install(TARGETS ZynAlienWah_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/Chorus/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/Chorus/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/Chorus/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/Chorus/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynChorus_vst PROPERTIES OUTPUT_NAME "ZynChorus") set_target_properties(ZynChorus_vst PROPERTIES PREFIX "") -target_link_libraries(ZynChorus_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynChorus_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynChorus_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynChorus_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynChorus_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynChorus_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynChorus_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynChorus.lv2/) install(TARGETS ZynChorus_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/Distortion/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/Distortion/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/Distortion/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/Distortion/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynDistortion_vst PROPERTIES OUTPUT_NAME "ZynDistortion") set_target_properties(ZynDistortion_vst PROPERTIES PREFIX "") -target_link_libraries(ZynDistortion_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynDistortion_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynDistortion_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynDistortion_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynDistortion_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynDistortion_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynDistortion_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynDistortion.lv2/) install(TARGETS ZynDistortion_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/DynamicFilter/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/DynamicFilter/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/DynamicFilter/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/DynamicFilter/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynDynamicFilter_vst PROPERTIES OUTPUT_NAME "ZynDynamicFilter") set_target_properties(ZynDynamicFilter_vst PROPERTIES PREFIX "") -target_link_libraries(ZynDynamicFilter_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynDynamicFilter_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynDynamicFilter_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynDynamicFilter_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynDynamicFilter_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynDynamicFilter_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynDynamicFilter_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynDynamicFilter.lv2/) install(TARGETS ZynDynamicFilter_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/Echo/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/Echo/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/Echo/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/Echo/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynEcho_vst PROPERTIES OUTPUT_NAME "ZynEcho") set_target_properties(ZynEcho_vst PROPERTIES PREFIX "") -target_link_libraries(ZynEcho_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynEcho_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynEcho_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynEcho_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynEcho_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynEcho_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynEcho_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynEcho.lv2/) install(TARGETS ZynEcho_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/Phaser/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/Phaser/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/Phaser/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/Phaser/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynPhaser_vst PROPERTIES OUTPUT_NAME "ZynPhaser") set_target_properties(ZynPhaser_vst PROPERTIES PREFIX "") -target_link_libraries(ZynPhaser_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynPhaser_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynPhaser_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynPhaser_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynPhaser_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynPhaser_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynPhaser_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynPhaser.lv2/) install(TARGETS ZynPhaser_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/Reverb/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/Reverb/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/Reverb/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/Reverb/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -14,8 +14,13 @@ set_target_properties(ZynReverb_vst PROPERTIES OUTPUT_NAME "ZynReverb") set_target_properties(ZynReverb_vst PROPERTIES PREFIX "") -target_link_libraries(ZynReverb_lv2 zynaddsubfx_core ${OS_LIBRARIES}) -target_link_libraries(ZynReverb_vst zynaddsubfx_core ${OS_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynReverb_lv2 zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + target_link_libraries(ZynReverb_vst zynaddsubfx_core ${OS_LIBRARIES} "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynReverb_lv2 zynaddsubfx_core ${OS_LIBRARIES}) + target_link_libraries(ZynReverb_vst zynaddsubfx_core ${OS_LIBRARIES}) +endif() install(TARGETS ZynReverb_lv2 LIBRARY DESTINATION ${PluginLibDir}/lv2/ZynReverb.lv2/) install(TARGETS ZynReverb_vst LIBRARY DESTINATION ${PluginLibDir}/vst/) diff -Nru zynaddsubfx-3.0.3/src/Plugin/ZynAddSubFX/CMakeLists.txt zynaddsubfx-3.0.4/src/Plugin/ZynAddSubFX/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Plugin/ZynAddSubFX/CMakeLists.txt 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/ZynAddSubFX/CMakeLists.txt 2018-03-11 15:01:27.000000000 +0000 @@ -115,7 +115,11 @@ "-static" iphlpapi "-static" winpthread) elseif(ZestGui) - set(PLATFORM_LIBRARIES X11 GL rt) + if(APPLE) + set(PLATFORM_LIBRARIES) + else() + set(PLATFORM_LIBRARIES X11 GL rt) + endif() elseif(NtkGui OR FltkGui) set(PLATFORM_LIBRARIES X11 rt) else() @@ -126,10 +130,20 @@ target_link_libraries(ZynAddSubFX_lv2 zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} ${PLATFORM_LIBRARIES}) endif() -target_link_libraries(ZynAddSubFX_vst zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} - ${PLATFORM_LIBRARIES}) +if(APPLE) + target_link_libraries(ZynAddSubFX_vst zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} + ${PLATFORM_LIBRARIES} "-framework Cocoa" "-framework openGL" "-Wl,-exported_symbol,_VSTPluginMain") +else() + target_link_libraries(ZynAddSubFX_vst zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES} + ${PLATFORM_LIBRARIES}) +endif() if(ZestGui AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - target_link_libraries(ZynAddSubFX_lv2_ui X11 GL) + if(APPLE) + target_link_libraries(ZynAddSubFX_lv2_ui "-framework Cocoa" "-framework openGL" "-Wl,-exported_symbol,_lv2ui_descriptor") + target_link_libraries(ZynAddSubFX_lv2 "-Wl,-exported_symbol,_lv2_descriptor" "-Wl,-exported_symbol,_lv2_generate_ttl") + else() + target_link_libraries(ZynAddSubFX_lv2_ui X11 GL) + endif() endif() if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") diff -Nru zynaddsubfx-3.0.3/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp zynaddsubfx-3.0.4/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp --- zynaddsubfx-3.0.3/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp 2017-08-10 14:06:15.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Plugin/ZynAddSubFX/ZynAddSubFX-UI-Zest.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -28,9 +28,9 @@ void (*zest_close)(zest_t*); void (*zest_setup)(zest_t*); void (*zest_draw)(zest_t*); - void (*zest_motion)(zest_t*, int x, int y); - void (*zest_scroll)(zest_t*, int x, int y, int dx, int dy); - void (*zest_mouse)(zest_t *z, int button, int action, int x, int y); + void (*zest_motion)(zest_t*, int x, int y, int mod); + void (*zest_scroll)(zest_t*, int x, int y, int dx, int dy, int mod); + void (*zest_mouse)(zest_t *z, int button, int action, int x, int y, int mod); void (*zest_key)(zest_t *z, char *key, bool press); void (*zest_resize)(zest_t *z, int w, int h); void (*zest_special)(zest_t *z, int key, int press); @@ -60,6 +60,10 @@ handle = LoadLibrary("./libzest.dll"); if(!handle) handle = LoadLibrary("libzest.dll"); +#elif defined __APPLE__ + handle = dlopen("@loader_path/libzest.dylib", RTLD_NOW | RTLD_LOCAL); + if(!handle) // VST + handle = dlopen("@loader_path/../Resources/libzest.dylib", RTLD_LAZY); #else handle = dlopen("./libzest.so", RTLD_LAZY); if(!handle) @@ -154,7 +158,7 @@ bool onScroll(const ScrollEvent &ev) override { if(z.zest) - z.zest_scroll(z.zest, ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); + z.zest_scroll(z.zest, ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY(), ev.mod); return false; } @@ -169,14 +173,14 @@ bool onMouse(const MouseEvent &m) override { if(z.zest) - z.zest_mouse(z.zest, m.button, m.press, m.pos.getX(), m.pos.getY()); + z.zest_mouse(z.zest, m.button, m.press, m.pos.getX(), m.pos.getY(), m.mod); return false; } bool onMotion(const MotionEvent &m) override { if(z.zest) - z.zest_motion(z.zest, m.pos.getX(), m.pos.getY()); + z.zest_motion(z.zest, m.pos.getX(), m.pos.getY(), m.mod); return false; } @@ -190,8 +194,8 @@ if(!z.zest) { if(!z.zest_open) return; -if(!oscPort) - return; + if(!oscPort) + return; printf("[INFO:Zyn] zest_open()\n"); char address[1024]; snprintf(address, sizeof(address), "osc.udp://127.0.0.1:%d",oscPort); @@ -203,7 +207,6 @@ } z.zest_draw(z.zest); - repaint(); } bool onKeyboard(const KeyboardEvent &ev) @@ -218,8 +221,11 @@ void uiIdle(void) override { - if(z.zest) - z.zest_tick(z.zest); + if(z.zest) { + if (z.zest_tick(z.zest)) { + repaint(); + } + } } void uiReshape(uint width, uint height) diff -Nru zynaddsubfx-3.0.3/src/Synth/ADnote.cpp zynaddsubfx-3.0.4/src/Synth/ADnote.cpp --- zynaddsubfx-3.0.3/src/Synth/ADnote.cpp 2017-10-01 19:35:35.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/ADnote.cpp 2019-03-10 16:16:42.000000000 +0000 @@ -23,6 +23,7 @@ #include "../Misc/Allocator.h" #include "../Params/ADnoteParameters.h" #include "../Containers/ScratchString.h" +#include "../Containers/NotePool.h" #include "ModFilter.h" #include "OscilGen.h" #include "ADnote.h" @@ -41,10 +42,12 @@ ADnoteParameters &pars = *pars_; portamento = spars.portamento; - midinote = spars.note; + note_log2_freq = spars.note_log2_freq; NoteEnabled = ON; basefreq = spars.frequency; velocity = spars.velocity; + initial_seed = spars.seed; + current_prng_state = spars.seed; stereo = pars.GlobalPar.PStereo; NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType, @@ -53,8 +56,8 @@ bandwidthDetuneMultiplier = pars.getBandwidthDetuneMultiplier(); if(pars.GlobalPar.PPanning == 0) - NoteGlobalPar.Panning = RND; - else + NoteGlobalPar.Panning = getRandomFloat(); + else NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; @@ -216,7 +219,7 @@ voice.filterbypass = param.Pfilterbypass; setupVoiceMod(nvoice); - + voice.FMVoice = param.PFMVoice; voice.FMFreqEnvelope = NULL; voice.FMAmpEnvelope = NULL; @@ -422,7 +425,7 @@ else switch(param.PFMEnabled) { case 1: - voice.FMEnabled = MORPH; + voice.FMEnabled = MIX; break; case 2: voice.FMEnabled = RING_MOD; @@ -453,7 +456,7 @@ float tmp = 1.0f; if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) - || (voice.FMEnabled == MORPH) + || (voice.FMEnabled == MIX) || (voice.FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); @@ -482,7 +485,7 @@ //Compute the Voice's modulator volume (incl. damping) float fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice), param.PFMVolumeDamp / 64.0f - 1.0f); - const float fmvolume_ = param.PFMVolume / 127.0f; + const float fmvolume_ = param.FMvolume / 100.0f; switch(voice.FMEnabled) { case PHASE_MOD: case PW_MOD: @@ -508,8 +511,9 @@ SynthNote *ADnote::cloneLegato(void) { - SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - (bool)portamento, legato.param.midinote, true}; + SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, + (bool)portamento, legato.param.note_log2_freq, true, + initial_seed }; return memory.alloc(&pars, sp); } @@ -520,14 +524,15 @@ void ADnote::legatonote(LegatoParams lpars) { //ADnoteParameters &pars = *partparams; - // Manage legato stuff if(legato.update(lpars)) return; portamento = lpars.portamento; - midinote = lpars.midinote; + note_log2_freq = lpars.note_log2_freq; basefreq = lpars.frequency; + initial_seed = lpars.seed; + current_prng_state = lpars.seed; if(velocity > 1.0f) velocity = 1.0f; @@ -538,12 +543,12 @@ pars.GlobalPar.PDetune); bandwidthDetuneMultiplier = pars.getBandwidthDetuneMultiplier(); - if(pars.GlobalPar.PPanning == 0) - NoteGlobalPar.Panning = RND; - else + if(pars.GlobalPar.PPanning == 0) { + NoteGlobalPar.Panning = getRandomFloat(); + } else NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; - NoteGlobalPar.Filter->updateSense(velocity, + NoteGlobalPar.Filter->updateSense(velocity, pars.GlobalPar.PFilterVelocityScale, pars.GlobalPar.PFilterVelocityScaleFunction); @@ -587,13 +592,12 @@ pars.VoicePar[nvoice].PFMCoarseDetune, pars.VoicePar[nvoice].PFMDetune); - //Get the voice's oscil or external's voice oscil int vc = nvoice; if(pars.VoicePar[nvoice].Pextoscil != -1) vc = pars.VoicePar[nvoice].Pextoscil; if(!pars.GlobalPar.Hrandgrouping) - pars.VoicePar[vc].OscilSmp->newrandseed(prng()); + pars.VoicePar[vc].OscilSmp->newrandseed(getRandomUint()); pars.VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp, getvoicebasefreq(nvoice), @@ -631,20 +635,20 @@ nvoice), pars.VoicePar[nvoice].PFMVolumeDamp / 64.0f); NoteVoicePar[nvoice].FMVolume = - (expf(pars.VoicePar[nvoice].PFMVolume / 127.0f + (expf(pars.VoicePar[nvoice].FMvolume / 100.0f * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; break; case FREQ_MOD: NoteVoicePar[nvoice].FMVolume = - (expf(pars.VoicePar[nvoice].PFMVolume / 127.0f + (expf(pars.VoicePar[nvoice].FMvolume / 100.0f * FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f; break; default: if(fmvoldamp > 1.0f) fmvoldamp = 1.0f; NoteVoicePar[nvoice].FMVolume = - pars.VoicePar[nvoice].PFMVolume - / 127.0f * fmvoldamp; + pars.VoicePar[nvoice].FMvolume + / 100.0f * fmvoldamp; } //Voice's modulator velocity sensing @@ -657,7 +661,6 @@ * logf(50.0f)) - 1.0f) / synth.buffersize_f / 10.0f * synth.samplerate_f); } - /// initparameters(); /////////////// @@ -666,17 +669,11 @@ int tmp[NUM_VOICES]; NoteGlobalPar.Volume = 4.0f - * powf(0.1f, 3.0f - * (1.0f - pars.GlobalPar.PVolume - / 96.0f)) //-60 dB .. 0 dB + * powf(10.0f, pars.GlobalPar.Volume / 20.0) //-60 dB .. 20 dB * VelF( velocity, pars.GlobalPar.PAmpVelocityScaleFunction); //velocity sensing - globalnewamplitude = NoteGlobalPar.Volume - * NoteGlobalPar.AmpEnvelope->envout_dB() - * NoteGlobalPar.AmpLfo->amplfoout(); - { auto *filter = NoteGlobalPar.Filter; filter->updateSense(velocity, pars.GlobalPar.PFilterVelocityScale, @@ -707,9 +704,9 @@ if(pars.VoicePar[nvoice].PVolumeminus != 0) NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; - if(pars.VoicePar[nvoice].PPanning == 0) - NoteVoicePar[nvoice].Panning = RND; // random panning - else + if(pars.VoicePar[nvoice].PPanning == 0) { + NoteVoicePar[nvoice].Panning = getRandomFloat(); + } else NoteVoicePar[nvoice].Panning = pars.VoicePar[nvoice].PPanning / 128.0f; @@ -734,7 +731,7 @@ && (NoteVoicePar[nvoice].FMVoice < 0)) { pars.VoicePar[nvoice].FMSmp->newrandseed(prng()); - //Perform Anti-aliasing only on MORPH or RING MODULATION + //Perform Anti-aliasing only on MIX or RING MODULATION int vc = nvoice; if(pars.VoicePar[nvoice].PextFMoscil != -1) @@ -866,9 +863,9 @@ if(param.PVolumeminus) vce.Volume = -vce.Volume; - if(param.PPanning == 0) - vce.Panning = RND; // random panning - else + if(param.PPanning == 0) { + vce.Panning = getRandomFloat(); + } else vce.Panning = param.PPanning / 128.0f; newamplitude[nvoice] = 1.0f; @@ -924,7 +921,7 @@ param.FMSmp->newrandseed(prng()); vce.FMSmp = memory.valloc(synth.oscilsize + OSCIL_SMP_EXTRA_SAMPLES); - //Perform Anti-aliasing only on MORPH or RING MODULATION + //Perform Anti-aliasing only on MIX or RING MODULATION int vc = nvoice; if(param.PextFMoscil != -1) @@ -932,7 +929,7 @@ float tmp = 1.0f; if((pars.VoicePar[vc].FMSmp->Padaptiveharmonics != 0) - || (vce.FMEnabled == MORPH) + || (vce.FMEnabled == MIX) || (vce.FMEnabled == RING_MOD)) tmp = getFMvoicebasefreq(nvoice); @@ -1072,9 +1069,7 @@ float fixedfreq = 440.0f; int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET; if(fixedfreqET != 0) { //if the frequency varies according the keyboard note - float tmp = - (midinote - - 69.0f) / 12.0f + float tmp = (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) fixedfreq *= powf(2.0f, tmp); @@ -1305,9 +1300,9 @@ }; */ /* - * Computes the Oscillator (Morphing) + * Computes the Oscillator (Mixing) */ -inline void ADnote::ComputeVoiceOscillatorMorph(int nvoice) +inline void ADnote::ComputeVoiceOscillatorMix(int nvoice) { ComputeVoiceOscillator_LinearInterpolation(nvoice); if(FMnewamplitude[nvoice] > 1.0f) @@ -1564,15 +1559,15 @@ float *tw = tmpwave_unison[k]; float *f = &pinking[nvoice][k > 0 ? 7 : 0]; for(int i = 0; i < synth.buffersize; ++i) { - float white = (RND-0.5)/4.0; - f[0] = 0.99886*f[0]+white*0.0555179; - f[1] = 0.99332*f[1]+white*0.0750759; - f[2] = 0.96900*f[2]+white*0.1538520; - f[3] = 0.86650*f[3]+white*0.3104856; - f[4] = 0.55000*f[4]+white*0.5329522; - f[5] = -0.7616*f[5]-white*0.0168980; - tw[i] = f[0]+f[1]+f[2]+f[3]+f[4]+f[5]+f[6]+white*0.5362; - f[6] = white*0.115926; + float white = (RND-0.5)/4.0; + f[0] = 0.99886*f[0]+white*0.0555179; + f[1] = 0.99332*f[1]+white*0.0750759; + f[2] = 0.96900*f[2]+white*0.1538520; + f[3] = 0.86650*f[3]+white*0.3104856; + f[4] = 0.55000*f[4]+white*0.5329522; + f[5] = -0.7616*f[5]-white*0.0168980; + tw[i] = f[0]+f[1]+f[2]+f[3]+f[4]+f[5]+f[6]+white*0.5362; + f[6] = white*0.115926; } } } @@ -1621,8 +1616,8 @@ switch (NoteVoicePar[nvoice].noisetype) { case 0: //voice mode=sound switch(NoteVoicePar[nvoice].FMEnabled) { - case MORPH: - ComputeVoiceOscillatorMorph(nvoice); + case MIX: + ComputeVoiceOscillatorMix(nvoice); break; case RING_MOD: ComputeVoiceOscillatorRingModulation(nvoice); @@ -1980,10 +1975,10 @@ AmpLfo = memory.alloc(*param.AmpLfo, basefreq, time, wm, (pre+"GlobalPar/AmpLfo/").c_str); - Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB + Volume = 4.0f * powf(10.0f, param.Volume / 20.0) //-60 dB .. 20 dB * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing - Filter = memory.alloc(*param.GlobalFilter, synth, time, memory, + Filter = memory.alloc(*param.GlobalFilter, synth, time, memory, stereo, basefreq); FilterEnvelope = memory.alloc(*param.FilterEnvelope, basefreq, @@ -1993,7 +1988,7 @@ Filter->addMod(*FilterEnvelope); Filter->addMod(*FilterLfo); - + { Filter->updateSense(velocity, param.PFilterVelocityScale, param.PFilterVelocityScaleFunction); diff -Nru zynaddsubfx-3.0.3/src/Synth/ADnote.h zynaddsubfx-3.0.4/src/Synth/ADnote.h --- zynaddsubfx-3.0.3/src/Synth/ADnote.h 2017-10-01 19:33:45.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/ADnote.h 2019-02-23 15:43:37.000000000 +0000 @@ -86,9 +86,9 @@ * Affects tmpwave_unison and updates oscposhi/oscposlo * @todo remove this declaration if it is commented out*/ inline void ComputeVoiceOscillator_CubicInterpolation(int nvoice); - /**Computes the Oscillator samples with morphing. + /**Computes the Oscillator samples with mixing. * updates tmpwave_unison*/ - inline void ComputeVoiceOscillatorMorph(int nvoice); + inline void ComputeVoiceOscillatorMix(int nvoice); /**Computes the Ring Modulated Oscillator.*/ inline void ComputeVoiceOscillatorRingModulation(int nvoice); /**Computes the Frequency Modulated Oscillator. @@ -110,7 +110,7 @@ //GLOBALS ADnoteParameters &pars; unsigned char stereo; //if the note is stereo (allows note Panning) - int midinote; + float note_log2_freq; float velocity, basefreq; ONOFFTYPE NoteEnabled; @@ -255,8 +255,8 @@ /* INTERNAL VALUES OF THE NOTE AND OF THE VOICES */ /********************************************************/ - //pinking filter (Paul Kellet) - float pinking[NUM_VOICES][14]; + //pinking filter (Paul Kellet) + float pinking[NUM_VOICES][14]; //the size of unison for a single voice int unison_size[NUM_VOICES]; diff -Nru zynaddsubfx-3.0.3/src/Synth/Envelope.cpp zynaddsubfx-3.0.4/src/Synth/Envelope.cpp --- zynaddsubfx-3.0.3/src/Synth/Envelope.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/Envelope.cpp 2018-03-11 15:03:35.000000000 +0000 @@ -32,7 +32,7 @@ if(!pars.Pfreemode) pars.converttofree(); - int mode = pars.Envmode; + mode = pars.Envmode; //for amplitude envelopes if((mode == 1) && !linearenvelope) @@ -101,26 +101,54 @@ envfinish = true; } +void Envelope::watch(float time, float value) +{ + float pos[2]; + float factor1; + float factor2; + pos[0] = time; + switch(mode) { + case 2: + pos[1] = 1 - value / -40.f; + watchOut(pos, 2); + break; + case 3: + factor1 = log(value/100. + 1.) / (6. * log(2)); + factor2 = log(1. - value/100.) / (6. * log(2)); + pos[1] = ((0.5 * factor1) >= 0) ? (0.5 * factor1 + 0.5) : (0.5 - factor2 * 0.5); + watchOut(pos, 2); + break; + case 4: + pos[1] = (value + 6.) / 12.f; + watchOut(pos, 2); + break; + case 5: + pos[1] = (value + 10.) / 20.f; + watchOut(pos, 2); + break; + default: + pos[1] = value; + watchOut(pos, 2); + } +} + /* * Envelope Output */ float Envelope::envout(bool doWatch) { float out; - if(envfinish) { //if the envelope is finished envoutval = envval[envpoints - 1]; if(doWatch) { - float pos[2] = {(float)envpoints - 1, envoutval}; - watchOut(pos, 2); + watch(envpoints - 1, envoutval); } return envoutval; } if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now envoutval = envval[envsustain]; if(doWatch) { - float pos[2] = {(float)envsustain, envoutval}; - watchOut(pos, 2); + watch(envsustain, envoutval); } return envoutval; } @@ -144,8 +172,7 @@ } if(doWatch) { - float pos[2] = {(float)tmp + t, envoutval}; - watchOut(pos, 2); + watch(tmp + t, envoutval); } return out; @@ -169,8 +196,7 @@ envoutval = out; if(doWatch) { - float pos[2] = {(float)currentpoint + t, envoutval}; - watchOut(pos, 2); + watch(currentpoint + t, envoutval); } return out; } @@ -201,13 +227,13 @@ envoutval = EnvelopeParams::env_rap2dB(out); else envoutval = MIN_ENVELOPE_DB; + out = envoutval; } else - out = EnvelopeParams::env_dB2rap(envout(false)); + out = envout(false); - float pos[2] = {(float)currentpoint + t, out}; - watchOut(pos, 2); + watch(currentpoint + t, out); + return EnvelopeParams::env_dB2rap(out); - return out; } bool Envelope::finished() const diff -Nru zynaddsubfx-3.0.3/src/Synth/Envelope.h zynaddsubfx-3.0.4/src/Synth/Envelope.h --- zynaddsubfx-3.0.3/src/Synth/Envelope.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/Envelope.h 2018-03-11 15:01:52.000000000 +0000 @@ -23,7 +23,6 @@ class Envelope { public: - /**Constructor*/ Envelope(class EnvelopeParams &pars, float basefreq, float dt, WatchManager *m=0, const char *watch_prefix=0); @@ -37,6 +36,8 @@ /**Determines the status of the Envelope * @return returns 1 if the envelope is finished*/ bool finished(void) const; + void watch(float time, float value); + private: int envpoints; int envsustain; //"-1" means disabled @@ -44,6 +45,7 @@ float envval[MAX_ENVELOPE_POINTS]; // [0.0f .. 1.0f] float envstretch; int linearenvelope; + int mode; int currentpoint; //current envelope point (starts from 1) bool forcedrelease; diff -Nru zynaddsubfx-3.0.3/src/Synth/OscilGen.cpp zynaddsubfx-3.0.4/src/Synth/OscilGen.cpp --- zynaddsubfx-3.0.3/src/Synth/OscilGen.cpp 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/OscilGen.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -793,7 +793,7 @@ } else for(int i = 0; i < synth.oscilsize / 2 - 1; ++i) { - int oldh = i + ::abs(harmonicshift); + int oldh = i + ::abs(harmonicshift); if(oldh >= (synth.oscilsize / 2 - 1)) h = 0.0f; else { @@ -1045,6 +1045,9 @@ fft_t *input = freqHz > 0.0f ? oscilFFTfreqs : pendingfreqs; + unsigned int realrnd = prng(); + sprng(randseed); + int outpos = (int)((RND * 2.0f - 1.0f) * synth.oscilsize_f * (Prand - 64.0f) / 64.0f); @@ -1090,8 +1093,7 @@ //Harmonic Amplitude Randomness if((freqHz > 0.1f) && (!ADvsPAD)) { - unsigned int realrnd = prng(); - sprng(randseed); + float power = Pamprandpower / 127.0f; float normalize = 1.0f / (1.2f - power); switch(Pamprandtype) { @@ -1110,7 +1112,6 @@ * normalize; break; } - sprng(realrnd + 1); } if((freqHz > 0.1f) && (resonance != 0)) @@ -1127,6 +1128,8 @@ smps[i] *= 0.25f; //correct the amplitude } + sprng(realrnd + 1); + if(Prand < 64) return outpos; else diff -Nru zynaddsubfx-3.0.3/src/Synth/PADnote.cpp zynaddsubfx-3.0.4/src/Synth/PADnote.cpp --- zynaddsubfx-3.0.3/src/Synth/PADnote.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/PADnote.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -20,6 +20,7 @@ #include "../Params/Controller.h" #include "../Params/FilterParams.h" #include "../Containers/ScratchString.h" +#include "../Containers/NotePool.h" #include "../Misc/Util.h" namespace zyn { @@ -34,14 +35,14 @@ NoteGlobalPar.FilterLfo = nullptr; firsttime = true; - setup(pars.frequency, pars.velocity, pars.portamento, pars.note, false, wm, prefix); + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, false, wm, prefix); } void PADnote::setup(float freq, float velocity_, int portamento_, - int midinote, - bool legato, + float note_log2_freq, + bool legato, WatchManager *wm, const char *prefix) { @@ -57,8 +58,7 @@ int fixedfreqET = pars.PfixedfreqET; if(fixedfreqET != 0) { //if the frequency varies according the keyboard note float tmp = - (midinote - - 69.0f) / 12.0f + (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) basefreq *= powf(2.0f, tmp); @@ -73,7 +73,7 @@ BendAdjust = BendAdj / 24.0f; float offset_val = (pars.POffsetHz - 64)/64.0f; OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val))); - firsttime = true; + if(!legato) firsttime = true; realfreq = basefreq; if(!legato) NoteGlobalPar.Detune = getdetune(pars.PDetuneType, pars.PCoarseDetune, @@ -156,11 +156,13 @@ * powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)) //-60 dB .. 0 dB * VelF(velocity, pars.PAmpVelocityScaleFunction); //velocity sensing - NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output - globaloldamplitude = globalnewamplitude = NoteGlobalPar.Volume - * NoteGlobalPar.AmpEnvelope-> - envout_dB() - * NoteGlobalPar.AmpLfo->amplfoout(); + if (!legato) { + NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output + globaloldamplitude = globalnewamplitude = NoteGlobalPar.Volume + * NoteGlobalPar.AmpEnvelope-> + envout_dB() + * NoteGlobalPar.AmpLfo->amplfoout(); + } if(!legato) { ScratchString pre = prefix; @@ -194,8 +196,8 @@ SynthNote *PADnote::cloneLegato(void) { - SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - (bool)portamento, legato.param.midinote, true}; + SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, + (bool)portamento, legato.param.note_log2_freq, true, legato.param.seed}; return memory.alloc(&pars, sp, interpolation); } @@ -205,7 +207,7 @@ if(legato.update(pars)) return; - setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote, true); + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, true); } diff -Nru zynaddsubfx-3.0.3/src/Synth/PADnote.h zynaddsubfx-3.0.4/src/Synth/PADnote.h --- zynaddsubfx-3.0.3/src/Synth/PADnote.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/PADnote.h 2019-02-23 15:43:37.000000000 +0000 @@ -38,7 +38,7 @@ void releasekey(); private: void setup(float freq, float velocity, int portamento_, - int midinote, bool legato = false, WatchManager *wm=0, const char *prefix=0); + float note_log2_freq, bool legato = false, WatchManager *wm=0, const char *prefix=0); void fadein(float *smps); void computecurrentparameters(); bool finished_; diff -Nru zynaddsubfx-3.0.3/src/Synth/Resonance.cpp zynaddsubfx-3.0.4/src/Synth/Resonance.cpp --- zynaddsubfx-3.0.3/src/Synth/Resonance.cpp 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/Resonance.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -33,7 +33,7 @@ "resonance enable"), rToggle(Pprotectthefundamental, rShort("p.fund."), rDefault(false), "Disable resonance filter on first harmonic"), - rParams(Prespoints, N_RES_POINTS, rDefaultMissing, + rParams(Prespoints, N_RES_POINTS, rDefault([64 ...]), "Resonance data points"), rParamZyn(PmaxdB, rShort("max"), rDefault(20), "how many dB the signal may be amplified"), diff -Nru zynaddsubfx-3.0.3/src/Synth/SUBnote.cpp zynaddsubfx-3.0.4/src/Synth/SUBnote.cpp --- zynaddsubfx-3.0.3/src/Synth/SUBnote.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/SUBnote.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -21,6 +21,7 @@ #include "Envelope.h" #include "ModFilter.h" #include "../Containers/ScratchString.h" +#include "../Containers/NotePool.h" #include "../Params/Controller.h" #include "../Params/SUBnoteParameters.h" #include "../Params/FilterParams.h" @@ -34,7 +35,8 @@ namespace zyn { -SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) +SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars, WatchManager *wm, const char *prefix + ) :SynthNote(spars), pars(*parameters), AmpEnvelope(nullptr), FreqEnvelope(nullptr), @@ -42,10 +44,9 @@ GlobalFilter(nullptr), GlobalFilterEnvelope(nullptr), NoteEnabled(true), - lfilter(nullptr), rfilter(nullptr), - wm(nullptr) + lfilter(nullptr), rfilter(nullptr) { - setup(spars.frequency, spars.velocity, spars.portamento, spars.note); + setup(spars.frequency, spars.velocity, spars.portamento, spars.note_log2_freq, false, wm, prefix); } float SUBnote::setupFilters(int *pos, bool automation) @@ -90,8 +91,10 @@ void SUBnote::setup(float freq, float velocity, int portamento_, - int midinote, - bool legato) + float note_log2_freq, + bool legato, + WatchManager *wm, + const char *prefix) { this->velocity = velocity; portamento = portamento_; @@ -117,7 +120,7 @@ basefreq = 440.0f; int fixedfreqET = pars.PfixedfreqET; if(fixedfreqET) { //if the frequency varies according the keyboard note - float tmp = (midinote - 69.0f) / 12.0f + float tmp = (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) basefreq *= powf(2.0f, tmp); @@ -166,18 +169,17 @@ } //how much the amplitude is normalised (because the harmonics) - float reduceamp = setupFilters(pos, false); + float reduceamp = setupFilters(pos, legato); oldreduceamp = reduceamp; - volume /= reduceamp; oldpitchwheel = 0; oldbandwidth = 64; if(!legato) { //normal note if(pars.Pfixedfreq == 0) - initparameters(basefreq, wm); + initparameters(basefreq, wm, prefix); else - initparameters(basefreq / 440.0f * freq, wm); + initparameters(basefreq / 440.0f * freq, wm, prefix); } else { if(pars.Pfixedfreq == 0) @@ -195,7 +197,7 @@ SynthNote *SUBnote::cloneLegato(void) { SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - portamento, legato.param.midinote, true}; + portamento, legato.param.note_log2_freq, true, legato.param.seed}; return memory.alloc(&pars, sp); } @@ -206,8 +208,8 @@ return; try { - setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote, - true); + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, + true, wm); } catch (std::bad_alloc &ba) { std::cerr << "failed to set legato note parameter in SUBnote: " << ba.what() << std::endl; } @@ -353,10 +355,9 @@ /* * Init Parameters */ -void SUBnote::initparameters(float freq, WatchManager *wm) +void SUBnote::initparameters(float freq, WatchManager *wm, const char *prefix) { - //TODO populate this base string - ScratchString pre; + ScratchString pre = prefix; AmpEnvelope = memory.alloc(*pars.AmpEnvelope, freq, synth.dt(), wm, (pre+"AmpEnvelope/").c_str); diff -Nru zynaddsubfx-3.0.3/src/Synth/SUBnote.h zynaddsubfx-3.0.4/src/Synth/SUBnote.h --- zynaddsubfx-3.0.3/src/Synth/SUBnote.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/SUBnote.h 2019-02-23 15:43:37.000000000 +0000 @@ -22,7 +22,8 @@ class SUBnote:public SynthNote { public: - SUBnote(const SUBnoteParameters *parameters, SynthParams &pars); + SUBnote(const SUBnoteParameters *parameters, SynthParams &pars, + WatchManager *wm = 0, const char *prefix = 0); ~SUBnote(); SynthNote *cloneLegato(void); @@ -37,15 +38,15 @@ void setup(float freq, float velocity, int portamento_, - int midinote, - bool legato = false); + float note_log2_freq, + bool legato = false, WatchManager *wm = 0, const char *prefix = 0); float setupFilters(int *pos, bool automation); void computecurrentparameters(); /* * Initialize envelopes and global filter * calls computercurrentparameters() */ - void initparameters(float freq, WatchManager *wm); + void initparameters(float freq, WatchManager *wm, const char *prefix); void KillNote(); const SUBnoteParameters &pars; diff -Nru zynaddsubfx-3.0.3/src/Synth/SynthNote.cpp zynaddsubfx-3.0.4/src/Synth/SynthNote.cpp --- zynaddsubfx-3.0.3/src/Synth/SynthNote.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/SynthNote.cpp 2019-02-23 15:43:37.000000000 +0000 @@ -10,6 +10,7 @@ of the License, or (at your option) any later version. */ #include "SynthNote.h" +#include "../Misc/Util.h" #include "../globals.h" #include #include @@ -20,11 +21,11 @@ SynthNote::SynthNote(SynthParams &pars) :memory(pars.memory), legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, - pars.note, pars.quiet), ctl(pars.ctl), synth(pars.synth), time(pars.time) + pars.note_log2_freq, pars.quiet, pars.seed), ctl(pars.ctl), synth(pars.synth), time(pars.time) {} SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, - int note, bool quiet) + float note_log2_freq, bool quiet, prng_t seed) :synth(synth_) { // Initialise some legato-specific vars @@ -37,7 +38,8 @@ param.freq = freq; param.vel = vel; param.portamento = port; - param.midinote = note; + param.note_log2_freq = note_log2_freq; + param.seed = seed; lastfreq = 0.0f; silent = quiet; } @@ -51,7 +53,7 @@ param.freq = pars.frequency; param.vel = pars.velocity; param.portamento = pars.portamento; - param.midinote = pars.midinote; + param.note_log2_freq = pars.note_log2_freq; if(msg == LM_Norm) { if(silent) { fade.m = 0.0f; @@ -90,7 +92,7 @@ decounter = -10; msg = LM_ToNorm; LegatoParams pars{param.freq, param.vel, param.portamento, - param.midinote, false}; + param.note_log2_freq, false, param.seed}; note.legatonote(pars); break; } @@ -132,7 +134,7 @@ //previous freq during the fadeout. float catchupfreq = param.freq * (param.freq / lastfreq); LegatoParams pars{catchupfreq, param.vel, param.portamento, - param.midinote, false}; + param.note_log2_freq, false, param.seed}; note.legatonote(pars); break; } @@ -152,7 +154,7 @@ void SynthNote::setVelocity(float velocity_) { legato.setSilent(true); //Let legato.update(...) returns 0. LegatoParams pars{legato.getFreq(), velocity_, - legato.getPortamento(), legato.getMidinote(), true}; + legato.getPortamento(), legato.getNoteLog2Freq(), true, legato.getSeed()}; try { legatonote(pars); } catch (std::bad_alloc &ba) { @@ -161,4 +163,13 @@ legato.setDecounter(0); //avoid chopping sound due fade-in } +float SynthNote::getRandomFloat() { + return (getRandomUint() / (INT32_MAX * 1.0f)); +} + +prng_t SynthNote::getRandomUint() { + current_prng_state = prng_r(current_prng_state); + return current_prng_state; +} + } diff -Nru zynaddsubfx-3.0.3/src/Synth/SynthNote.h zynaddsubfx-3.0.4/src/Synth/SynthNote.h --- zynaddsubfx-3.0.3/src/Synth/SynthNote.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/SynthNote.h 2019-02-23 15:43:37.000000000 +0000 @@ -13,6 +13,8 @@ #ifndef SYNTH_NOTE_H #define SYNTH_NOTE_H #include "../globals.h" +#include "../Misc/Util.h" +#include "../Containers/NotePool.h" namespace zyn { @@ -27,8 +29,9 @@ float frequency; //Note base frequency float velocity; //Velocity of the Note bool portamento;//True if portamento is used for this note - int note; //Integer value of the note + float note_log2_freq; //Floating point value of the note bool quiet; //Initial output condition for legato notes + prng_t seed; //Random seed }; struct LegatoParams @@ -36,8 +39,9 @@ float frequency; float velocity; bool portamento; - int midinote; + float note_log2_freq; //Floating point value of the note bool externcall; + prng_t seed; }; class SynthNote @@ -68,6 +72,10 @@ /* For polyphonic aftertouch needed */ void setVelocity(float velocity_); + /* Random numbers with own seed */ + float getRandomFloat(); + prng_t getRandomUint(); + //Realtime Safe Memory Allocator For notes class Allocator &memory; protected: @@ -76,7 +84,7 @@ { public: Legato(const SYNTH_T &synth_, float freq, float vel, int port, - int note, bool quiet); + float note_log2_freq, bool quiet, prng_t seed); void apply(SynthNote ¬e, float *outl, float *outr); int update(LegatoParams pars); @@ -92,9 +100,10 @@ } fade; public: struct { // Note parameters - float freq, vel; - bool portamento; - int midinote; + float freq, vel; + bool portamento; + float note_log2_freq; + prng_t seed; } param; const SYNTH_T &synth; @@ -102,11 +111,14 @@ float getFreq() {return param.freq; } float getVelocity() {return param.vel; } bool getPortamento() {return param.portamento; } - int getMidinote() {return param.midinote; } + float getNoteLog2Freq() {return param.note_log2_freq; } + prng_t getSeed() {return param.seed;} void setSilent(bool silent_) {silent = silent_; } void setDecounter(int decounter_) {decounter = decounter_; } } legato; + prng_t initial_seed; + prng_t current_prng_state; const Controller &ctl; const SYNTH_T &synth; const AbsTime &time; diff -Nru zynaddsubfx-3.0.3/src/Synth/WatchPoint.cpp zynaddsubfx-3.0.4/src/Synth/WatchPoint.cpp --- zynaddsubfx-3.0.3/src/Synth/WatchPoint.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Synth/WatchPoint.cpp 2018-06-09 14:45:00.000000000 +0000 @@ -20,6 +20,7 @@ */ #include "WatchPoint.h" +#include "../Misc/Util.h" #include #include @@ -30,9 +31,9 @@ { identity[0] = 0; if(prefix) - strncpy(identity, prefix, 128); + fast_strcpy(identity, prefix, sizeof(identity)); if(id) - strncat(identity, id, 128); + strncat(identity, id, sizeof(identity)); } bool WatchPoint::is_active(void) @@ -50,7 +51,7 @@ return false; } - + FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id) :WatchPoint(ref, prefix, id) {} @@ -58,7 +59,7 @@ VecWatchPoint::VecWatchPoint(WatchManager *ref, const char *prefix, const char *id) :WatchPoint(ref, prefix, id) {} - + WatchManager::WatchManager(thrlnk *link) :write_back(link), new_active(false) { @@ -67,7 +68,7 @@ memset(data_list, 0, sizeof(data_list)); memset(deactivate, 0, sizeof(deactivate)); } - + void WatchManager::add_watch(const char *id) { //Don't add duplicate watchs @@ -78,7 +79,7 @@ //Apply to a free slot for(int i=0; ibuffersize; - TS_ASSERT_DELTA(outL[255], 0.254609f, 0.0001f); + TS_ASSERT_DELTA(outL[255], 0.2555f, 0.0001f); note->releasekey(); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.102197f, 0.0001f); + TS_ASSERT_DELTA(outL[255], -0.4688f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.111261f, 0.0001f); + TS_ASSERT_DELTA(outL[255], 0.0613f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.021375f, 0.0001f); + TS_ASSERT_DELTA(outL[255], 0.0971f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], 0.149149f, 0.0001f); + TS_ASSERT_DELTA(outL[255], -0.0901f, 0.0001f); while(!note->finished()) { note->noteout(outL, outR); diff -Nru zynaddsubfx-3.0.3/src/Tests/check-ports.rb zynaddsubfx-3.0.4/src/Tests/check-ports.rb --- zynaddsubfx-3.0.3/src/Tests/check-ports.rb 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/check-ports.rb 2018-06-09 14:45:00.000000000 +0000 @@ -0,0 +1,16 @@ +#!/usr/bin/ruby + +require 'open3' + +# start zyn, grep the lo server port, and connect the port checker to it +Open3.popen3(Dir.pwd + "/../zynaddsubfx -O null --no-gui") do |stdin, stdout, stderr, wait_thr| + pid = wait_thr[:pid] + while line=stderr.gets do + # print "line: " + line; + if /^lo server running on (\d+)$/.match(line) then + rval = system(Dir.pwd + "/../../rtosc/port-checker 'osc.udp://localhost:" + $1 + "/'") + Process.kill("KILL", pid) + exit rval + end + end +end diff -Nru zynaddsubfx-3.0.3/src/Tests/CMakeLists.txt zynaddsubfx-3.0.4/src/Tests/CMakeLists.txt --- zynaddsubfx-3.0.3/src/Tests/CMakeLists.txt 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/CMakeLists.txt 2019-02-23 15:43:37.000000000 +0000 @@ -1,3 +1,8 @@ +function(cp_script script_name) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${script_name} + ${CMAKE_CURRENT_BINARY_DIR}/${script_name} COPYONLY) +endfunction() + #for tests looking for files stored in the source dir add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") @@ -64,13 +69,19 @@ add_executable(ins-test InstrumentStats.cpp) target_link_libraries(ins-test ${test_lib} rt) +if(LIBLO_FOUND) + cp_script(check-ports.rb) +# Currently fails due to zynaddsubfx issues? +# add_test(PortChecker check-ports.rb) +endif() + add_executable(save-osc SaveOSC.cpp) target_link_libraries(save-osc zynaddsubfx_core zynaddsubfx_nio zynaddsubfx_gui_bridge ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}) -#this will be replace with a for loop when the code will get more stable: -add_test(SaveOsc save-osc ../../../instruments/examples/Arpeggio\ 1.xmz) +#this will be replaced with a for loop when the code will get more stable: +add_test(SaveOsc save-osc ${CMAKE_CURRENT_SOURCE_DIR}/../../instruments/examples/Arpeggio\ 1.xmz) #message(STATUS "Plugin Test ${GUI_LIBRARIES} ${NIO_LIBRARIES} ${AUDIO_LIBRARIES}") diff -Nru zynaddsubfx-3.0.3/src/Tests/guitar-adnote.xmz zynaddsubfx-3.0.4/src/Tests/guitar-adnote.xmz --- zynaddsubfx-3.0.3/src/Tests/guitar-adnote.xmz 2017-10-15 01:55:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/guitar-adnote.xmz 2019-03-10 16:33:26.000000000 +0000 @@ -2,7 +2,7 @@ +version-revision="4" ZynAddSubFX-author="Nasca Octavian Paul"> @@ -66,7 +66,7 @@ - + @@ -349,7 +349,7 @@ - + @@ -764,7 +764,7 @@ - + diff -Nru zynaddsubfx-3.0.3/src/Tests/MemoryStressTest.h zynaddsubfx-3.0.4/src/Tests/MemoryStressTest.h --- zynaddsubfx-3.0.3/src/Tests/MemoryStressTest.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/MemoryStressTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -86,7 +86,7 @@ unsigned char testnote = 42; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; std::vector notes; diff -Nru zynaddsubfx-3.0.3/src/Tests/MessageTest.h zynaddsubfx-3.0.4/src/Tests/MessageTest.h --- zynaddsubfx-3.0.3/src/Tests/MessageTest.h 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/MessageTest.h 2018-03-11 15:01:27.000000000 +0000 @@ -303,10 +303,10 @@ while(ms->bToU->hasNext()) { const char *msg = ms->bToU->read(); if(state == 0) { - TS_ASSERT_EQUALS(rtosc_narguments(msg), 0); + TS_ASSERT_EQUALS(rtosc_narguments(msg), 0U); state = 1; } else if(state == 1) { - TS_ASSERT_EQUALS(rtosc_narguments(msg), 1); + TS_ASSERT_EQUALS(rtosc_narguments(msg), 1U); value = rtosc_argument(msg, 0).i; state = 2; } else if(state == 2) { diff -Nru zynaddsubfx-3.0.3/src/Tests/MicrotonalTest.h zynaddsubfx-3.0.4/src/Tests/MicrotonalTest.h --- zynaddsubfx-3.0.3/src/Tests/MicrotonalTest.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/MicrotonalTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -62,7 +62,7 @@ for(int i = 0; i < 128; ++i) TS_ASSERT_EQUALS(testMicro->Pmapping[i], i); - TS_ASSERT_DELTA(testMicro->getnotefreq(19, 0), 24.4997f, 0.0001f); + TS_ASSERT_DELTA(testMicro->getnotefreq(19 / 12.0f, 0), 24.4997f, 0.0001f); } //Tests saving/loading to XML diff -Nru zynaddsubfx-3.0.3/src/Tests/OscilGenTest.h zynaddsubfx-3.0.4/src/Tests/OscilGenTest.h --- zynaddsubfx-3.0.3/src/Tests/OscilGenTest.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/OscilGenTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -86,10 +86,10 @@ void testOutput(void) { oscil->get(outL, freq); - TS_ASSERT_DELTA(outL[23], -0.044547f, 0.0001f); - TS_ASSERT_DELTA(outL[129], -0.018169f, 0.0001f); - TS_ASSERT_DELTA(outL[586], 0.045647f, 0.0001f); - TS_ASSERT_DELTA(outL[1023], -0.038334f, 0.0001f); + TS_ASSERT_DELTA(outL[23], -0.5371f, 0.0001f); + TS_ASSERT_DELTA(outL[129], 0.3613f, 0.0001f); + TS_ASSERT_DELTA(outL[586], 0.1118f, 0.0001f); + TS_ASSERT_DELTA(outL[1023], -0.6889f, 0.0001f); } void testSpectrum(void) diff -Nru zynaddsubfx-3.0.3/src/Tests/PadNoteTest.h zynaddsubfx-3.0.4/src/Tests/PadNoteTest.h --- zynaddsubfx-3.0.3/src/Tests/PadNoteTest.h 2017-10-01 13:12:57.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/PadNoteTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -104,7 +104,7 @@ //lets go with.... 50! as a nice note testnote = 50; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars_{memory, *controller, *synth, *time, freq, 120, 0, testnote, false}; + SynthParams pars_{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new PADnote(pars, pars_, interpolation); } @@ -147,7 +147,7 @@ #endif sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], 0.0660f, 0.0005f); + TS_ASSERT_DELTA(outL[255], -0.0554, 0.0005f); note->releasekey(); @@ -159,15 +159,15 @@ note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], 0.060818f, 0.0005f); + TS_ASSERT_DELTA(outL[255], -0.0331f, 0.0005f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], 0.036895f, 0.0005f); + TS_ASSERT_DELTA(outL[255], 0.0219f, 0.0005f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.006623f, 0.0001f); + TS_ASSERT_DELTA(outL[255], 0.0137f, 0.0001f); while(!note->finished()) { note->noteout(outL, outR); @@ -202,26 +202,26 @@ for(int i=8; isample[i].smp); - TS_ASSERT_DELTA(pars->sample[0].smp[0], -0.057407f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[1], -0.050704f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[2], -0.076559f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[3], -0.069974f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[4], -0.053268f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[5], -0.025702f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[6], -0.021064f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[7], 0.002593f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[8], 0.049286f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[9], 0.031929f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[10], 0.044527f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[11], 0.040447f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[12], 0.022108f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[13], 0.005787f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[14], -0.008430f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[15], -0.009642f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[16], -0.018427f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[17], -0.052831f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[18], -0.058690f, 0.0005f); - TS_ASSERT_DELTA(pars->sample[0].smp[19], -0.090954f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[0], 0.0516f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[1], 0.0845f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[2], 0.1021f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[3], 0.0919f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[4], 0.0708f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[5], 0.0414f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[6], 0.0318f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[7], 0.0217f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[8], 0.0309f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[9], 0.0584f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[10], 0.0266f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[11], 0.0436f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[12], 0.0199f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[13], 0.0505f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[14], 0.0438f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[15], 0.0024f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[16], 0.0052f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[17], -0.0180f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[18], 0.0342f, 0.0005f); + TS_ASSERT_DELTA(pars->sample[0].smp[19], 0.0051f, 0.0005f); //Verify Harmonic Input diff -Nru zynaddsubfx-3.0.3/src/Tests/SaveOSC.cpp zynaddsubfx-3.0.4/src/Tests/SaveOSC.cpp --- zynaddsubfx-3.0.3/src/Tests/SaveOSC.cpp 2017-12-05 01:00:37.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/SaveOSC.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -32,7 +32,9 @@ * Once after the loading * Twice for the temporary exchange during saving */ +#ifdef SAVE_OSC_DEBUG printf("Changing master from %p (%p) to %p...\n", master, &master, m); +#endif master = m; master->setMasterChangedCallback(__masterChangedCallback, this); } @@ -55,7 +57,9 @@ } void tearDown() { +#ifdef SAVE_OSC_DEBUG printf("Master at the end: %p\n", master); +#endif delete mw; delete synth; } @@ -112,12 +116,18 @@ if(!strcmp(msg, "/save_osc") || !strcmp(msg, "/load_xmz")) { mutex_guard guard(cb_mutex); +#ifdef SAVE_OSC_DEBUG fprintf(stderr, "Received message \"%s\".\n", msg); +#endif recent.operation = msg; recent.file = rtosc_argument(msg, 0).s; recent.stamp = rtosc_argument(msg, 1).t; recent.status = rtosc_argument(msg, 2).T; } + else if(!strcmp(msg, "/damage")) + { + // (ignore) + } else fprintf(stderr, "Unknown message \"%s\", ignoring...\n", msg); } @@ -176,7 +186,9 @@ continue; } const char *msg = master->uToB->read(); +#ifdef SAVE_OSC_DEBUG printf("Master %p: handling <%s>\n", master, msg); +#endif master->applyOscEvent(msg, false); }}); } diff -Nru zynaddsubfx-3.0.3/src/Tests/SubNoteTest.h zynaddsubfx-3.0.4/src/Tests/SubNoteTest.h --- zynaddsubfx-3.0.3/src/Tests/SubNoteTest.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/SubNoteTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -78,7 +78,7 @@ testnote = 50; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new SUBnote(defaultPreset, pars); this->pars = defaultPreset; } @@ -119,19 +119,19 @@ note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], 0.0016f, 0.0001f); + TS_ASSERT_DELTA(outL[255], 0.0029f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.0000f, 0.0001f); + TS_ASSERT_DELTA(outL[255], -0.0011f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.0013f, 0.0001f); + TS_ASSERT_DELTA(outL[255], -0.0017f, 0.0001f); note->noteout(outL, outR); sampleCount += synth->buffersize; - TS_ASSERT_DELTA(outL[255], -0.0002f, 0.0001f); + TS_ASSERT_DELTA(outL[255], -0.0005f, 0.0001f); while(!note->finished()) { note->noteout(outL, outR); diff -Nru zynaddsubfx-3.0.3/src/Tests/UnisonTest.h zynaddsubfx-3.0.4/src/Tests/UnisonTest.h --- zynaddsubfx-3.0.3/src/Tests/UnisonTest.h 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/Tests/UnisonTest.h 2019-02-23 15:43:37.000000000 +0000 @@ -23,6 +23,7 @@ #include "../Synth/ADnote.h" #include "../Synth/OscilGen.h" #include "../Params/Presets.h" +#include "../Params/FilterParams.h" #include "../DSP/FFTwrapper.h" #include "../globals.h" using namespace std; @@ -64,6 +65,9 @@ //sawtooth to make things a bit more interesting params->VoicePar[0].OscilSmp->Pcurrentbasefunc = 3; + params->GlobalPar.PFilterVelocityScale = 64; + params->GlobalPar.GlobalFilter->basefreq = 5076.203125; + controller = new Controller(*synth, time); //lets go with.... 50! as a nice note @@ -84,7 +88,7 @@ void run_test(int a, int b, int c, int d, int e, int f, float values[4]) { sprng(0); - + const int ADnote_unison_sizes[] = {1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0}; params->VoicePar[0].Unison_size = ADnote_unison_sizes[a]; @@ -94,20 +98,20 @@ params->VoicePar[0].Unison_vibratto_speed = e; params->VoicePar[0].Unison_invert_phase = f; - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new ADnote(params, pars); note->noteout(outL, outR); TS_ASSERT_DELTA(outL[80], values[0], 1e-5); - //printf("{%f,", outL[80]); + printf("\n{%f,", outL[80]); note->noteout(outL, outR); TS_ASSERT_DELTA(outR[90], values[1], 1e-5); - //printf("%f,", outR[90]); + printf("\n%f,", outR[90]); note->noteout(outL, outR); TS_ASSERT_DELTA(outL[20], values[2], 1e-5); - //printf("%f,", outL[20]); + printf("\n%f,", outL[20]); note->noteout(outL, outR); TS_ASSERT_DELTA(outR[200], values[3], 1e-5); - //printf("%f},\n", outR[200]); + printf("\n%f},\n", outR[200]); } void testUnison() { @@ -115,20 +119,20 @@ float data[][4] = { {-0.034547,0.034349,-0.000000,0.138284}, - {0.023612,-0.093842,0.000000,-0.040384}, - {-0.015980,0.001871,-0.014463,-0.000726}, - {-0.040970,-0.000275,0.000000,-0.121016}, - {0.019250,-0.045252,0.000270,0.105372}, - {-0.086575,0.001130,-0.018921,0.001329}, - {0.009203,-0.006176,0.017344,-0.003316}, - {0.029411,-0.000248,-0.112797,-0.012883}, - {0.043657,-0.014062,-0.003374,-0.071821}, - {0.007973,0.068019,-0.038900,0.047639}, - {-0.002055,0.011170,-0.058152,-0.043493}, - {-0.005298,0.000605,-0.070932,-0.005678}, - {0.025028,-0.027742,0.020985,-0.015417}, - {0.074349,0.000640,0.080613,0.066636}, - {-0.045721,0.000279,0.009819,0.032202}, + {0.016801,-0.084991,0.000000,0.009240}, + {0.020383,-0.002424,-0.012952,-0.014037}, + {-0.041653,0.002287,0.000000,-0.098181}, + {-0.009189,-0.049860,0.000268,-0.084961}, + {0.056999,-0.084627,-0.018144,0.000666}, + {-0.015588,0.003690,0.003994,0.002435}, + {0.023178,-0.024961,0.004433,-0.015144}, + {0.042007,-0.006559,-0.005887,0.083685}, + {0.007638,0.057870,-0.014244,0.041457}, + {-0.018006,-0.017846,-0.063624,-0.016378}, + {0.004914,-0.001756,-0.046715,0.015975}, + {0.004341,-0.014575,0.000560,0.050902}, + {0.000470,-0.036961,0.038622,0.031383}, + {-0.045796,0.000262,0.009858,0.031958}, }; int freq_spread[15]; diff -Nru zynaddsubfx-3.0.3/src/UI/EnvelopeFreeEdit.cpp zynaddsubfx-3.0.4/src/UI/EnvelopeFreeEdit.cpp --- zynaddsubfx-3.0.3/src/UI/EnvelopeFreeEdit.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/EnvelopeFreeEdit.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -55,12 +55,12 @@ rtosc_blob_t b = rtosc_argument(msg, 0).b; assert(b.len == MAX_ENVELOPE_POINTS); memcpy(Penvval, b.data, MAX_ENVELOPE_POINTS); - } else if(strstr(msg, "Penvval") && !strcmp(args, "c")) { + } else if(strstr(msg, "Penvval") && !strcmp(args, "i")) { const char *str = strstr(msg, "Penvval"); int id = atoi(str+7); assert(0 <= id && id < MAX_ENVELOPE_POINTS); Penvval[id] = rtosc_argument(msg, 0).i; - } else if(strstr(msg, "Penvdt") && !strcmp(args, "c")) { + } else if(strstr(msg, "Penvdt") && !strcmp(args, "i")) { const char *str = strstr(msg, "Penvdt"); int id = atoi(str+6); assert(0 <= id && id < MAX_ENVELOPE_POINTS); @@ -270,13 +270,13 @@ int ny = Penvval[lastpoint] - delta; ny = ny < 0 ? 0 : ny > 127 ? 127 : ny; Penvval[lastpoint] = ny; - oscWrite(to_s("Penvval")+to_s(lastpoint), "c", ny); + oscWrite(to_s("Penvval")+to_s(lastpoint), "i", ny); oscWrite("Penvval",""); } else if (lastpoint > 0) { int newdt = Penvdt[lastpoint] - delta; newdt = newdt < 0 ? 0 : newdt > 127 ? 127 : newdt; Penvdt[lastpoint] = newdt; - oscWrite(to_s("Penvdt")+to_s(lastpoint), "c", newdt); + oscWrite(to_s("Penvdt")+to_s(lastpoint), "i", newdt); oscWrite("Penvdt",""); } redraw(); @@ -300,7 +300,7 @@ const int newval=limit(cpval+dy, 0, 127); Penvval[currentpoint]=newval; - oscWrite(to_s("Penvval")+to_s(currentpoint), "c", newval); + oscWrite(to_s("Penvval")+to_s(currentpoint), "i", newval); oscWrite("Penvval",""); } @@ -312,7 +312,7 @@ Penvdt[currentpoint]=newdt; else Penvdt[currentpoint]=0; - oscWrite(to_s("Penvdt")+to_s(currentpoint), "c", newdt); + oscWrite(to_s("Penvdt")+to_s(currentpoint), "i", newdt); oscWrite("Penvdt",""); } diff -Nru zynaddsubfx-3.0.3/src/UI/Fl_Osc_Interface.h zynaddsubfx-3.0.4/src/UI/Fl_Osc_Interface.h --- zynaddsubfx-3.0.3/src/UI/Fl_Osc_Interface.h 2016-03-07 17:41:14.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/Fl_Osc_Interface.h 2019-02-23 15:43:37.000000000 +0000 @@ -26,29 +26,29 @@ virtual void OSC_value(const char *){}; //labeled forwarding methods - virtual void OSC_value(float x, const char *){}; - virtual void OSC_value(bool x, const char *){}; - virtual void OSC_value(int x, const char *){}; - virtual void OSC_value(char x, const char *){}; - virtual void OSC_value(unsigned x, void *v, const char *){}; - virtual void OSC_value(const char *x, const char *){}; + virtual void OSC_value(float x, const char *){(void)x;}; + virtual void OSC_value(bool x, const char *){(void)x;}; + virtual void OSC_value(int x, const char *){(void)x;}; + virtual void OSC_value(char x, const char *){(void)x;}; + virtual void OSC_value(unsigned x, void *, const char *){(void)x;}; + virtual void OSC_value(const char *x, const char *){(void)x;}; //Raw messages virtual void OSC_raw(const char *){}; //Widget methods - void oscWrite(std::string path, const char *args, ...){}; - void oscWrite(std::string path){}; - void oscRegister(const char *path){}; + void oscWrite(std::string path, const char *args, ...){(void)path;(void)args;}; + void oscWrite(std::string path){(void)path;}; + void oscRegister(const char *path){(void)path;}; //Forces an update of parameters as they have become stale somehow virtual void update(void){}; //Smoothly change the base path - virtual void rebase(std::string new_base){}; - void oscMove(std::string new_ext){}; + virtual void rebase(std::string new_base){(void)new_base;}; + void oscMove(std::string new_ext){(void)new_ext;}; //Explict version for weirdly routed controls - void oscMove(std::string old_loc, std::string new_loc){}; + void oscMove(std::string old_loc, std::string new_loc){(void)old_loc;(void)new_loc;}; }; #endif diff -Nru zynaddsubfx-3.0.3/src/UI/Fl_Osc_Tree.H zynaddsubfx-3.0.4/src/UI/Fl_Osc_Tree.H --- zynaddsubfx-3.0.3/src/UI/Fl_Osc_Tree.H 2017-11-19 17:28:18.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/Fl_Osc_Tree.H 2018-06-09 14:45:00.000000000 +0000 @@ -10,6 +10,7 @@ of the License, or (at your option) any later version. */ #pragma once +#include #include #include "Fl_Osc_Interface.h" #include @@ -77,7 +78,8 @@ } else { char tmpa[1024]; char tmpb[1024]; - strncpy(tmpa, path.c_str(), 1024); + assert(path.length() < 1024); + strncpy(tmpa, path.c_str(), sizeof(tmpa)); char *pound = index(tmpa, '#'); int N = atoi(pound+1); *pound = 0; diff -Nru zynaddsubfx-3.0.3/src/UI/Fl_Resonance_Graph.cpp zynaddsubfx-3.0.4/src/UI/Fl_Resonance_Graph.cpp --- zynaddsubfx-3.0.3/src/UI/Fl_Resonance_Graph.cpp 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/Fl_Resonance_Graph.cpp 2018-03-11 15:01:27.000000000 +0000 @@ -253,6 +253,6 @@ void Fl_Resonance_Graph::setPoint(int idx, int val) { Prespoints[idx] = val; - oscWrite(std::string("Prespoints")+to_s(idx), "c", val); + oscWrite(std::string("Prespoints")+to_s(idx), "i", val); redraw(); } diff -Nru zynaddsubfx-3.0.3/src/UI/MasterUI.fl zynaddsubfx-3.0.4/src/UI/MasterUI.fl --- zynaddsubfx-3.0.3/src/UI/MasterUI.fl 2017-07-19 18:31:41.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/MasterUI.fl 2019-03-10 16:34:32.000000000 +0000 @@ -2,7 +2,7 @@ version 1.0302 header_name {.h} code_name {.cc} -decl {//Copyright (c) 2002-2009 Nasca Octavian Paul - (c) 2009-2017 Mark McCurry} {private local +decl {//Copyright (c) 2002-2009 Nasca Octavian Paul - (c) 2009-2019 Mark McCurry} {private local } decl {//License: GNU GPL version 2 or later} {private local @@ -869,7 +869,7 @@ xywh {411 344 365 280} type Double hide } { Fl_Box {} { - label {Copyright (c) 2002-2009 Nasca O. PAUL, 2009-2016 Mark McCurry, and others. Please read AUTHORS.txt} + label {Copyright (c) 2002-2009 Nasca O. PAUL, 2009-2019 Mark McCurry, and others. Please read AUTHORS.txt} xywh {15 35 335 55} labeltype EMBOSSED_LABEL labelsize 15 align 208 } Fl_Box {} { diff -Nru zynaddsubfx-3.0.3/src/UI/MicrotonalUI.fl zynaddsubfx-3.0.4/src/UI/MicrotonalUI.fl --- zynaddsubfx-3.0.3/src/UI/MicrotonalUI.fl 2016-02-13 17:15:20.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/MicrotonalUI.fl 2019-02-23 15:43:37.000000000 +0000 @@ -202,14 +202,14 @@ else o->textcolor(FL_BLACK);*/ o->redraw();} - tooltip {The "A" note (the reference note for which freq. ("A" freq) is given)} xywh {173 17 65 20} labelfont 1 labelsize 10 align 129 minimum 0 maximum 127 step 1 value 69 textfont 1 textsize 10 + tooltip {The "A" note (the reference note for which freq. ("A" freq) is given, default=69)} xywh {173 17 65 20} labelfont 1 labelsize 10 align 129 minimum 0 maximum 127 step 1 value 69 textfont 1 textsize 10 code0 {o->lstep(12);} code1 {o->init("PAnote");} class Fl_Osc_Counter } Fl_Value_Input afreqinput { label {"A" Freq.} - tooltip {The freq. of "A" note (default=440.0)} xywh {118 17 45 20} labelfont 1 labelsize 10 align 1 minimum 1 maximum 20000 step 0.001 value 440 textfont 1 textsize 10 + tooltip {The freq. of "A" note (default=440.0)} xywh {110 17 61 20} labelfont 1 labelsize 10 align 1 minimum 1 maximum 20000 step 0.001 value 440 textfont 1 textsize 10 code0 {o->init("PAfreq");} class Fl_Osc_Value } diff -Nru zynaddsubfx-3.0.3/src/UI/PartUI.fl zynaddsubfx-3.0.4/src/UI/PartUI.fl --- zynaddsubfx-3.0.3/src/UI/PartUI.fl 2016-03-07 17:41:14.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/PartUI.fl 2019-02-23 15:43:37.000000000 +0000 @@ -487,8 +487,8 @@ xywh {205 37 69 15} down_box DOWN_BOX labelsize 10 } Fl_Counter bendrng { - label {PWheelB.Rng (cents)} - tooltip {Pitch Wheel Bend Range (cents)} xywh {165 15 110 20} labelsize 10 align 1 minimum -6400 maximum 6400 step 1 + label {PWheelB.Rng (% semitone)} + tooltip {Pitch Wheel Bend Range (cents of semitone)} xywh {165 15 110 20} labelsize 10 align 1 minimum -6400 maximum 6400 step 1 code0 {o->init("pitchwheel.bendrange", 'i');} code1 {o->lstep(100);} class Fl_Osc_Counter diff -Nru zynaddsubfx-3.0.3/src/UI/SUBnoteUI.fl zynaddsubfx-3.0.4/src/UI/SUBnoteUI.fl --- zynaddsubfx-3.0.3/src/UI/SUBnoteUI.fl 2015-12-22 15:24:21.000000000 +0000 +++ zynaddsubfx-3.0.4/src/UI/SUBnoteUI.fl 2018-03-11 15:01:27.000000000 +0000 @@ -49,7 +49,7 @@ Function {SUBSlider(int x,int y, int w, int h, const char *label=0) :Fl_Osc_TSlider(x,y,w,h,label)} {open } { code {} {}} - Function {OSC_value(char c)} {open return_type void + Function {OSC_value(int c)} {open return_type void } { code { value(127-c); selection_color(value() == reset_value ? 0 : 222); @@ -58,7 +58,7 @@ } { code { selection_color(value() == reset_value ? 0 : 222); - oscWrite(ext, "c", (int)(127-Fl_Slider::value())); + oscWrite(ext, "i", (int)(127-Fl_Slider::value())); if(cb_data.first) cb_data.first(this, cb_data.second); } {} @@ -332,11 +332,11 @@ label Clear callback {o->oscWrite("clear"); for (int i=1;imag->oscWrite(h[i]->mag->ext, "c", 0); - h[i]->bw->oscWrite(h[i]->bw->ext, "c", 64); + h[i]->mag->oscWrite(h[i]->mag->ext, "i", 0); + h[i]->bw->oscWrite(h[i]->bw->ext, "i", 64); }; -h[0]->mag->oscWrite(h[0]->mag->ext, "c", 127); -h[0]->bw->oscWrite(h[0]->bw->ext, "c", 64); +h[0]->mag->oscWrite(h[0]->mag->ext, "i", 127); +h[0]->bw->oscWrite(h[0]->bw->ext, "i", 64); SUBparameters->redraw();} tooltip {Clear the harmonics} xywh {445 446 70 25} box THIN_UP_BOX class Fl_Osc_Button diff -Nru zynaddsubfx-3.0.3/src/zyn-config.h.in zynaddsubfx-3.0.4/src/zyn-config.h.in --- zynaddsubfx-3.0.3/src/zyn-config.h.in 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/src/zyn-config.h.in 2019-02-23 15:43:37.000000000 +0000 @@ -0,0 +1,23 @@ +/* + ZynAddSubFX - a software synthesizer + + config.h - options configurable by CMake + Copyright (C) 2018 Johannes Lorenz + Author: Johannes Lorenz + + 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 + of the License, or (at your option) any later version. +*/ + +#ifndef ZYN_CONFIG_H_IN +#define ZYN_CONFIG_H_IN + +namespace zyn { + +constexpr const char* fusion_dir = "@ZynFusionDir@"; + +} + +#endif diff -Nru zynaddsubfx-3.0.3/src/zyn-version.h.in zynaddsubfx-3.0.4/src/zyn-version.h.in --- zynaddsubfx-3.0.3/src/zyn-version.h.in 2017-05-18 13:58:31.000000000 +0000 +++ zynaddsubfx-3.0.4/src/zyn-version.h.in 2019-02-23 15:43:37.000000000 +0000 @@ -12,8 +12,8 @@ of the License, or (at your option) any later version. */ -#ifndef VERSION_H -#define VERSION_H +#ifndef ZYN_VERSION_H +#define ZYN_VERSION_H #include diff -Nru zynaddsubfx-3.0.3/TODO-default-values.txt zynaddsubfx-3.0.4/TODO-default-values.txt --- zynaddsubfx-3.0.3/TODO-default-values.txt 2017-12-05 01:00:37.000000000 +0000 +++ zynaddsubfx-3.0.4/TODO-default-values.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -TODOs for default values: -* use b (or #?) for bundles, not a -* code for random port ("init() to random") -* remove rDefaultMissing if possible -* auto range-print -* rEnabled vs rExist: - * clarify/rename - * make a clean function? -move code: -ports.cpp => default_values.cpp -rtosc.cpp => ...cmp.cpp? -test: -* zyn fx (all presets) -* rtosc arg val maths -fix all new todos diff -Nru zynaddsubfx-3.0.3/.travis.yml zynaddsubfx-3.0.4/.travis.yml --- zynaddsubfx-3.0.3/.travis.yml 2017-09-07 18:50:36.000000000 +0000 +++ zynaddsubfx-3.0.4/.travis.yml 2019-03-10 16:16:42.000000000 +0000 @@ -3,14 +3,12 @@ compiler: - gcc +dist: xenial + before_install: - - sudo apt-get install zlib1g-dev libmxml-dev libfftw3-dev dssi-dev libfltk1.3-dev libxpm-dev + - sudo apt-get install zlib1g-dev libmxml-dev libfftw3-dev dssi-dev libfltk1.3-dev fluid libxpm-dev + - sudo apt-get install liblo-dev - sudo apt-get install --force-yes cxxtest - - wget http://downloads.sourceforge.net/liblo/liblo-0.28.tar.gz - - tar xvf liblo-0.28.tar.gz && cd liblo-0.28 - - ./configure && make && sudo make install - - sudo ldconfig - - cd .. script: diff -Nru zynaddsubfx-3.0.3/zynaddsubfx-jack-multi.desktop zynaddsubfx-3.0.4/zynaddsubfx-jack-multi.desktop --- zynaddsubfx-3.0.3/zynaddsubfx-jack-multi.desktop 1970-01-01 00:00:00.000000000 +0000 +++ zynaddsubfx-3.0.4/zynaddsubfx-jack-multi.desktop 2019-02-23 15:43:37.000000000 +0000 @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=ZynAddSubFX - Jack (multi channel) +Name[fr]=ZynAddSubFX - Jack (multi canaux) +Comment=A powerful realtime software synthesizer +Comment[fr]=Un synthétiseur logiciel temps-réel puissant +Comment[pl]=Funkcjonalny syntezator wirtualny czasu rzeczywistego +Keywords=audio;sound;jack;midi;synth;synthesizer; +Exec=zynaddsubfx -I jack -O jack-multi +Icon=zynaddsubfx +Terminal=false +Type=Application +Categories=AudioVideo;Audio;