synthv1-1.5.1/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215270627745014767 xustar0030 mtime=1793273829.969168979 30 atime=1793273829.969168979 30 ctime=1793273829.969168979 synthv1-1.5.1/CMakeLists.txt0000644000175000001440000002717115270627745014767 0ustar00rncbcuserscmake_minimum_required (VERSION 3.15) project (synthv1 VERSION 1.5.1 DESCRIPTION "an old-school polyphonic synthesizer" HOMEPAGE_URL "https://synthv1.sourceforge.io" LANGUAGES C CXX) set (PROJECT_COPYRIGHT "Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved.") set (PROJECT_DOMAIN "rncbc.org") execute_process ( COMMAND git describe --tags --dirty --abbrev=6 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT RESULT_VARIABLE GIT_DESCRIBE_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE) if (GIT_DESCRIBE_RESULT EQUAL 0) set (GIT_VERSION "${GIT_DESCRIBE_OUTPUT}") string (REGEX REPLACE "^[^0-9]+" "" GIT_VERSION "${GIT_VERSION}") string (REGEX REPLACE "-g" "git." GIT_VERSION "${GIT_VERSION}") string (REGEX REPLACE "[_|-]" "." GIT_VERSION "${GIT_VERSION}") execute_process ( COMMAND git rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_REVPARSE_OUTPUT RESULT_VARIABLE GIT_REVPARSE_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE) if (GIT_REVPARSE_RESULT EQUAL 0 AND NOT GIT_REVPARSE_OUTPUT STREQUAL "main") set (GIT_VERSION "${GIT_VERSION} [${GIT_REVPARSE_OUTPUT}]") endif () set (PROJECT_VERSION "${GIT_VERSION}") endif () if (CMAKE_BUILD_TYPE MATCHES "Debug") set (CONFIG_DEBUG 1) set (CONFIG_BUILD_TYPE "debug") else () set (CONFIG_DEBUG 0) set (CONFIG_BUILD_TYPE "release") set (CMAKE_BUILD_TYPE "Release") endif () set (CONFIG_PREFIX "${CMAKE_INSTALL_PREFIX}") include (GNUInstallDirs) set (CONFIG_BINDIR "${CONFIG_PREFIX}/${CMAKE_INSTALL_BINDIR}") set (CONFIG_LIBDIR "${CONFIG_PREFIX}/${CMAKE_INSTALL_LIBDIR}") set (CONFIG_DATADIR "${CONFIG_PREFIX}/${CMAKE_INSTALL_DATADIR}") set (CONFIG_MANDIR "${CONFIG_PREFIX}/${CMAKE_INSTALL_MANDIR}") # Enable JACK standalone build. option (CONFIG_JACK "Enable JACK stand-alone build (default=yes)" 1) # Enable JACK session support. option (CONFIG_JACK_SESSION "Enable JACK session support (default=yes)" 1) # Enable JACK MIDI support option. option (CONFIG_JACK_MIDI "Enable JACK MIDI support (default=yes)" 1) # Enable ALSA MIDI support option. option (CONFIG_ALSA_MIDI "Enable ALSA MIDI support (default=yes)" 1) # Enable LV2 plugin build. option (CONFIG_LV2 "Enable LV2 plug-in build (default=yes)" 1) if (WIN32) option (CONFIG_LV2_UI_WINDOWS "Enable LV2 plug-in Windows UI support (default=yes)" 1) # Windows install target path. set (CONFIG_WINDOWS_LV2_PATH "$ENV{SYSTEMDRIVE}/Program Files/Common Files/LV2" CACHE STRING "Specify Windows LV2 install path") else () option (CONFIG_LV2_UI_X11 "Enable LV2 plug-in X11 UI support (default=yes)" 1) endif () option (CONFIG_LV2_UI_EXTERNAL "Enable LV2 plug-in External UI support (default=yes)" 1) option (CONFIG_LV2_UI_IDLE "Enable LV2 UI Idle interface support (default=yes)" 1) option (CONFIG_LV2_UI_SHOW "Enable LV2 UI Show interface support (default=yes)" 1) option (CONFIG_LV2_UI_RESIZE "Enable LV2 UI Resize interface support (default=yes)" 1) option (CONFIG_LV2_PROGRAMS "Enable LV2 plug-in Programs support (default=yes)" 1) option (CONFIG_LV2_PATCH "Enable LV2 plug-in Patch support (default=yes)" 1) option (CONFIG_LV2_PORT_EVENT "Enable LV2 plug-in Port-event support (default=yes)" 1) option (CONFIG_LV2_PORT_CHANGE_REQUEST "Enable LV2 plug-in Port-change request support (default=yes)" 1) # Enable liblo availability. option (CONFIG_LIBLO "Enable liblo interface (default=yes)" 1) # Enable NSM support. option (CONFIG_NSM "Enable NSM support (default=yes)" 1) # Enable Qt6 build preference. option (CONFIG_QT6 "Enable Qt6 build (default=yes)" 1) # Fix for new CMAKE_REQUIRED_LIBRARIES policy. if (POLICY CMP0075) cmake_policy (SET CMP0075 NEW) endif () # Fix for CXX_VISIBILITY_PRESET policy. if (POLICY CMP0063) cmake_policy (SET CMP0063 NEW) set (CMAKE_CXX_VISIBILITY_PRESET hidden) set (CMAKE_VISIBILITY_INLINES_HIDDEN 1) endif () # Check for Qt... if (CONFIG_QT6) find_package (Qt6 QUIET) if (NOT Qt6_FOUND) set (CONFIG_QT6 0) endif () endif () if (CONFIG_QT6) find_package (QT QUIET NAMES Qt6) else () find_package (QT QUIET NAMES Qt5) endif () find_package (Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets Xml Svg) #find_package (Qt${QT_VERSION_MAJOR}LinguistTools) include (CheckIncludeFile) include (CheckIncludeFiles) include (CheckIncludeFileCXX) include (CheckFunctionExists) include (CheckLibraryExists) # Make sure we get some subtle optimizations out there... add_compile_options (-ffast-math) # Checks for header files. if (UNIX AND NOT APPLE) check_include_files ("fcntl.h;unistd.h;signal.h" HAVE_SIGNAL_H) endif () # Find package modules include (FindPkgConfig) # Check for JACK libraries. if (CONFIG_JACK) pkg_check_modules (JACK IMPORTED_TARGET jack>=0.100.0) if (JACK_FOUND) find_library (JACK_LIBRARY NAMES ${JACK_LIBRARIES} HINTS ${JACK_LIBDIR}) endif () if (JACK_LIBRARY) set (CONFIG_JACK 1) set (CMAKE_REQUIRED_LIBRARIES "${JACK_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") # Check for JACK MIDI headers availability. if (CONFIG_JACK_MIDI) check_include_file (jack/midiport.h HAVE_JACK_MIDIPORT_H) if (NOT HAVE_JACK_MIDIPORT_H) set (CONFIG_JACK_MIDI 0) endif () endif () # Check for JACK session headers availability. if (CONFIG_JACK_SESSION) check_include_file (jack/session.h HAVE_JACK_SESSION_H) if (NOT HAVE_JACK_SESSION_H) set (CONFIG_JACK_SESSION 0) endif () endif () # Check for JACK session event callback availability. if (CONFIG_JACK_SESSION) check_function_exists (jack_set_session_callback CONFIG_JACK_SESSION) endif () # Check for ALSA libraries. if (CONFIG_ALSA_MIDI) pkg_check_modules (ALSA IMPORTED_TARGET alsa) if (ALSA_FOUND) find_library (ALSA_LIBRARY NAMES ${ALSA_LIBRARIES} HINTS ${ALSA_LIBDIR}) endif () if (ALSA_LIBRARY) set (CONFIG_ALSA_MIDI 1) #set (CMAKE_REQUIRED_LIBRARIES "${ALSA_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") else () message (WARNING "*** ALSA library not found.") endif () endif () else () message (WARNING "*** JACK library not found.") set (CONFIG_JACK 0) endif () endif () if (NOT CONFIG_JACK) set (CONFIG_JACK_SESSION 0) set (CONFIG_JACK_MIDI 0) set (CONFIG_ALSA_MIDI 0) set (CONFIG_LIBLO 0) set (CONFIG_NSM 0) endif () # Check for LIBLO libraries. if (CONFIG_LIBLO) pkg_check_modules (LIBLO IMPORTED_TARGET liblo) if (NOT LIBLO_FOUND) message (WARNING "*** LIBLO library not found.") set (CONFIG_LIBLO 0) endif () endif () # Check for LV2 support. if (CONFIG_LV2) pkg_check_modules (LV2 lv2) if (LV2_FOUND) include_directories (${LV2_INCLUDE_DIRS}) # Check for LV2 old/new headers style. check_include_file (lv2/urid/urid.h HAVE_LV2_URID_H) if (NOT HAVE_LV2_URID_H) check_include_file (lv2/lv2plug.in/ns/ext/urid/urid.h HAVE_OLD_LV2_URID_H) if (NOT HAVE_OLD_LV2_URID_H) set (CONFIG_LV2 0) else () set (CONFIG_LV2_OLD_HEADERS 1) endif () else () set (CONFIG_LV2_OLD_HEADERS 0) endif () endif () if (NOT CONFIG_LV2) message (WARNING "*** LV2 SDK not found.") endif () endif () if (CONFIG_LV2) # Check for LV2 Atom support. if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/atom/atom.h HAVE_LV2_ATOM_H) else () check_include_file (lv2/atom/atom.h HAVE_LV2_ATOM_H) endif () if (NOT HAVE_LV2_ATOM_H) set (CONFIG_LV2_ATOM 0) else () set (CONFIG_LV2_ATOM 1) endif () set (CONFIG_LV2_ATOM_FORGE_OBJECT ${CONFIG_LV2_ATOM}) set (CONFIG_LV2_ATOM_FORGE_KEY ${CONFIG_LV2_ATOM}) # Check for LV2 UI support. if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/extensions/ui/ui.h HAVE_LV2_UI_H) else () check_include_file (lv2/ui/ui.h HAVE_LV2_UI_H) endif () if (NOT HAVE_LV2_UI_H) set (CONFIG_LV2_UI 0) else () set (CONFIG_LV2_UI 1) endif () if (NOT CONFIG_LV2_UI) set (CONFIG_LV2_UI_X11 0) set (CONFIG_LV2_UI_WINDOWS 0) set (CONFIG_LV2_UI_EXTERNAL 0) set (CONFIG_LV2_UI_IDLE 0) set (CONFIG_LV2_UI_SHOW 0) set (CONFIG_LV2_UI_RESIZE 0) endif () endif () # Check for LV2 headers. if (CONFIG_LV2) set (LV2_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/lv2) set (CMAKE_REQUIRED_INCLUDES "${LV2_INCLUDES};${CMAKE_REQUIRED_INCLUDES}") include_directories (${LV2_INCLUDES}) if (CONFIG_LV2_OLD_HEADERS) set (CMAKE_REQUIRED_DEFINITIONS "-DCONFIG_LV2_OLD_HEADERS;${CMAKE_REQUIRED_DEFINITIONS}") endif () else () set (CONFIG_LV2_UI_X11 0) set (CONFIG_LV2_UI_WINDOWS 0) set (CONFIG_LV2_UI_EXTERNAL 0) set (CONFIG_LV2_UI_IDLE 0) set (CONFIG_LV2_UI_SHOW 0) set (CONFIG_LV2_UI_RESIZE 0) set (CONFIG_LV2_PROGRAMS 0) set (CONFIG_LV2_PATCH 0) set (CONFIG_LV2_PORT_EVENT 0) set (CONFIG_LV2_PORT_CHANGE_REQUEST 0) endif () if (CONFIG_LV2_UI_EXTERNAL) check_include_file (lv2_external_ui.h HAVE_LV2_EXTERNAL_UI_H) if (NOT HAVE_LV2_EXTERNAL_UI_H) set (CONFIG_LV2_UI_EXTERNAL 0) endif () endif () if (CONFIG_LV2_PROGRAMS) check_include_file (lv2_programs.h HAVE_LV2_PROGRAMS_H) if (NOT HAVE_LV2_PROGRAMS_H) set (CONFIG_LV2_PROGRAMS 0) endif () endif () if (CONFIG_LV2_PATCH) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/patch/patch.h HAVE_LV2_PATCH_H) else () check_include_file (lv2/patch/patch.h HAVE_LV2_PATCH_H) endif () if (NOT HAVE_LV2_PATCH_H) set (CONFIG_LV2_PATCH 0) endif () endif () if (CONFIG_LV2_PORT_CHANGE_REQUEST) check_include_file (lv2_port_change_request.h HAVE_LV2_PORT_CHANGE_REQUEST_H) if (NOT HAVE_LV2_PORT_CHANGE_REQUEST_H) set (CONFIG_LV2_PORT_CHANGE_REQUEST 0) endif () endif () add_subdirectory (src) # Finally check whether Qt is statically linked. if (QT_FEATURE_static) set(QT_VERSION "${QT_VERSION}-static") endif () # Configuration status macro (SHOW_OPTION text value) if (${value}) message ("${text}: yes") else () message ("${text}: no") endif () endmacro () message ("\n ${PROJECT_NAME} ${PROJECT_VERSION} (Qt ${QT_VERSION})") message ("\n Build target . . . . . . . . . . . . . . . . . . .: ${CONFIG_BUILD_TYPE}\n") show_option (" JACK stand-alone build . . . . . . . . . . . . . ." CONFIG_JACK) show_option (" JACK session support . . . . . . . . . . . . . . ." CONFIG_JACK_SESSION) show_option (" JACK MIDI support . . . . . . . . . . . . . . . ." CONFIG_JACK_MIDI) show_option (" ALSA MIDI support . . . . . . . . . . . . . . . ." CONFIG_ALSA_MIDI) show_option (" LV2 plug-in build . . . . . . . . . . . . . . . ." CONFIG_LV2) if (WIN32) show_option (" LV2 plug-in Windows UI support . . . . . . . . ." CONFIG_LV2_UI_WINDOWS) else () show_option (" LV2 plug-in X11 UI support . . . . . . . . . . ." CONFIG_LV2_UI_X11) endif () show_option (" LV2 plug-in External UI support . . . . . . . . ." CONFIG_LV2_UI_EXTERNAL) show_option (" LV2 plug-in UI Idle interface support . . . . . ." CONFIG_LV2_UI_IDLE) show_option (" LV2 plug-in UI Show interface support . . . . . ." CONFIG_LV2_UI_SHOW) show_option (" LV2 plug-in UI Resize interface support . . . . ." CONFIG_LV2_UI_RESIZE) show_option (" LV2 plug-in Programs support . . . . . . . . . . ." CONFIG_LV2_PROGRAMS) show_option (" LV2 plug-in Patch support . . . . . . . . . . . ." CONFIG_LV2_PATCH) show_option (" LV2 plug-in Port-event support . . . . . . . . . ." CONFIG_LV2_PORT_EVENT) show_option (" LV2 plug-in Port-change request . . . . . . . . ." CONFIG_LV2_PORT_CHANGE_REQUEST) show_option (" OSC service support (liblo) . . . . . . . . . . ." CONFIG_LIBLO) show_option (" Non/New Session Management (NSM) support . . . . ." CONFIG_NSM) message ("\n Install prefix . . . . . . . . . . . . . . . . . .: ${CONFIG_PREFIX}\n") synthv1-1.5.1/PaxHeaders/ChangeLog0000644000000000000000000000013215270627745014001 xustar0030 mtime=1793273829.969168979 30 atime=1793273829.969168979 30 ctime=1793273829.969168979 synthv1-1.5.1/ChangeLog0000644000175000001440000006515515270627745014005 0ustar00rncbcuserssynthv1 - an old-school polyphonic synthesizer ---------------------------------------------- ChangeLog 1.5.1 2026-10-29 An Autumn'26 Release. - Cosmetic fix to parameter dial/knob modified value colors. - Avoid recalling not-user-writable preset directories. - Replace the Random(ize) warning dialog with a percentage input dialog. 1.5.0 2026-09-15 An End-of-Summer'26 Release. - Introducing factory/community presets install package. - Color theme (with palette editor) and (widget) Style theme options moved to Help/Configure.../Custom tab (with preview). 1.4.3 2026-09-01 A Late-Summer'26 Release. - Fixed an old freezer bug that manifested whenever a MIDI Program Change is issued. - Vastly improved Bank/Preset management widgets. - Fixed wrong keymap-file setter on tuning loader. - Added file-types property to LV2 plug-in Path parameters. 1.4.2 2026-04-30 A Spring'26 Release. - Bumping into next development cycle (Qt >= 6.11) 1.4.1 2026-03-18 An Early-Spring'26 Release. - DejaVuSansCondensed.ttf is now the officially bundled font. 1.4.0 2026-02-12 A Mid-Winter'26 Release. - DejaVuSans TTF font is now firmly embedded to improve on the self-containment status (ie. freetype2 static linking). - Fixed 'mime-info' file to the correct MIME-type icon names. - Get rid of CONFIG_WAYLAND build config option; add underlying platform name (eg. xcb, wayland) to Qt version string. 1.3.2 2025-05-20 A Mid-Spring'25 Release. - Stepping up next development cycle (Qt >= 6.9) 1.3.1 2025-04-03 An Early Spring'25 Release. - Fixed command line parsing (QCommandLineParser/Option) to not exiting the application with a segfault when showing help and version information. 1.3.0 2025-01-16 A New-Year'25 Release. - Just another Vee-One's release for the New Year... 1.2.0 2024-12-15 An End-of-Year'24 Release. - Configure/Tuning: fixed initial scale and keyboard map tooltips. 1.1.3 2024-10-31 A Halloween'24 Release. - Prepping up next development cycle (Qt >= 6.8) 1.1.2 2024-10-02 An Early-Fall'24 Release. - Configure: disable widget style option on LV2 plug-ins only, for which it wasn't working on the JACK stand-alones either. 1.1.1 2024-09-20 An End-ofSummer'24 Release. - NSM: prevent loading any preset(s) given on command line. 1.1.0 2024-08-28 A Mid-Summer'24 Release. - LV2 Plug-in Control Input Port-Change Request extension is not tagged experimental anymore. 1.0.0 2024-06-20 An Unthinkable Release. - Making up the unthinkable (aka. v1.0.0) 0.9.91 2024-05-02 A Spring'24 Release Candidate 2. - Prepping the unthinkable (aka. v1.0.0-rc2) - Updated to latest framework level (Qt >= 6.7) 0.9.90 2024-04-11 A Spring'24 Release Candidate. - Prepping the unthinkable (aka. v1.0.0-rc1) - Custom color themes are now file based (*.conf); legacy still preserved ntl. - Fixed the build checks on whether to use old or newer style of LV2 include headers. 0.9.34 2024-01-26 A Winter'24 Release. - Added build checks on whether to use old or newer style of LV2 include headers. - LV2 plugin Control Input Port-change request extension feature support added. - Updated copyright headers into the New Year (2024). 0.9.33 2023-12-20 An End-of-Autumn'23 Release. - Minor optimization to denormal avoidance on some fx's. - Bumping into next development cycle (Qt >= 6.6) 0.9.32 2023-09-12 An End-of-Summer'23 Release. - Preppings to next development cycle (Qt >= 6.6) 0.9.31 2023-06-06 A Spring'23 Release. - Make sure the XCB interface is in use when instantiating the LV2 plugin on non-Qt hosts, allowing LV2 X11 UI embedding to work and show up properly. - Prepping into the next development cycle (with Qt >= 6.5). 0.9.30 2023-03-24 An Early-Spring'23 Release. - Restrict to existing named presets when adding new or editing programs; load and restore presets directly when previewing programs (cf. Configure... > Programs). - NSM open and save operation failures are now replied with an error status (was always OK before). - JACK client watchdog introduced for automatic reactivation and resilience (standalone only). - NSM announcement reply/error messages now relegated to debug builds only. 0.9.29 2023-01-25 A Winter'23 Release. - Bumping copyright headers to the brand new year. 0.9.28 2022-12-29 An End-of-Year'22 Release. - Mitigate NSM sending lots of dirty messages on SIGTERM signal. - Although being deprecated to use, JACK Session support hopefuly fixed once again. 0.9.27 2022-10-04 An Early-Autumn'22 Release. - Fixed MIDI Controller... modeless dialog (de)instantiation. - Custom color/style themes applies only to main widget only. - Fixed typos and updated some old MIDI GM2 Controller names. 0.9.26 2022-06-07 An End-of-Sping'22 Release. - Corrected an incredible mistake introduced in the last year's Glide/portamento protential crash fix. - Added Qt::Svg module as required at build time. 0.9.25 2022-04-07 A Spring'22 Release. - Added missing file code to desktop exec entry (standalone only). - Main application icon is now presented in scalable format (SVG). - Migrated command line parsing to QCommandLineParser/Option (Qt >= 5.2) - Avoid issuing NSM dirty messages as much as possible. 0.9.24 2022-01-09 A Winter'22 Release. - Dropped autotools (autoconf, automake, etc.) build system. - Fixed deprecate warnings in preparation for Qt >= 6.2. 0.9.23 2021-07-07 An Early-Summer'21 Release. - Fixed some ages old Glide/portamento potential crash bug (reported by AnClark). - Add support for LV2 UI Windows platform (by AnClark). - Sustenuto pedal controller (MIDI CC#66) is now implemented. - Have some tolerance for buffer-size changes. - All builds default to Qt6 (Qt >= 6.1) where available. - CMake is now the official build system. 0.9.22 2021-05-13 A Spring'21 Release. - All packaging builds switching to CMake. 0.9.21 2021-03-16 An End-of-Winter'21 Release. - Fixed an old lurker, causing slight differences to voice on-sets, due to a wrong initial LFO Volume ramping value. - Slightly improved eye-candyness to all graphic widgets, most especially on the EG widget handling, curved lines and gradient fills. 0.9.20 2021-02-10 A Winter'21 Release. - When under under the NSM auspices, fix SIGTERM handler on exit and just hide the main-window on close (applies to the JACK stand-alone only). 0.9.19 20220-12-19 A Winter'20 Release. - Fixed display of old knob/dial values on status-bar. - Reduced formant filter coefficient slew rate, aiming to a tenfold DSP performance gain. - Get a chance on bringing Wayland into the picture when dealing Qt static-linked deliverables. 0.9.18 2020-10-27 A Fall'20 Release. - Upstream package naming for the JACK standalone and LV2 plugin deliverables are suffixed to '-jack' and '-lv2', respectively. - White keys on the virtual piano keyboard are now fully highlighted. 0.9.17 2020-09-08 Late Summer'10 Release. - Added -n, --client-name to the JACK stand-alone client application command line option arguments. - Fixed unitialized number of voices variable; early-bird adaptations to Qt6 >= 6.0.0 and C++17 standard. 0.9.16 2020-08-06 A Summer'20 Release. - Fixed deprecated stuff on an early preparation for Qt6. 0.9.15 2020-06-22 An Early-Summer'20 Release. - A long awaited expedite "Panic" button is now featured. - Note velocity doesn't affect DCF envelope anymore. - Avoid glib event-loop upon LV2 plug-in instantiation. 0.9.14 2020-05-05 A Mid-Spring'20 Release. - Fixed initial DCF1, LFO1 group enablement (GUI). - Fixed initial window title for the LV2 External UI. - LV2 Atom/Port-event host notification support has been implemented (unofficial). - Prevent execution of duplicate or redundant MIDI bank- select/program-change commands. - Early fixing to build for Qt >= 5.15.0. 0.9.13 2020-03-26 A Spring'20 Release. - Maximum pitch-bend range has been expanded to 0..400%, allegedly to support some PME driven instruments (as kindly requested by sacarlson PR#10). - Make man page compression reproducible (after request by Jelle van der Waa, thanks). - Fixed crash when showing the tooltip of a negative note number that results from dragging over and beyond the left edge of the virtual piano keyboard. - Remove -v (verbose) flag from 'strip' post-link command. - Fixed CMake build by adding missing Custom Color Theme (palette) form file (.ui). - Bumped copyright headers into the New Year (2020). 0.9.12 2019-12-26 The Winter'19 Release. - Custom color (palette) theme editor introduced; color (palette) theme changes are now effective immediately, except on default. - Second attempt to fix the yet non-official though CMake build configuration. - Move QApplication construction/destruction from LV2 UI to plug-in instantiation and cleanup. 0.9.11 2019-10-31 A Halloween'19 Release. - When using autotools and ./configure --with-qt=..., it is also necessary to adjust the PKG_CONFIG_PATH environment variable (after a merge request by plcl aka. Pedro López-Cabanillas, while on qmidinet). - Upstream packaging is now split to JACK standalone and LV2 plugin only: the former shared common core and UI package is now duplicated but statically linked though. 0.9.10 2019-09-24 An Early-Fall'19 Release. - Upstream packaging is now split on JACK standalone, LV2 plugin and common core and UI packages, similar to recent Debian practice. - Added alternate yet non-official CMake build option. - Fix HiDPI display screen effective support (Qt >= 5.6). - All randomizers now compliant to the standard normal distribution. - Make sure compiler flags comply to c++11 as standard. 0.9.9 2019-07-18 A Summer'19 Release. - Randomization of current parameters is now available through a new top-level push-button. - Updated for the newer Qt5 development tools (>= 5.13). - Per instance custom tuning (micro-tonal) option has been added to the previously existing global settings (cf. Help > Configure... > Tuning > Global, Instance). - New DCF and LFO Enabled parameters. - Configure updated to check for qtchooser availability. 0.9.8 2019-06-06 A Spring'19 Release. - Improved slew-rate to DCF Formant coefficient updates. - Refactored the LFO Sync for correctness. - Fixed all LFO Panning and Volume modulation, now being correctly isolated on a per voice basis. - Fixed DC offset on all main DCO wavetable oscillators. - Fixed initial DCO and LFO wave shapes on LV2 plug-in UI. - Minor update to Debian packaging control file. 0.9.7 2019-04-14 A Spring-Break'19 Release. - All audio input now get through without being processed by any or whole effects stage anymore. - Re-defined all JACK stand-alone client application UNIX signal handling. 0.9.6 2019-03-18 Pre-LAC2019 Release Frenzy. - A gentlier shutdown for the JACK client standalone client. 0.9.5 2019-03-04 The End of Winter'19 release. - HiDPI display screen support (Qt >= 5.6). - A fake/visual piano-keyboard widget is now being introduced to the status bar. - Avoid destructing the possibly shared QApplication instance on LV2 plug-in UI clean-up. 0.9.4 2018-12-12 A Late Autumn'18 release. - Old deprecated Qt4 build support is no more. - Normalized wavetable oscillator phasors. - Added missing include to shut up some stricter compilers from build failures. 0.9.3 2018-10-22 An Autumn'18 release. - For safety reasons, all processing is now suspended while loading presets or program changes are issued. - AppStream metadata updated to be the most compliant with latest freedesktop.org specification and recommendation. - SIGTERM (and SIGINT) signal handler added to close the JACK stand-alone client applications properly. - Make the GUI not to show initially on NSM. - Make NSM state independent to session display name, keeping backward compatibility for old sessions. - Give some more slack to schedule/worker thread ring-buffer. 0.9.2 2018-07-24 A Summer'18 release. - Add LV2 UI Resize extension data support. - Process MIDI Controlllers even though the channel filter is on (DEF Channel is set anything but "Omni"). - AppData/AppStream metadata is now settled under an all permisssive license (FSFAP). 0.9.1 2018-06-26 An Early Summer'18 release. - Fixed for some g++ >= 8.1.1 warnings and quietness. - Added LV2 UI X11 support option. - Disable reference micro-tuning settings when a Scala keyboard map override is in effect. - Added "All files (*.*)" filter to every file requestor dialog, wherever missing. 0.9.0 2018-03-07 The End of Winter'18 release. - Introducing Scala micro-tuning classes, borrowed, stirred and refactored from original Nick Dowell's amsynth code, all under the GPL umbrella, of course. - An internal note-key/frequency indirection table is now in place, as a baseline for any micro-tuning implementations. - Whether to use native file browser/requester dialogs is now an effective option when launching under NSM session management (was once disabled initially). - Trying to get CC14 MSB+LSB (course+fine) running status on, no matter whether each pairing event are under 200ms apart. - A little hardening on the configure (autoconf) macro side. 0.8.6 2017-12-20 The End of Autumn'17 release. - Sync option added to DCO wavetable oscillators, as for making either one to hard-sync (slave) with the other one (master) in each pair. - Set on a minimum attack time of 500usec as much to prevent audible clicking on low-pitched notes. 0.8.5 2017-10-29 An Autumn'17 release. - Opening multiple preset files is now possible, populating the preset drop-down listing, while only the first one is loaded effectively into the scene as usual. - Mono(phonic) "Legato" mode option introduced. - Desktop entry specification file is now finally independent from all build/configure template chains, whatever. - Updated target path for freedesktop.org's AppStream metainfo file (formerly AppData). 0.8.4 2017-08-22 A Late-Summer'17 release. - Disabled "Custom style theme" option on LV2 plug-in form. - Brand new LFO Balance parameter introduced. 0.8.3 2017-06-21 A Summer'17 release. - Added StartupWMClass entry to desktop file. - Long overdue, some brand new and fundamental icons revamp. 0.8.2 2017-05-02 Pre-LAC2017 release frenzy. - A custom knob/spin-box behavioral option have been added: Configure/Knob edit mode, as to avoid abrupt changes upon editing values (still the default behavior) and only take effect (Deferred) when enter is pressed or the spin-box loses focus. - The main GUI has been partially revamped, after replacing some rotary knob/dial combos with kinda more skeuomorphic fake-LED radio-buttons or check-boxes. - Alias-free/band-limited wavetable oscillators have been fixed to prevent cross-octave, polyphonic interference. - A MIDI In(put) status fake-LED is now featured on the bottom-left status bar, adding up to eye-candy as usual. 0.8.1 2017-03-21 A Spring'17 release. - Fixed a probable old miss about changing spin-box and drop-down list not reflecting changes immediately into the respective parameter dial knobs. - Fixed middle-button clicking on the dial-knobs to reset to the current default parameter value. - Help/Configure.../Options/Use desktop environment native dialogs option is now set initially off by default. - Added French man page (by Olivier Humbert, thanks). - Make builds reproducible byte for byte, by getting rid of the configure build date and time stamps. 0.8.0 2016-11-17 A Fall'16 release. - LV2_STATE__StateChanged is now transmitted as a regular atom notification event, as far as to give some careful hosts enough slack to raise a dirty flag. - Fixed input MIDI RPN/NRPN running status processing. - Almost complete overhaul on the configure script command line options, wrt. installation directories specification, eg. --prefix, --bindir, --libdir, --datadir and --mandir. 0.7.6 2016-09-19 The Eleventh official beta. - MIDI RPN/NRPN running status and RPN NULL reset command are now supported (input only). - The core engine implementation is now delivered as a shared object library, common to both the JACK stand-alone client and the LV2 instrument plug-in. - Discretely fixed MIDI Controlllers catch-up algorithm. 0.7.5 2016-06-16 The Tenth official beta. - All changes to most continuous parameter values are now smoothed to a fast but finite slew rate. - All BPM sync options to current transport (Auto) have been refactored to new special minimum value (which is now zero). - In compliance to the LV2 spec. MIDI Controllers now affect cached parameter values only, via shadow ports, instead of input control ports directly, mitigating their read-only restriction. - Make sure LV2 plug-in state is properly reset on restore. - Dropped the --enable-qt5 from configure as found redundant given that's the build default anyway (suggestion by Guido Scholz, while for Qtractor, thanks). 0.7.4 2016-03-02 The ninth-bis official beta. - Fixed the DCF Formant filter voice initialization reset. 0.7.3 2016-02-22 The ninth official beta. - Avoid out-of-bound MIDI events as much as possible, coping with LV2 plug-in hosts that feed/run them in on border line circumstances (as reported by Thorsten Wilms, on suspected Ardour looping crash/bug, probably fixed already, thanks). - Safe defaults are tentatively sbeing introduced to internal OUT FX buffer-sizes, as read from JACK buffer-size changes and LV2 block-length instantiation bound options. - Added application keywords to freedesktop.org's AppData. 0.7.2 2015-12-04 The eighth official beta. - A brand new LFO BPM control parameter is being introduced, as a subordinate to LFO Rate, with follow/sync to current transport/host option (BPM=Auto). - LFO Sync (free running) mode option has been introduced. - A fourth DCF type has been added: a vocal Formant filter. - A third DCF slope/type has been added: the RBJ's bi-quad. - Prefer Qt5 over Qt4 by default with configure script. - Introducing brand new OUT FX Send parameter per engine. - Introducing brand new DCO, LFO Ring Mod(ulator) parameters. - Special value (0=Off) added to DCO1,2 Glide knob spin-boxes. 0.7.1 2015-08-24 A seventh-bis official beta. - Improved Qt4 vs. Qt5 configure builds (via qmake). 0.7.0 2015-07-24 A seventh official beta. - Complete rewrite of Qt4 vs. Qt5 configure builds. - Reset ramps after LV2 control port reconnection; small fixes to LV2.ttl (pull-requests by Hanspeter Portner aka. ventosus, thanks). - MIDI Controllers/Programs is now an optional feature on the LV2 plugin forms, as some LV2 hosts might enforce the purity restriction to input control ports as being absolutely read- only parameter values from a plugin's instance perspective. - MIDI Controller mapping/learn is now possible on all parameter control knobs; with global configuration also avaiable on the Help/Configure dialog. - French (fr) translation line to desktop file added (by Olivier Humbert, thanks). 0.6.3 2015-05-09 A sixth official beta. - Main widget layout changed as much to expand or grow vertically as needed. 0.6.2 2015-04-30 A fifth official beta. - Custom knob/dial behavior mode options are now introduced: linear and angular (aka. radial) as far to avoid abrupt changes on first mouse click (still the default behavior). - Fixed for some strict tests for Qt4 vs. Qt5 configure builds. 0.6.1 2015-03-09 A fourth official beta. - Added application description as freedesktop.org's AppData. - Introducing LV2 port-groups (as proposed by Amadeus Folego aka. badosu, thanks). - Improved envelope widget nodes click-and-drag precision. - Introducing a brand new user preference on eye-candy: cf.Help /Configure.../Options/Custom style theme (applies to the JACK stand-alone client only though). - Envelope and filter now rendered with anti-aliased lines. - Fixed a Qt5 FTBFS re. QHeaderView::set[Section]ResizeMode(). 0.6.0 2014-01-23 A third official beta. - MIDI bank-select/program-changes is now supported, for patch, preset and/or instrument program control. - New Help/Configure dialog is introduced for editing the also new MIDI bank/programs interface and user preference options as well (new home of the old Help/Use native dialogs option). - Presets may now be specified by base name, as an alternative to the complete preset file-path on command line argument (re. stand-alone JACK client). - Fixed parameters A/B comparison swap. 0.5.1 2014-09-12 One second official beta. - One decimal digit added to all scalar parameters and knobs. - Stand-alone JACK client ports outrageously renamed from a zero based numbering scheme into a plus one natural one. - Experimental LV2 Time/position atom-event support (Delay BPM). 0.5.0 2014-06-30 Officially beta now. - LV2 UI Idle and Show interfaces support added. - First attempt to allow a headless stand-alone JACK client application run mode, without a GUI, with option given as command line argument (--no-gui). - A man page has beed added (re. stand-alone JACK client). - Disable band-limit option for 'Noise' oscillator wave-shapes. - Allow the build system to include an user specified LDFLAGS. 0.4.2 2014-04-29 A pre-LAC frenzy beta. - Badly named 'Noise' wave-shape oscillators get their band- limited treat option and re-labeled as 'Rand'; also a brand new wave-shape 'Noise' is then introduced (now for real :)). - New user preference option, cf. Help/Use native dialogs. - An anti-denormal regression applied to the Phaser fx stage (affecting early proto-beta >= 0.4.0). 0.4.1 2014-04-07 A proto-beta bis. - Once so called 'Noise' wave-shapes are now being made a lot more deterministic, read idempotent ;). - Fully alias-free/band-limited wavetable oscillators are now in place, making it a better virtual-analog synthesizer (esp. for running sample-rates below 96KHz). - Late optimizations to basic wave-table oscillators. - Make sure the LV2 plugin back-end always builds first, before its respective LV2 UI front-end client. 0.4.0 2014-03-06 A proto-beta party. - All knobs default value setting slightly improved. - A run-time circumvention have been hacked, strictly related to when NSM session management is in charge: the native file browser/requester dialogs are then disabled (were taking too long to list the current directory on first time invocation). - Auto-detection of the correct target library path for the LV2 plugin, whether it may be .../lib or .../lib64 on bi -arch targets, when libdir is not specified on configure time (--libdir=LIBDIR). - Reverb has been added as a brand new effects stage, though based on good old FreeVerb :). - Introduced a variable env.stage time control parameter. - Dummy change-log entry for the new year. 0.3.6 2013-12-31 A fifth of a Jubilee. - Effects/Delay BPM sync option to current transport (Auto). - More preparations for Qt5 configure build. 0.3.5 2013-09-30 Fall greetings. - First attempt on separating the DSP code from UI as runtime objects, effective for the LV2 plugin deploy. - MIDI channel filter switch is now introduced. - Fixed uninstall target make rule. - Mono(phonic) mode switch is now being introduced. - Another minor fix to note-off fast-release. 0.3.4 2013-07-16 Brand new icon ready. - Preset file icon and mimetype now introduced. - A brand new icon drops the lamest old-schooler's out, in a master lesson taught by Jarle Richard Akselsen, thanks. 0.3.3 2013-05-31 Late spring blossoming - Some sympathy to extreme dark color (read black) schemes is now being indulged; some discrete changes on the icon front has also emerged ;) - MIDI Key pressure/polyphonic aftertouch events now properly recognized, in addition to MIDI channel pressure/aftertouch processing. - Parameter value ramping moved into the late post-processing, an attempt to reduce some pop/click artifacts even further. - NSM support introduced to JACK client. - A slight GUI behavior change: (re)writing existing names doesn't load the respective preset anymore (after ticket by Jiri Prochazka aka. Anchakor, thanks). - Introducing LV2 External UI "rogue" support. - Moog-like 24dB/oct resonant filter slight modification. - Fixed JACK client fail/fake initialization. 0.3.2 2013-03-01 One third bug-fix release. - Pitch-bend, Modwheel and LFO Pitch range internal fixes. - Sustain/damper pedal switch (MIDI CC#64) support added. - Color palette hack on Qt5 dark themes. 0.3.1 2013-02-08 One second bug-fix release. - Improved filters parameter sensitivity (cutoff, reso). - Envelope generators stage curves now gone a little more old- schooler and analog-like, hopefully improving on the punchy and click-less sound front. - Experimental LV2 Time designated port support (Delay BPM). - Preparations for Qt5 migration. 0.3.0 2012-12-21 Third coming release. - Dropped all SSE dummy build optimization flags as they were making more harm than real good, performance wise. - GUI layout moved from a toolbox into a stacked tab widget; also, a status-bar has been added at the bottom display. - LV2 Atom/MIDI event processing is now official, a replacement for the formerly deprecated LV2 Event/MIDI specification. - Added dirty checking also on switching from unamed presets. - JACK stand-alone: preset loader initialization fix. 0.2.0 2012-11-22 Second public release. - Parameters A/B comparison toggle buttons have been added. - Keep LV2 event buffer valid on each run(), as found for strict LV2 specification and compliance (as suggested by falkTX). - Increased polyphony baseline from 24 to 32 voices. - Prepared for relative paths from preset file references. - Velocity sensitivity configuration control has been added. - Reset (all values to preset defaults) button has been added. - Slight minor fix on fast-release re-triggering. - Added some command line options to the stand-alone JACK client program (--help and --version information). - Reimplemented mouse-wheel stepping on combo-dial knob widgets. - Detached generic double-linked list class into its own header. 0.1.1 2012-09-24 One first bug-fix release. - Fixed a crash bug on LV2 main widget close (GUI) due to some superfluous preset dirty checking. - Fixed a nasty rather fundamental bug introduced by late voice list optimization, which was seriously crippled and noticeable when more than one LV2 plug-in were being instantiated. 0.1.0 2012-09-10 First true official release. - Preset drop-down list is now sorted. - Migrated to sourceforge.net. 0.0.9 2012-08-27 Post-vacation relax. - Main widget now a plain basic QWidget, instead of QDialog. - Preset salvage query check on main widget close (GUI). - Voice list alloc/free optimization--doubled polyphony. 0.0.8 2012-07-15 Second coming consolidation. - Proto-icon, Desktop file and Help About menus added. 0.0.7 2012-07-07 Initial change-log entry. synthv1-1.5.1/PaxHeaders/LICENSE0000644000000000000000000000013215270627745013234 xustar0030 mtime=1793273829.969168979 30 atime=1793273829.969168979 30 ctime=1793273829.969168979 synthv1-1.5.1/LICENSE0000644000175000001440000004310315270627745013225 0ustar00rncbcusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. synthv1-1.5.1/PaxHeaders/README0000644000000000000000000000013215270627745013107 xustar0030 mtime=1793273829.969168979 30 atime=1793273829.969168979 30 ctime=1793273829.969168979 synthv1-1.5.1/README0000644000175000001440000000526515270627745013107 0ustar00rncbcuserssynthv1 - an old-school polyphonic synthesizer ---------------------------------------------- an old-school 4-oscillator subtractive polyphonic synthesizer with stereo fx. Features: - pure stand-alone JACK [1] client with JACK-session, NSM [3] and both JACK MIDI and ALSA MIDI [2] input support; - LV2 [4] instrument plugin. URI: http://synthv1.sourceforge.net/lv2 License: synthv1 is free, Linux Audio [5] open-source software, distributed under the terms of the GNU General Public License (GPL) [6] version 2 or later. Website: https://synthv1.sourceforge.io http://synthv1.sourceforge.net Project page: https://sourceforge.net/projects/synthv1 Git repos: https://git.code.sf.net/p/synthv1/code https://github.com/rncbc/synthv1.git https://gitlab.com/rncbc/synthv1.git https://codeberg.org/rncbc/synthv1.git Weblog: https://www.rncbc.org Requirements: mandatory, - Qt framework [7], C++ class library and tools for cross-platform application and UI development https://qt.io/ optional (opted-in at build time), - JACK [1] Audio Connection Kit https://jackaudio.org/ - ALSA [2], Advanced Linux Sound Architecture https://www.alsa-project.org/ - LV2 [4], Audio Plugin Standard, the extensible successor of LADSPA https://lv2plug.in/ - liblo [8], Lightweight OSC implementation (needed for NSM support [3]) http://liblo.sourceforge.net/ Installation: - unpack tarball as usual; in the extracted source directory: cmake [-DCMAKE_INSTALL_PREFIX=] -B build cmake --build build [--parallel ] - optionally, as root: [sudo] cmake --install build - note that the default installation path () is /usr/local . Acknowledgements: synthv1 logo/icon is an original fine work of Jarle Richard Akselsen. References: [1] JACK Audio Connection Kit https://jackaudio.org/ [2] ALSA, Advanced Linux Sound Architecture https://www.alsa-project.org/ [3] Non Session Management (NSM) (legacy) http://non.tuxfamily.org/nsm/ New Session Manager (NSM) https://new-session-manager.jackaudio.org/ [4] LV2, Audio Plugin Standard, the extensible successor of LADSPA http://lv2plug.in/ [5] Linux Audio consortium of libre software for audio-related work https://linuxaudio.org [6] GNU General Public License https://www.gnu.org/copyleft/gpl.html [7] Qt framework, C++ class library and tools for cross-platform application and UI development https://qt.io/ [8] liblo [8], Lightweight OSC implementation (needed for NSM support) http://liblo.sourceforge.net/ Cheers && Enjoy. -- rncbc aka. Rui Nuno Capela rncbc@rncbc.org https://www.rncbc.org synthv1-1.5.1/PaxHeaders/src0000644000000000000000000000013215270627745012741 xustar0030 mtime=1793273829.975582031 30 atime=1793273829.969653869 30 ctime=1793273829.975582031 synthv1-1.5.1/src/0000755000175000001440000000000015270627745013006 5ustar00rncbcuserssynthv1-1.5.1/src/PaxHeaders/synthv1.h0000644000000000000000000000013215270627745014603 xustar0030 mtime=1793273829.973346489 30 atime=1793273829.973346489 30 ctime=1793273829.973346489 synthv1-1.5.1/src/synthv1.h0000644000175000001440000001163415270627745014600 0ustar00rncbcusers// synthv1.h // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #ifndef __synthv1_h #define __synthv1_h #include "config.h" #include // forward declarations class synthv1_impl; class synthv1_port; class synthv1_controls; class synthv1_programs; //------------------------------------------------------------------------- // synthv1 - decl. // class synthv1 { public: synthv1(uint16_t nchannels = 2, float srate = 44100.0f, uint32_t nsize = 1024); virtual ~synthv1(); void setChannels(uint16_t nchannels); uint16_t channels() const; void setSampleRate(float srate); float sampleRate() const; void setBufferSize(uint32_t nsize); uint32_t bufferSize() const; void setTempo(float bpm); float tempo() const; enum ParamIndex { DCO1_SHAPE1 = 0, DCO1_WIDTH1, DCO1_BANDL1, DCO1_SYNC1, DCO1_SHAPE2, DCO1_WIDTH2, DCO1_BANDL2, DCO1_SYNC2, DCO1_BALANCE, DCO1_DETUNE, DCO1_PHASE, DCO1_RINGMOD, DCO1_OCTAVE, DCO1_TUNING, DCO1_GLIDE, DCO1_ENVTIME, DCF1_ENABLED, DCF1_CUTOFF, DCF1_RESO, DCF1_TYPE, DCF1_SLOPE, DCF1_ENVELOPE, DCF1_ATTACK, DCF1_DECAY, DCF1_SUSTAIN, DCF1_RELEASE, LFO1_ENABLED, LFO1_SHAPE, LFO1_WIDTH, LFO1_BPM, LFO1_RATE, LFO1_SYNC, LFO1_SWEEP, LFO1_PITCH, LFO1_BALANCE, LFO1_RINGMOD, LFO1_CUTOFF, LFO1_RESO, LFO1_PANNING, LFO1_VOLUME, LFO1_ATTACK, LFO1_DECAY, LFO1_SUSTAIN, LFO1_RELEASE, DCA1_VOLUME, DCA1_ATTACK, DCA1_DECAY, DCA1_SUSTAIN, DCA1_RELEASE, OUT1_WIDTH, OUT1_PANNING, OUT1_FXSEND, OUT1_VOLUME, DEF1_PITCHBEND, DEF1_MODWHEEL, DEF1_PRESSURE, DEF1_VELOCITY, DEF1_CHANNEL, DEF1_MONO, DCO2_SHAPE1, DCO2_WIDTH1, DCO2_BANDL1, DCO2_SYNC1, DCO2_SHAPE2, DCO2_WIDTH2, DCO2_BANDL2, DCO2_SYNC2, DCO2_BALANCE, DCO2_DETUNE, DCO2_PHASE, DCO2_RINGMOD, DCO2_OCTAVE, DCO2_TUNING, DCO2_GLIDE, DCO2_ENVTIME, DCF2_ENABLED, DCF2_CUTOFF, DCF2_RESO, DCF2_TYPE, DCF2_SLOPE, DCF2_ENVELOPE, DCF2_ATTACK, DCF2_DECAY, DCF2_SUSTAIN, DCF2_RELEASE, LFO2_ENABLED, LFO2_SHAPE, LFO2_WIDTH, LFO2_BPM, LFO2_RATE, LFO2_SYNC, LFO2_SWEEP, LFO2_PITCH, LFO2_BALANCE, LFO2_RINGMOD, LFO2_CUTOFF, LFO2_RESO, LFO2_PANNING, LFO2_VOLUME, LFO2_ATTACK, LFO2_DECAY, LFO2_SUSTAIN, LFO2_RELEASE, DCA2_VOLUME, DCA2_ATTACK, DCA2_DECAY, DCA2_SUSTAIN, DCA2_RELEASE, OUT2_WIDTH, OUT2_PANNING, OUT2_FXSEND, OUT2_VOLUME, DEF2_PITCHBEND, DEF2_MODWHEEL, DEF2_PRESSURE, DEF2_VELOCITY, DEF2_CHANNEL, DEF2_MONO, CHO1_WET, CHO1_DELAY, CHO1_FEEDB, CHO1_RATE, CHO1_MOD, FLA1_WET, FLA1_DELAY, FLA1_FEEDB, FLA1_DAFT, PHA1_WET, PHA1_RATE, PHA1_FEEDB, PHA1_DEPTH, PHA1_DAFT, DEL1_WET, DEL1_DELAY, DEL1_FEEDB, DEL1_BPM, REV1_WET, REV1_ROOM, REV1_DAMP, REV1_FEEDB, REV1_WIDTH, DYN1_COMPRESS, DYN1_LIMITER, KEY1_LOW, KEY1_HIGH, NUM_PARAMS }; void setParamPort(ParamIndex index, float *pfParam); synthv1_port *paramPort(ParamIndex index) const; synthv1_controls *controls() const; synthv1_programs *programs() const; void setParamValue(ParamIndex index, float fValue); float paramValue(ParamIndex index) const; bool running(bool on); void stabilize(); void reset(); void process_midi(uint8_t *data, uint32_t size); void process(float **ins, float **outs, uint32_t nframes); virtual void updatePreset(bool bDirty) = 0; virtual void updateParam(ParamIndex index) = 0; virtual void updateParams() = 0; void midiInEnabled(bool on); uint32_t midiInCount(); void directNoteOn(int note, int vel); void setTuningEnabled(bool enabled); bool isTuningEnabled() const; void setTuningRefPitch(float refPitch); float tuningRefPitch() const; void setTuningRefNote(int refNote); int tuningRefNote() const; void setTuningScaleFile(const char *pszScaleFile); const char *tuningScaleFile() const; void setTuningKeyMapFile(const char *pszKeyMapFile); const char *tuningKeyMapFile() const; void resetTuning(); virtual void updateTuning() = 0; private: synthv1_impl *m_pImpl; }; #endif// __synthv1_h // end of synthv1.h synthv1-1.5.1/src/PaxHeaders/mimetypes0000644000000000000000000000013215270627745014755 xustar0030 mtime=1793273829.973264068 30 atime=1793273829.973232649 30 ctime=1793273829.973264068 synthv1-1.5.1/src/mimetypes/0000755000175000001440000000000015270627745015022 5ustar00rncbcuserssynthv1-1.5.1/src/mimetypes/PaxHeaders/org.rncbc.synthv1.xml0000644000000000000000000000013215270627745021044 xustar0030 mtime=1793273829.973264068 30 atime=1793273829.973264068 30 ctime=1793273829.973264068 synthv1-1.5.1/src/mimetypes/org.rncbc.synthv1.xml0000644000175000001440000000055315270627745021037 0ustar00rncbcusers synthv1 preset synthv1-1.5.1/src/mimetypes/PaxHeaders/org.rncbc.synthv1.application-x-synthv1-preset.svg0000644000000000000000000000013215270627745026524 xustar0030 mtime=1793273829.973264068 30 atime=1793273829.973232649 30 ctime=1793273829.973264068 synthv1-1.5.1/src/mimetypes/org.rncbc.synthv1.application-x-synthv1-preset.svg0000644000175000001440000050700715270627745026525 0ustar00rncbcusers synthv1-1.5.1/src/mimetypes/PaxHeaders/org.rncbc.synthv1.application-x-synthv1-preset.png0000644000000000000000000000013215270627745026511 xustar0030 mtime=1793273829.973232649 30 atime=1793273829.973232649 30 ctime=1793273829.973232649 synthv1-1.5.1/src/mimetypes/org.rncbc.synthv1.application-x-synthv1-preset.png0000644000175000001440000000322515270627745026503 0ustar00rncbcusers‰PNG  IHDR szzô pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î<"IDATX…½—[l\W†¿½ÏÌÏ}<—x�ï6“ĉQÍÅA�"*%u å)Ró@‚„òÈOH4– öRT$Dª6ni $ jâÄ%qqíØm]׎oã¹Øgæ\6¾`{Æ3i£öšÙ{íµ¿³Î^kí#”RÔ’B²†™RJ95�íõ½ ©³³åÒÐ^RJ‰PÊQŽÜ²SŽc½uýÍñç~ùó¯­­}ø°®²Kí=ö°vª£+Ýë÷×=‹ÅžXZZºû0k*…µê“W“? ÕÜÚøëß<#ù“<’,¥ñÄɯùû»^oH¥¾R˾ì<ª"õQ–3«<óí§½ õZª­íë3ÓÓ×?3€@0L¾°Ž®ëœ~ú©:¥Ôß›ÛÛ¿úÁÔÔ�ÏÀç󱸲ÊòJ†P0À·Oy,Ëzã`KˉÙ÷ßëSðÔy™üà=î¼}…¥…y¤p8˜Lx~rá§ÃBˆ£J©™O  ‘>|”ô‘c躎ÇS‡RŠìÊb°³û¹ï!†v¬� PÈg¹ùïk(Çþ¸KeGççÄãÒï÷ÿ£P(ÜúD·oÞ`äú0n·€�*]®�ã[¿{u? ürM€j2 £°¼¼ü̾¶eUà{?ºHªµÛ²¤”ˆÍæ$ Åæ·Uù¼ø§þ65ñî«–eýK)•«P*å £·oÞöx #include #include #include #include //------------------------------------------------------------------------- // state params description. enum ParamType { PARAM_FLOAT = 0, PARAM_INT, PARAM_BOOL }; static struct ParamInfo { const char *name; ParamType type; float def; float min; float max; } synthv1_params[synthv1::NUM_PARAMS] = { // name type, def, min, max { "DCO1_SHAPE1", PARAM_INT, 1.0f, 0.0f, 4.0f }, // DCO1 Wave Shape 1 { "DCO1_WIDTH1", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCO1 Wave Width 1 { "DCO1_BANDL1", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO1 Wave Bandlimit 1 { "DCO1_SYNC1", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO1 Wave Sync 1 { "DCO1_SHAPE2", PARAM_INT, 1.0f, 0.0f, 4.0f }, // DCO1 Wave Shape 2 { "DCO1_WIDTH2", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCO1 Width 2 { "DCO1_BANDL2", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO1 Wave Bandlimit 2 { "DCO1_SYNC2", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO1 Wave Sync 2 { "DCO1_BALANCE", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // DCO1 Balance { "DCO1_DETUNE", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCO1 Detune { "DCO1_PHASE", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO1 Phase { "DCO1_RINGMOD", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO1 Ring Mod { "DCO1_OCTAVE", PARAM_FLOAT, 0.0f, -4.0f, 4.0f }, // DCO1 Octave { "DCO1_TUNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // DCO1 Tuning { "DCO1_GLIDE", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO1 Glide { "DCO1_ENVTIME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCO1 Env.Time { "DCF1_ENABLED", PARAM_BOOL, 1.0f, 0.0f, 1.0f }, // DCF1 Enabled { "DCF1_CUTOFF", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF1 Cutoff { "DCF1_RESO", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCF1 Resonance { "DCF1_TYPE", PARAM_INT, 0.0f, 0.0f, 3.0f }, // DCF1 Type { "DCF1_SLOPE", PARAM_INT, 0.0f, 0.0f, 3.0f }, // DCF1 Slope { "DCF1_ENVELOPE", PARAM_FLOAT, 1.0f, -1.0f, 1.0f }, // DCF1 Envelope { "DCF1_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCF1 Attack { "DCF1_DECAY", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DCF1 Decay { "DCF1_SUSTAIN", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF1 Sustain { "DCF1_RELEASE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF1 Release { "LFO1_ENABLED", PARAM_BOOL, 1.0f, 0.0f, 1.0f }, // LFO1 Enabled { "LFO1_SHAPE", PARAM_INT, 1.0f, 0.0f, 4.0f }, // LFO1 Wave Shape { "LFO1_WIDTH", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // LFO1 Wave Width { "LFO1_BPM", PARAM_FLOAT, 180.0f, 0.0f, 360.0f }, // LFO1 BPM { "LFO1_RATE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // LFO1 Rate { "LFO1_SYNC", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // LFO1 Sync { "LFO1_SWEEP", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Sweep { "LFO1_PITCH", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Pitch { "LFO1_BALANCE", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Balance { "LFO1_RINGMOD", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Ring Mod { "LFO1_CUTOFF", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Cutoff { "LFO1_RESO", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Resonance { "LFO1_PANNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Panning { "LFO1_VOLUME", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO1 Volume { "LFO1_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // LFO1 Attack { "LFO1_DECAY", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // LFO1 Decay { "LFO1_SUSTAIN", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // LFO1 Sustain { "LFO1_RELEASE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // LFO1 Release { "DCA1_VOLUME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCA1 Volume { "DCA1_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCA1 Attack { "DCA1_DECAY", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCA1 Decay { "DCA1_SUSTAIN", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCA1 Sustain { "DCA1_RELEASE", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCA1 Release { "OUT1_WIDTH", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // OUT1 Stereo Width { "OUT1_PANNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // OUT1 Panning { "OUT1_FXSEND", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // OUT1 FX Send { "OUT1_VOLUME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // OUT1 Volume { "DEF1_PITCHBEND",PARAM_FLOAT, 0.2f, 0.0f, 4.0f }, // DEF1 Pitchbend { "DEF1_MODWHEEL", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF1 Modwheel { "DEF1_PRESSURE", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF1 Pressure { "DEF1_VELOCITY", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF1 Velocity { "DEF1_CHANNEL", PARAM_INT, 0.0f, 0.0f, 16.0f }, // DEF1 Channel { "DEF1_MONO", PARAM_INT, 0.0f, 0.0f, 2.0f }, // DEF1 Mono { "DCO2_SHAPE1", PARAM_INT, 1.0f, 0.0f, 4.0f }, // DCO2 Wave Shape 1 { "DCO2_WIDTH1", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCO2 Wave Width 1 { "DCO2_BANDL1", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO2 Wave Bandlimit 1 { "DCO2_SYNC1", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO2 Wave Sync 1 { "DCO2_SHAPE2", PARAM_INT, 1.0f, 0.0f, 4.0f }, // DCO2 Wave Shape 2 { "DCO2_WIDTH2", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCO2 Wave Width 2 { "DCO2_BANDL2", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO2 Wave Bandlimit 2 { "DCO2_SYNC2", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // DCO2 Wave Sync 2 { "DCO2_BALANCE", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // DCO2 Balance { "DCO2_DETUNE", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCO2 Detune { "DCO2_PHASE", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO2 Phase { "DCO2_RINGMOD", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO2 Ring Mod { "DCO2_OCTAVE", PARAM_FLOAT, -2.0f, -4.0f, 4.0f }, // DCO2 Octave { "DCO2_TUNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // DCO2 Tuning { "DCO2_GLIDE", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCO2 Glide { "DCO2_ENVTIME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCO2 Env.Time { "DCF2_ENABLED", PARAM_BOOL, 1.0f, 0.0f, 1.0f }, // DCF2 Enabled { "DCF2_CUTOFF", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF2 Cutoff { "DCF2_RESO", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCF2 Resonance { "DCF2_TYPE", PARAM_INT, 0.0f, 0.0f, 3.0f }, // DCF2 Type { "DCF2_SLOPE", PARAM_INT, 0.0f, 0.0f, 3.0f }, // DCF2 Slope { "DCF2_ENVELOPE", PARAM_FLOAT, 1.0f, -1.0f, 1.0f }, // DCF2 Envelope { "DCF2_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCF2 Attack { "DCF2_DECAY", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DCF2 Decay { "DCF2_SUSTAIN", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF2 Sustain { "DCF2_RELEASE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCF2 Release { "LFO2_ENABLED", PARAM_BOOL, 1.0f, 0.0f, 1.0f }, // LFO2 Enabled { "LFO2_SHAPE", PARAM_INT, 1.0f, 0.0f, 4.0f }, // LFO2 Wave Shape { "LFO2_WIDTH", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // LFO2 Wave Width { "LFO2_BPM", PARAM_FLOAT, 180.0f, 0.0f, 360.0f }, // LFO2 BPM { "LFO2_RATE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // LFO2 Rate { "LFO2_SYNC", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // LFO2 Sync { "LFO2_SWEEP", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Sweep { "LFO2_PITCH", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Pitch { "LFO2_BALANCE", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Balance { "LFO2_RINGMOD", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Ring Mod { "LFO2_CUTOFF", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Cutoff { "LFO2_RESO", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Resonance { "LFO2_PANNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Panning { "LFO2_VOLUME", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // LFO2 Volume { "LFO2_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // LFO2 Attack { "LFO2_DECAY", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // LFO2 Decay { "LFO2_SUSTAIN", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // LFO2 Sustain { "LFO2_RELEASE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // LFO2 Release { "DCA2_VOLUME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // DCA2 Volume { "DCA2_ATTACK", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // DCA2 Attack { "DCA2_DECAY", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCA2 Decay { "DCA2_SUSTAIN", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // DCA2 Sustain { "DCA2_RELEASE", PARAM_FLOAT, 0.1f, 0.0f, 1.0f }, // DCA2 Release { "OUT2_WIDTH", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // OUT2 Stereo Width { "OUT2_PANNING", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // OUT2 Panning { "OUT2_FXSEND", PARAM_FLOAT, 1.0f, 0.0f, 1.0f }, // OUT2 FX Send { "OUT2_VOLUME", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // OUT2 Volume { "DEF2_PITCHBEND",PARAM_FLOAT, 0.2f, 0.0f, 4.0f }, // DEF2 Pitchbend { "DEF2_MODWHEEL", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF2 Modwheel { "DEF2_PRESSURE", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF2 Pressure { "DEF2_VELOCITY", PARAM_FLOAT, 0.2f, 0.0f, 1.0f }, // DEF2 Velocity { "DEF2_CHANNEL", PARAM_INT, 0.0f, 0.0f, 16.0f }, // DEF2 Channel { "DEF2_MONO", PARAM_INT, 0.0f, 0.0f, 2.0f }, // DEF2 Mono { "CHO1_WET", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Chorus Wet { "CHO1_DELAY", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Chorus Delay { "CHO1_FEEDB", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Chorus Feedback { "CHO1_RATE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Chorus Rate { "CHO1_MOD", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Chorus Modulation { "FLA1_WET", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Flanger Wet { "FLA1_DELAY", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Flanger Delay { "FLA1_FEEDB", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Flanger Feedback { "FLA1_DAFT", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Flanger Daft { "PHA1_WET", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Phaser Wet { "PHA1_RATE", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Phaser Rate { "PHA1_FEEDB", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Phaser Feedback { "PHA1_DEPTH", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Phaser Depth { "PHA1_DAFT", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Phaser Daft { "DEL1_WET", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Delay Wet { "DEL1_DELAY", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Delay Delay { "DEL1_FEEDB", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Delay Feedback { "DEL1_BPM", PARAM_FLOAT, 180.0f, 0.0f, 360.0f }, // Delay BPM { "REV1_WET", PARAM_FLOAT, 0.0f, 0.0f, 1.0f }, // Reverb Wet { "REV1_ROOM", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Reverb Room { "REV1_DAMP", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Reverb Damp { "REV1_FEEDB", PARAM_FLOAT, 0.5f, 0.0f, 1.0f }, // Reverb Feedback { "REV1_WIDTH", PARAM_FLOAT, 0.0f, -1.0f, 1.0f }, // Reverb Width { "DYN1_COMPRESS", PARAM_BOOL, 0.0f, 0.0f, 1.0f }, // Dynamic Compressor { "DYN1_LIMITER", PARAM_BOOL, 1.0f, 0.0f, 1.0f }, // Dynamic Limiter { "KEY1_LOW", PARAM_INT, 0.0f, 0.0f, 127.0f }, // Keyboard Low { "KEY1_HIGH", PARAM_INT, 127.0f, 0.0f, 127.0f } // Keyboard High }; const char *synthv1_param::paramName ( synthv1::ParamIndex index ) { return synthv1_params[index].name; } float synthv1_param::paramDefaultValue ( synthv1::ParamIndex index ) { return synthv1_params[index].def; } float synthv1_param::paramSafeValue ( synthv1::ParamIndex index, float fValue ) { const ParamInfo& param = synthv1_params[index]; if (param.type == PARAM_BOOL) return (fValue > 0.5f ? 1.0f : 0.0f); if (fValue < param.min) return param.min; if (fValue > param.max) return param.max; if (param.type == PARAM_INT) return ::rintf(fValue); else return fValue; } float synthv1_param::paramValue ( synthv1::ParamIndex index, float fScale ) { const ParamInfo& param = synthv1_params[index]; if (param.type == PARAM_BOOL) return (fScale > 0.5f ? 1.0f : 0.0f); const float fValue = param.min + fScale * (param.max - param.min); if (param.type == PARAM_INT) return ::rintf(fValue); else return fValue; } float synthv1_param::paramScale ( synthv1::ParamIndex index, float fValue ) { const ParamInfo& param = synthv1_params[index]; if (param.type == PARAM_BOOL) return (fValue > 0.5f ? 1.0f : 0.0f); const float fScale = (fValue - param.min) / (param.max - param.min); if (param.type == PARAM_INT) return ::rintf(fScale); else return fScale; } bool synthv1_param::paramFloat ( synthv1::ParamIndex index ) { return (synthv1_params[index].type == PARAM_FLOAT); } // Preset initialization method. bool synthv1_param::newPreset ( synthv1 *pSynth ) { if (pSynth == nullptr) return false; const bool bRunning = pSynth->running(false); synthv1_sched::sync_reset(); pSynth->stabilize(); pSynth->reset(); synthv1_sched::sync_pending(); pSynth->running(bRunning); return true; } // Preset serialization methods. bool synthv1_param::loadPreset ( synthv1 *pSynth, const QString& sPresetFile ) { const bool bRunning = pSynth->running(false); synthv1_sched::sync_reset(); const bool bLoaded = loadPresetEx(pSynth, sPresetFile); synthv1_sched::sync_pending(); pSynth->running(bRunning); return bLoaded; } bool synthv1_param::loadPresetEx ( synthv1 *pSynth, const QString& sPresetFile ) { if (pSynth == nullptr) return false; QFile file(sPresetFile); if (!file.open(QIODevice::ReadOnly)) return false; pSynth->setTuningEnabled(false); pSynth->reset(); static QHash s_hash; if (s_hash.isEmpty()) { for (uint32_t i = 0; i < synthv1::NUM_PARAMS; ++i) { const synthv1::ParamIndex index = synthv1::ParamIndex(i); s_hash.insert(synthv1_param::paramName(index), index); } } const QFileInfo fi(sPresetFile); const QDir currentDir(QDir::current()); QDir::setCurrent(fi.absolutePath()); QDomDocument doc(PROJECT_NAME); if (doc.setContent(&file)) { QDomElement ePreset = doc.documentElement(); if (ePreset.tagName() == "preset") { // && ePreset.attribute("name") == fi.completeBaseName()) { for (QDomNode nChild = ePreset.firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; if (eChild.tagName() == "params") { for (QDomNode nParam = eChild.firstChild(); !nParam.isNull(); nParam = nParam.nextSibling()) { QDomElement eParam = nParam.toElement(); if (eParam.isNull()) continue; if (eParam.tagName() == "param") { synthv1::ParamIndex index = synthv1::ParamIndex( eParam.attribute("index").toULong()); const QString& sName = eParam.attribute("name"); if (!sName.isEmpty()) { if (!s_hash.contains(sName)) continue; index = s_hash.value(sName); } const float fValue = eParam.text().toFloat(); pSynth->setParamValue(index, synthv1_param::paramSafeValue(index, fValue)); } } } else if (eChild.tagName() == "tuning") { synthv1_param::loadTuning(pSynth, eChild); } } } } file.close(); pSynth->stabilize(); pSynth->reset(); QDir::setCurrent(currentDir.absolutePath()); return true; } bool synthv1_param::loadPresetName ( synthv1 *pSynth, const QString& sPreset ) { if (pSynth == nullptr) return false; if (sPreset.isEmpty()) return false; synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return false; const QString& sPresetFile = pConfig->presetFile(sPreset); if (sPresetFile.isEmpty()) return false; if (!QFileInfo::exists(sPresetFile)) return false; const bool bRunning = pSynth->running(false); const bool bLoaded = loadPresetEx(pSynth, sPresetFile); pSynth->running(bRunning); return bLoaded; } bool synthv1_param::savePreset ( synthv1 *pSynth, const QString& sPresetFile, bool bSymLink ) { if (pSynth == nullptr) return false; pSynth->stabilize(); const QFileInfo fi(sPresetFile); const QDir currentDir(QDir::current()); QDir::setCurrent(fi.absolutePath()); QDomDocument doc(PROJECT_NAME); QDomElement ePreset = doc.createElement("preset"); ePreset.setAttribute("name", fi.completeBaseName()); ePreset.setAttribute("version", PROJECT_VERSION); QDomElement eParams = doc.createElement("params"); for (uint32_t i = 0; i < synthv1::NUM_PARAMS; ++i) { QDomElement eParam = doc.createElement("param"); const synthv1::ParamIndex index = synthv1::ParamIndex(i); eParam.setAttribute("index", QString::number(i)); eParam.setAttribute("name", synthv1_param::paramName(index)); const float fValue = pSynth->paramValue(index); eParam.appendChild(doc.createTextNode(QString::number(fValue))); eParams.appendChild(eParam); } ePreset.appendChild(eParams); if (pSynth->isTuningEnabled()) { QDomElement eTuning = doc.createElement("tuning"); synthv1_param::saveTuning(pSynth, doc, eTuning, bSymLink); ePreset.appendChild(eTuning); } doc.appendChild(ePreset); QFile file(fi.filePath()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; QTextStream(&file) << doc.toString(); file.close(); QDir::setCurrent(currentDir.absolutePath()); return true; } // Tuning serialization methods. void synthv1_param::loadTuning ( synthv1 *pSynth, const QDomElement& eTuning ) { if (pSynth == nullptr) return; pSynth->setTuningEnabled(eTuning.attribute("enabled").toInt() > 0); for (QDomNode nChild = eTuning.firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; if (eChild.tagName() == "enabled") { pSynth->setTuningEnabled(eChild.text().toInt() > 0); } if (eChild.tagName() == "ref-pitch") { pSynth->setTuningRefPitch(eChild.text().toFloat()); } else if (eChild.tagName() == "ref-note") { pSynth->setTuningRefNote(eChild.text().toInt()); } else if (eChild.tagName() == "scale-file") { const QString& sScaleFile = eChild.text(); const QByteArray aScaleFile = synthv1_param::loadFilename(sScaleFile).toUtf8(); pSynth->setTuningScaleFile(aScaleFile.constData()); } else if (eChild.tagName() == "keymap-file") { const QString& sKeyMapFile = eChild.text(); const QByteArray aKeyMapFile = synthv1_param::loadFilename(sKeyMapFile).toUtf8(); pSynth->setTuningKeyMapFile(aKeyMapFile.constData()); } } // Consolidate tuning state... pSynth->updateTuning(); } void synthv1_param::saveTuning ( synthv1 *pSynth, QDomDocument& doc, QDomElement& eTuning, bool bSymLink ) { if (pSynth == nullptr) return; eTuning.setAttribute("enabled", int(pSynth->isTuningEnabled())); QDomElement eRefPitch = doc.createElement("ref-pitch"); eRefPitch.appendChild(doc.createTextNode( QString::number(pSynth->tuningRefPitch()))); eTuning.appendChild(eRefPitch); QDomElement eRefNote = doc.createElement("ref-note"); eRefNote.appendChild(doc.createTextNode( QString::number(pSynth->tuningRefNote()))); eTuning.appendChild(eRefNote); const char *pszScaleFile = pSynth->tuningScaleFile(); if (pszScaleFile) { const QString& sScaleFile = QString::fromUtf8(pszScaleFile); if (!sScaleFile.isEmpty()) { QDomElement eScaleFile = doc.createElement("scale-file"); eScaleFile.appendChild(doc.createTextNode( QDir::current().relativeFilePath( synthv1_param::saveFilename(sScaleFile, bSymLink)))); eTuning.appendChild(eScaleFile); } } const char *pszKeyMapFile = pSynth->tuningKeyMapFile(); if (pszKeyMapFile) { const QString& sKeyMapFile = QString::fromUtf8(pszKeyMapFile); if (!sKeyMapFile.isEmpty()) { QDomElement eKeyMapFile = doc.createElement("keymap-file"); eKeyMapFile.appendChild(doc.createTextNode( QDir::current().relativeFilePath( synthv1_param::saveFilename(sKeyMapFile, bSymLink)))); eTuning.appendChild(eKeyMapFile); } } } // Load/save and convert canonical/absolute filename helpers. QString synthv1_param::loadFilename ( const QString& sFilename ) { QFileInfo fi(sFilename); if (fi.isSymLink()) fi.setFile(fi.symLinkTarget()); return fi.canonicalFilePath(); } QString synthv1_param::saveFilename ( const QString& sFilename, bool bSymLink ) { QFileInfo fi(sFilename); if (bSymLink && fi.absolutePath() != QDir::current().absolutePath()) { const QString& sPath = fi.absoluteFilePath(); const QString& sName = fi.baseName(); const QString& sExt = fi.completeSuffix(); const QString& sLink = sName + '-' + QString::number(qHash(sPath), 16) + '.' + sExt; QFile(sPath).link(sLink); fi.setFile(QDir::current(), sLink); } else if (fi.isSymLink()) fi.setFile(fi.symLinkTarget()); return fi.absoluteFilePath(); } // end of synthv1_param.cpp synthv1-1.5.1/src/PaxHeaders/synthv1widget_palette.ui0000644000000000000000000000013215270627745017713 xustar0030 mtime=1793273829.975582031 30 atime=1793273829.975582031 30 ctime=1793273829.975582031 synthv1-1.5.1/src/synthv1widget_palette.ui0000644000175000001440000001723015270627745017706 0ustar00rncbcusers rncbc aka Rui Nuno Capela synthv1 - an old-school polyphonic synthesizer Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. synthv1widget_palette 0 0 534 640 0 0 Color Themes Name 0 0 320 0 Current color palette name true QComboBox::NoInsert Save current color palette name Save :/images/presetSave.png Delete current color palette name Delete :/images/presetDelete.png Palette 280 360 Current color palette true Generate: 0 0 Qt::StrongFocus Base color to generate palette Reset all current palette colors Reset :/images/itemReset.png Qt::Horizontal 8 20 Import a custom color theme (palette) from file Import... :/images/presetOpen.png Export a custom color theme (palette) to file Export... :/images/presetSave.png Qt::Horizontal 8 20 Show Details Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok synthv1widget_palette::ColorButton nameCombo saveButton deleteButton paletteView generateButton resetButton importButton exportButton detailsCheck dialogButtons synthv1-1.5.1/src/PaxHeaders/synthv1widget_programs.cpp0000644000000000000000000000013215270627745020254 xustar0030 mtime=1793273829.975582031 30 atime=1793273829.975582031 30 ctime=1793273829.975582031 synthv1-1.5.1/src/synthv1widget_programs.cpp0000644000175000001440000004731515270627745020256 0ustar00rncbcusers// synthv1widget_programs.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1widget_programs.h" #include "synthv1_programs.h" #include "synthv1_presets.h" #include "synthv1_config.h" #include "synthv1widget_presets.h" #include #include #include #include //---------------------------------------------------------------------------- // synthv1widget_programs::ItemDelegate -- Custom (tree) list item delegate. class synthv1widget_programs::ItemDelegate : public QStyledItemDelegate { public: // ctor. ItemDelegate(QObject *pParent = nullptr); // painting void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index) const; // editing QWidget *createEditor(QWidget *pParent, const QStyleOptionViewItem& option, const QModelIndex& index) const; void setEditorData(QWidget *pEditor, const QModelIndex& index) const; void setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex& index) const; }; //---------------------------------------------------------------------------- // synthv1widget_programs_item_delegate -- Custom (tree) list item delegate. // ctor. synthv1widget_programs::ItemDelegate::ItemDelegate ( QObject *pParent ) : QStyledItemDelegate(pParent) { } // painting // void synthv1widget_programs::ItemDelegate::paint ( QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem opt = option; synthv1widget_programs *pWidget = qobject_cast(parent()); if (pWidget) { QTreeWidgetItem *pItem = pWidget->itemFromIndex(index); if (pItem && pItem->parent() == nullptr) { if (index.column() == 1) opt.font.setWeight(QFont::Bold); if (pWidget->currentItem() == pItem) { opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::HighlightedText)); } } } QStyledItemDelegate::paint(pPainter, opt, index); } QSize synthv1widget_programs::ItemDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const { return QStyledItemDelegate::sizeHint(option, index) + QSize(4, 4); } // editing // QWidget *synthv1widget_programs::ItemDelegate::createEditor ( QWidget *pParent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index ) const { QWidget *pEditor = nullptr; switch (index.column()) { case 0: // Data. { QSpinBox *pSpinBox = new QSpinBox(pParent); pSpinBox->setMinimum(0); if (index.parent().isValid()) pSpinBox->setMaximum(127); else pSpinBox->setMaximum(16383); pEditor = pSpinBox; break; } case 1: // Text. { if (index.parent().isValid()) { synthv1widget_presets::ComboBox *pComboBox = new synthv1widget_presets::ComboBox(pParent); pComboBox->setEditable(false); synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig) (pComboBox->presetsView())->loadPresets(&(pConfig->presets)); pEditor = pComboBox; } else { QLineEdit *pLineEdit = new QLineEdit(pParent); pEditor = pLineEdit; } break; } default: break; } #ifdef CONFIG_DEBUG_0 qDebug("synthv1widget_programs::ItemDelegate::createEditor(%p, %d, %d) = %p", pParent, index.row(), index.column(), pEditor); #endif return pEditor; } void synthv1widget_programs::ItemDelegate::setEditorData ( QWidget *pEditor, const QModelIndex& index ) const { #ifdef CONFIG_DEBUG_0 qDebug("synthv1widget_programs::ItemDelegate::setEditorData(%p, %d, %d)", pEditor, index.row(), index.column()); #endif switch (index.column()) { case 0: // Data. { const QString& sData = index.data().toString(); // = index.model()->data(index, Qt::DisplayRole).toString(); QSpinBox *pSpinBox = qobject_cast (pEditor); if (pSpinBox) { const int iData = sData.section("=", 0, 0).toInt(); pSpinBox->setValue(iData); } break; } case 1: // Text. { const QString& sText = index.data().toString(); // = index.model()->data(index, Qt::DisplayRole).toString(); if (index.parent().isValid()) { synthv1widget_presets::ComboBox *pComboBox = qobject_cast (pEditor); if (pComboBox) { pComboBox->setCurrentPreset(sText); } } else { QLineEdit *pLineEdit = qobject_cast (pEditor); if (pLineEdit) pLineEdit->setText(sText); } break; } default: break; } } void synthv1widget_programs::ItemDelegate::setModelData ( QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex& index ) const { #ifdef CONFIG_DEBUG_0 qDebug("synthv1widget_programs::ItemDelegate::setModelData(%p, %d, %d)", pEditor, index.row(), index.column()); #endif switch (index.column()) { case 0: // Data. { QSpinBox *pSpinBox = qobject_cast (pEditor); if (pSpinBox) { const int iData = pSpinBox->value(); QString sData = QString::number(iData); if (index.parent().isValid()) sData += " ="; pModel->setData(index, sData); } break; } case 1: // Text. { if (index.parent().isValid()) { synthv1widget_presets::ComboBox *pComboBox = qobject_cast (pEditor); if (pComboBox) { const QString& sText = pComboBox->currentPreset(); pModel->setData(index, sText); } } else { QLineEdit *pLineEdit = qobject_cast (pEditor); if (pLineEdit) { const QString& sText = pLineEdit->text(); pModel->setData(index, sText); } } break; } default: break; } // Done. } //---------------------------------------------------------------------------- // synthv1widget_programs -- UI wrapper form. // ctor. synthv1widget_programs::synthv1widget_programs ( QWidget *pParent ) : QTreeWidget(pParent) { QTreeWidget::setColumnCount(2); QTreeWidget::setRootIsDecorated(true); QTreeWidget::setAlternatingRowColors(true); QTreeWidget::setUniformRowHeights(true); QTreeWidget::setAllColumnsShowFocus(true); QTreeWidget::setSelectionBehavior(QAbstractItemView::SelectRows); QTreeWidget::setSelectionMode(QAbstractItemView::SingleSelection); QHeaderView *pHeaderView = QTreeWidget::header(); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) pHeaderView->setResizeMode(QHeaderView::ResizeToContents); #else pHeaderView->setSectionResizeMode(QHeaderView::ResizeToContents); #endif pHeaderView->hide(); QTreeWidget::setItemDelegate(new ItemDelegate(this)); QObject::connect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)), SLOT(itemChangedSlot(QTreeWidgetItem *, int))); QObject::connect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), SLOT(itemExpandedSlot(QTreeWidgetItem *))); QObject::connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem *)), SLOT(itemCollapsedSlot(QTreeWidgetItem *))); } // dtor. synthv1widget_programs::~synthv1widget_programs (void) { } // utilities. void synthv1widget_programs::loadPrograms ( synthv1_programs *pPrograms ) { QTreeWidget::clear(); QList items; QTreeWidgetItem *pCurrentItem = nullptr; const synthv1_programs::Banks& banks = pPrograms->banks(); synthv1_programs::Banks::ConstIterator bank_iter = banks.constBegin(); const synthv1_programs::Banks::ConstIterator& bank_end = banks.constEnd(); for ( ; bank_iter != bank_end; ++bank_iter) { synthv1_programs::Bank *pBank = bank_iter.value(); QTreeWidgetItem *pBankItem = new QTreeWidgetItem(this); pBankItem->setIcon(0, QIcon(":/images/presetBankOpen.png")); pBankItem->setText(0, QString::number(pBank->id())); pBankItem->setText(1, pBank->name()); pBankItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable); pBankItem->setData(0, Qt::UserRole, pBank->id()); const synthv1_programs::Progs& progs = pBank->progs(); synthv1_programs::Progs::ConstIterator prog_iter = progs.constBegin(); const synthv1_programs::Progs::ConstIterator& prog_end = progs.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { synthv1_programs::Prog *pProg = prog_iter.value(); QTreeWidgetItem *pProgItem = new QTreeWidgetItem(pBankItem); pProgItem->setIcon(1, QIcon(":/images/synthv1_preset.png")); pProgItem->setText(0, QString::number(pProg->id()) + " ="); pProgItem->setText(1, pProg->name()); pProgItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable); pProgItem->setData(0, Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter)); pProgItem->setData(0, Qt::UserRole, pProg->id()); if (pBank == pPrograms->current_bank() && pProg == pPrograms->current_prog()) pCurrentItem = pProgItem; } items.append(pBankItem); } QTreeWidget::addTopLevelItems(items); QTreeWidget::expandAll(); QTreeWidget::setCurrentItem(pCurrentItem); } void synthv1widget_programs::savePrograms ( synthv1_programs *pPrograms ) { pPrograms->clear_banks(); const int iBankCount = QTreeWidget::topLevelItemCount(); for (int iBank = 0 ; iBank < iBankCount; ++iBank) { QTreeWidgetItem *pBankItem = QTreeWidget::topLevelItem(iBank); uint16_t bank_id = pBankItem->data(0, Qt::UserRole).toInt(); const QString& bank_name = pBankItem->text(1).simplified(); synthv1_programs::Bank *pBank = pPrograms->add_bank(bank_id, bank_name); const int iProgCount = pBankItem->childCount(); for (int iProg = 0 ; iProg < iProgCount; ++iProg) { QTreeWidgetItem *pProgItem = pBankItem->child(iProg); uint16_t prog_id = pProgItem->data(0, Qt::UserRole).toInt(); const QString& prog_name = pProgItem->text(1).simplified(); pBank->add_prog(prog_id, prog_name); } } } QString synthv1widget_programs::currentProgramName (void) const { QString sProgramName; const QList& selectedItems = QTreeWidget::selectedItems(); if (!selectedItems.isEmpty()) { QTreeWidgetItem *pProgItem = selectedItems.first(); sProgramName = pProgItem->text(1).simplified(); } return sProgramName; } // slots. void synthv1widget_programs::addBankItem (void) { QTreeWidget::setFocus(); QTreeWidgetItem *pBankItem = newBankItem(); if (pBankItem) { QTreeWidget::setCurrentItem(pBankItem); QTreeWidget::editItem(pBankItem, 1); } } void synthv1widget_programs::addProgramItem (void) { QTreeWidget::setFocus(); QTreeWidgetItem *pProgItem = newProgramItem(); if (pProgItem) { QTreeWidget::setCurrentItem(pProgItem); QTreeWidget::editItem(pProgItem, 1); } } void synthv1widget_programs::addPresetItems ( synthv1_presets *pPresets ) { QStringList preset_list = pPresets->preset_list(); QStringListIterator bank_iter(pPresets->bank_list()); while (bank_iter.hasNext()) { const QString& sBank = bank_iter.next(); synthv1_presets::Bank *pBank = pPresets->find_bank(sBank); if (pBank) preset_list.append(pBank->preset_list()); } // Skip presets already mapped to programs... int iBankData = 0; QTreeWidgetItem *pBank0Item = nullptr; const int iBankCount = QTreeWidget::topLevelItemCount(); for (int iBank = 0; iBank < iBankCount; ++iBank) { QTreeWidgetItem *pBankItem = QTreeWidget::topLevelItem(iBank); const int iData = pBankItem->data(0, Qt::UserRole).toInt(); if (iData == 0) pBank0Item = pBankItem; if (iBankData < iData) iBankData = iData; if (iBankData >= 16383) return; const int iProgCount = pBankItem->childCount(); for (int iProg = 0; iProg < iProgCount; ++iProg) { QTreeWidgetItem *pProgItem = pBankItem->child(iProg); preset_list.removeAll(pProgItem->text(1)); } } if (preset_list.isEmpty()) return; QStringListIterator preset_iter(preset_list); while (preset_iter.hasNext()) { const QString& sPreset = preset_iter.next(); synthv1_presets::Bank *pBank = pPresets->find_preset_bank(sPreset); QTreeWidgetItem *pBankItem = nullptr; if (pBank) { const QString& sBank = pBank->name(); const QList& items = QTreeWidget::findItems( sBank, Qt::MatchExactly|Qt::MatchRecursive, 1); QListIterator iter(items); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->parent() == nullptr) { pBankItem = pItem; break; } } if (pBankItem == nullptr) { pBankItem = new QTreeWidgetItem(QStringList() << QString::number(++iBankData) << sBank); pBankItem->setIcon(0, QIcon(":/images/presetBank.png")); pBankItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable); pBankItem->setData(0, Qt::UserRole, iBankData); QTreeWidget::addTopLevelItem(pBankItem); } } else // Check for default bank... if (pBank0Item == nullptr) { pBank0Item = new QTreeWidgetItem(QStringList() << QString::number(0) << tr("Bank 0")); pBank0Item->setIcon(0, QIcon(":/images/presetBank.png")); pBank0Item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable); pBank0Item->setData(0, Qt::UserRole, 0); QTreeWidget::insertTopLevelItem(0, pBank0Item); } if (pBankItem == nullptr) pBankItem = pBank0Item; // Add program, filling in the gaps... int iProg = 0; int iProgData = 0; const int iProgCount = pBankItem->childCount(); for ( ; iProg < iProgCount; ++iProg) { QTreeWidgetItem *pProgItem = pBankItem->child(iProg); const int iData = pProgItem->data(0, Qt::UserRole).toInt(); if (iData != iProg) { iProgData = iProg - 1; break; } iProgData = iData; } if (iProgCount > 0) ++iProgData; if (iProgData >= 128) continue; QTreeWidgetItem *pProgItem = new QTreeWidgetItem(QStringList() << QString::number(iProgData) + " =" << sPreset); pProgItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable); pProgItem->setData(0, Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter)); pProgItem->setData(0, Qt::UserRole, iProgData); pProgItem->setIcon(1, QIcon(":/images/synthv1_preset.png")); pBankItem->insertChild(iProg, pProgItem); pBankItem->setExpanded(true); } } // factory methods. QTreeWidgetItem *synthv1widget_programs::newBankItem (void) { QTreeWidgetItem *pItem = QTreeWidget::currentItem(); QTreeWidgetItem *pBankItem = (pItem ? pItem->parent() : nullptr); if (pBankItem == nullptr) pBankItem = pItem; int iBank = 0; int iBankData = 0; if (pBankItem) { iBankData = pBankItem->data(0, Qt::UserRole).toInt() + 1; if (iBankData > 16383) iBankData = 0; else iBank = QTreeWidget::indexOfTopLevelItem(pBankItem) + 1; } const int iBankCount = QTreeWidget::topLevelItemCount(); for ( ; iBank < iBankCount; ++iBank) { pBankItem = QTreeWidget::topLevelItem(iBank); if (iBankData < pBankItem->data(0, Qt::UserRole).toInt()) break; if (++iBankData > 16383) return nullptr; } pBankItem = new QTreeWidgetItem(QStringList() << QString::number(iBankData) << tr("Bank %1").arg(iBankData)); pBankItem->setIcon(0, QIcon(":/images/presetBank.png")); pBankItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable); pBankItem->setData(0, Qt::UserRole, iBankData); QTreeWidget::insertTopLevelItem(iBank, pBankItem); return pBankItem; } QTreeWidgetItem *synthv1widget_programs::newProgramItem (void) { QTreeWidgetItem *pItem = QTreeWidget::currentItem(); QTreeWidgetItem *pBankItem = (pItem ? pItem->parent() : nullptr); QTreeWidgetItem *pProgItem = nullptr; if (pBankItem == nullptr) pBankItem = pItem; else pProgItem = pItem; if (pBankItem == nullptr) pBankItem = QTreeWidget::topLevelItem(0); if (pBankItem == nullptr) pBankItem = newBankItem(); if (pBankItem == nullptr) return nullptr; const int iBankData = pBankItem->data(0, Qt::UserRole).toInt(); int iProg = 0; int iProgData = 0; if (pProgItem) { iProgData = pProgItem->data(0, Qt::UserRole).toInt() + 1; if (iProgData > 127) iProgData = 0; else iProg = pBankItem->indexOfChild(pProgItem) + 1; } const int iProgCount = pBankItem->childCount(); for ( ; iProg < iProgCount; ++iProg) { pProgItem = pBankItem->child(iProg); if (iProgData < pProgItem->data(0, Qt::UserRole).toInt()) break; if (++iProgData > 127) return nullptr; } QString sProgram = tr("Program %1.%2").arg(iBankData).arg(iProgData); synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig) { QStringList preset_list; const QStringList& bank_list = pConfig->presets.bank_list(); if (iBankData < bank_list.count()) { const QString& sBank = bank_list.at(iBankData); synthv1_presets::Bank *pBank = pConfig->presets.find_bank(sBank); if (pBank) preset_list = pBank->preset_list(); } else { preset_list = pConfig->presets.preset_list(); } if (iProgData < preset_list.count()) sProgram = preset_list.at(iProgData); } pProgItem = new QTreeWidgetItem(QStringList() << QString::number(iProgData) + " =" << sProgram); pProgItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable); pProgItem->setData(0, Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter)); pProgItem->setData(0, Qt::UserRole, iProgData); pProgItem->setIcon(1, QIcon(":/images/synthv1_preset.png")); pBankItem->insertChild(iProg, pProgItem); pBankItem->setExpanded(true); return pProgItem; } void synthv1widget_programs::itemChangedSlot ( QTreeWidgetItem *pItem, int ) { const int iData = pItem->text(0).section("=", 0, 0).toInt(); if (iData == pItem->data(0, Qt::UserRole).toInt()) return; const bool bBlockSignals = QTreeWidget::blockSignals(true); QTreeWidgetItem *pBankItem = pItem->parent(); if (pBankItem) { int iProg, iProgData = 0; const int iOldProg = pBankItem->indexOfChild(pItem); pItem = pBankItem->takeChild(iOldProg); const int iProgCount = pBankItem->childCount(); for (iProg = 0; iProg < iProgCount; ++iProg) { QTreeWidgetItem *pProgItem = pBankItem->child(iProg); iProgData = pProgItem->data(0, Qt::UserRole).toInt(); if (iProgData >= iData) break; } if (iProgData == iData) { iProg = iOldProg; iProgData = pItem->data(0, Qt::UserRole).toInt(); pItem->setText(0, QString::number(iProgData) + " ="); } else pItem->setData(0, Qt::UserRole, iData); pBankItem->insertChild(iProg, pItem); } else { int iBank, iBankData = 0; const bool bExpanded = pItem->isExpanded(); const int iOldBank = QTreeWidget::indexOfTopLevelItem(pItem); pItem = QTreeWidget::takeTopLevelItem(iOldBank); const int iBankCount = QTreeWidget::topLevelItemCount(); for (iBank = 0; iBank < iBankCount; ++iBank) { pBankItem = QTreeWidget::topLevelItem(iBank); iBankData = pBankItem->data(0, Qt::UserRole).toInt(); if (iBankData >= iData) break; } if (iBankData == iData) { iBank = iOldBank; iBankData = pItem->data(0, Qt::UserRole).toInt(); pItem->setText(0, QString::number(iBankData)); } else pItem->setData(0, Qt::UserRole, iData); QTreeWidget::insertTopLevelItem(iBank, pItem); pItem->setExpanded(bExpanded); } QTreeWidget::setCurrentItem(pItem); QTreeWidget::blockSignals(bBlockSignals); } void synthv1widget_programs::itemExpandedSlot ( QTreeWidgetItem *pItem ) { if (pItem->parent() == nullptr) pItem->setIcon(0, QIcon(":/images/presetBankOpen.png")); } void synthv1widget_programs::itemCollapsedSlot ( QTreeWidgetItem *pItem ) { if (pItem->parent() == nullptr) pItem->setIcon(0, QIcon(":/images/presetBank.png")); } // end of synthv1widget_programs.cpp synthv1-1.5.1/src/PaxHeaders/synthv1_config.cpp0000644000000000000000000000013215270627745016463 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_config.cpp0000644000175000001440000004446415270627745016467 0ustar00rncbcusers// synthv1_config.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_config.h" #include "synthv1_programs.h" #include "synthv1_controls.h" #include #include #ifndef CONFIG_DATADIR #define CONFIG_DATADIR CONFIG_PREFIX "/share" #endif // Local static consts. static const char *PresetsGroup = "/Presets"; static const char *PresetsListKey = "/PresetList"; static const char *PresetsBanksGroup = "/Banks"; static const char *PresetsBankListKey = "/BankList"; static const char *PresetsConfGroup = "/PresetsConf"; static const char *PresetsConfListKey = "/ConfList"; static const char *ProgramsGroup = "/Programs"; static const char *BankPrefix = "/Bank_"; static const char *ControlsGroup = "/Controllers"; static const char *ControlPrefix = "/Control"; //------------------------------------------------------------------------- // synthv1_config - Prototype settings structure (pseudo-singleton). // // Singleton instance accessor (static). synthv1_config *synthv1_config::g_pSettings = nullptr; synthv1_config *synthv1_config::getInstance (void) { return g_pSettings; } // Constructor. synthv1_config::synthv1_config (void) : QSettings(PROJECT_DOMAIN, PROJECT_NAME) { g_pSettings = this; load(); } // Default destructor. synthv1_config::~synthv1_config (void) { save(); g_pSettings = nullptr; } // Preset utility methods. QString synthv1_config::presetFile ( const QString& sPreset ) { QSettings::beginGroup(PresetsGroup); const QString sPresetFile(QSettings::value(sPreset).toString()); QSettings::endGroup(); return sPresetFile; } void synthv1_config::setPresetFile ( const QString& sPreset, const QString& sPresetFile ) { QSettings::beginGroup(PresetsGroup); QSettings::setValue(sPreset, sPresetFile); QSettings::endGroup(); } void synthv1_config::removePreset ( const QString& sPreset ) { QSettings::beginGroup(PresetsGroup); const QString& sPresetFile = QSettings::value(sPreset).toString(); if (QFileInfo::exists(sPresetFile)) QFile(sPresetFile).remove(); QSettings::remove(sPreset); QSettings::endGroup(); } // Presets utility methods. void synthv1_config::importPresets ( const QString& sFilename, synthv1_presets *pPresets ) { QSettings settings(sFilename, QSettings::IniFormat); struct AbsPath : MapPath { AbsPath(const QString& sFilename) : dir(QFileInfo(sFilename).absolutePath()) {} QString operator()(const QString& sPath) const override { return QFileInfo(dir.filePath(sPath)).canonicalFilePath(); } QDir dir; }; loadPresets(&settings, pPresets, AbsPath(sFilename)); } void synthv1_config::loadPresets ( QSettings *pSettings, synthv1_presets *pPresets, const MapPath& mapPath ) { QStringList bank_list; pSettings->beginGroup(PresetsBankListKey); bank_list = pSettings->value(PresetsBankListKey).toStringList(); pSettings->endGroup(); pSettings->beginGroup(PresetsBanksGroup); QStringListIterator bank_iter(bank_list); while (bank_iter.hasNext()) { const QString& sBank = bank_iter.next(); synthv1_presets::Bank *pBank = pPresets->add_bank(sBank); const QStringList& preset_list = pSettings->value(sBank).toStringList(); QStringListIterator preset_iter(preset_list); while (preset_iter.hasNext()) { const QString& sPreset = preset_iter.next(); pBank->add_preset(sPreset); } } pSettings->endGroup(); QStringList preset_list; pSettings->beginGroup(PresetsListKey); preset_list = pSettings->value(PresetsListKey).toStringList(); pSettings->endGroup(); pSettings->beginGroup(PresetsGroup); const QStringList& preset_keys = pSettings->childKeys(); QStringListIterator preset_key(preset_keys); while (preset_key.hasNext()) { const QString& sPresetKey = preset_key.next(); if (!preset_list.contains(sPresetKey)) preset_list.append(sPresetKey); } QStringListIterator preset_iter(preset_list); while (preset_iter.hasNext()) { const QString& sPreset = preset_iter.next(); synthv1_presets::Preset *pPreset = pPresets->find_preset(sPreset); if (pPreset == nullptr) pPreset = pPresets->add_preset(sPreset); const QString& sPresetFile = mapPath(pSettings->value(sPreset).toString()); if (!sPresetFile.isEmpty() && QFileInfo::exists(sPresetFile)) { pPreset->set_file(sPresetFile); } else { pPresets->remove_preset(sPreset); } } pSettings->endGroup(); // cleanup database from dangling banks/presets... // bank_iter.toFront(); while (bank_iter.hasNext()) { const QString& sBank = bank_iter.next(); synthv1_presets::Bank *pBank = pPresets->find_bank(sBank); if (pBank == nullptr) { pPresets->remove_bank(sBank); } else { const QStringList& preset_list = pBank->preset_list(); QStringListIterator preset_iter(preset_list); while (preset_iter.hasNext()) { const QString& sPreset = preset_iter.next(); if (pPresets->find_preset(sPreset) == nullptr) pBank->remove_preset(sPreset); } } } } void synthv1_config::loadPresets (void) { presets.clear_banks(); presets.clear_presets(); loadPresets(this, &presets); if (loadPresetsConf(this, &presets) > 0) savePresets(); } void synthv1_config::exportPresets ( const QString& sFilename, synthv1_presets *pPresets ) { QSettings settings(sFilename, QSettings::IniFormat); struct RelPath : MapPath { RelPath(const QString& sFilename) : dir(QFileInfo(sFilename).absolutePath()) {} QString operator()(const QString& sPath) const override { return dir.relativeFilePath(sPath); } QDir dir; }; savePresets(&settings, pPresets, RelPath(sFilename)); } void synthv1_config::savePresets ( QSettings *pSettings, synthv1_presets *pPresets, const MapPath& mapPath ) { const QStringList& bank_list = pPresets->bank_list(); pSettings->beginGroup(PresetsBankListKey); if (bank_list.isEmpty()) pSettings->remove(PresetsBankListKey); else pSettings->setValue(PresetsBankListKey, bank_list); pSettings->endGroup(); pSettings->beginGroup(PresetsBanksGroup); const QStringList& bank_keys = pSettings->childKeys(); QStringListIterator bank_key(bank_keys); while (bank_key.hasNext()) pSettings->remove(bank_key.next()); QStringListIterator bank_iter(bank_list); while (bank_iter.hasNext()) { const QString& sBank = bank_iter.next(); synthv1_presets::Bank *pBank = pPresets->find_bank(sBank); if (pBank == nullptr) continue; QStringList preset_list; QStringListIterator bank_preset_iter(pBank->preset_list()); while (bank_preset_iter.hasNext()) { const QString& sPreset = bank_preset_iter.next(); preset_list.append(sPreset); } if (preset_list.isEmpty()) pSettings->remove(sBank); else pSettings->setValue(sBank, preset_list); } pSettings->endGroup(); const QStringList& preset_list = pPresets->preset_list(); pSettings->beginGroup(PresetsListKey); if (preset_list.isEmpty()) pSettings->remove(PresetsListKey); else pSettings->setValue(PresetsListKey, preset_list); pSettings->endGroup(); pSettings->beginGroup(PresetsGroup); const QStringList& preset_keys = pSettings->childKeys(); QStringListIterator preset_key(preset_keys); while (preset_key.hasNext()) pSettings->remove(preset_key.next()); const synthv1_presets::Presets& presets_map = pPresets->presets(); synthv1_presets::Presets::ConstIterator presets_iter = presets_map.constBegin(); const synthv1_presets::Presets::ConstIterator& presets_end = presets_map.constEnd(); for ( ; presets_iter != presets_end; ++presets_iter) { const QString& sPreset = presets_iter.key(); synthv1_presets::Preset *pPreset = presets_iter.value(); const QString& sPresetFile = mapPath(pPreset->file()); if (!sPresetFile.isEmpty()) { pSettings->setValue(sPreset, sPresetFile); } else { pPresets->remove_preset(sPreset); } } pSettings->endGroup(); pSettings->sync(); } void synthv1_config::savePresets (void) { savePresets(this, &presets); } // Factory presets(.conf) loading... // int synthv1_config::loadPresetsConf ( QSettings *pSettings, synthv1_presets *pPresets ) { int nconfs = 0; pSettings->beginGroup(PresetsConfGroup); QStringList confs = pSettings->value(PresetsConfListKey).toStringList(); if (confs.isEmpty() || pPresets->isEmpty()) { const QChar sep = QDir::separator(); QString sPresetsPath; sPresetsPath.append(CONFIG_DATADIR); sPresetsPath.append(sep); sPresetsPath.append(PROJECT_NAME); sPresetsPath.append(sep); sPresetsPath.append("preset"); QDir dir(sPresetsPath); if (dir.exists() && dir.isReadable()) { pSettings->remove(PresetsConfListKey); nconfs += loadPresetsConfDir(pPresets, dir, confs); if (nconfs > 0) pSettings->setValue(PresetsConfListKey, confs); } } pSettings->endGroup(); return nconfs; } int synthv1_config::loadPresetsConfDir ( synthv1_presets *pPresets, const QDir& dir, QStringList& confs ) { int nconfs = 0; const QDir::Filters filters = QDir::AllDirs | QDir::NoDotAndDotDot; QStringListIterator dir_iter(dir.entryList(filters)); while (dir_iter.hasNext()) { nconfs += loadPresetsConfDir(pPresets, QDir(QFileInfo(dir, dir_iter.next()).filePath()), confs); } const QStringList filter("*." PROJECT_NAME ".conf"); QStringListIterator iter(dir.entryList(filter, QDir::Files)); while (iter.hasNext()) { const QFileInfo fi(dir, iter.next()); const QString& sFilename = fi.absoluteFilePath(); if (!confs.contains(sFilename)) { synthv1_config::importPresets(sFilename, pPresets); confs.append(sFilename); ++nconfs; } } return nconfs; } // Programs utility methods. void synthv1_config::loadPrograms ( synthv1_programs *pPrograms ) { pPrograms->clear_banks(); QSettings::beginGroup(ProgramsGroup); const QStringList& bank_keys = QSettings::childKeys(); QStringListIterator bank_iter(bank_keys); while (bank_iter.hasNext()) { const QString& bank_key = bank_iter.next(); uint16_t bank_id = bank_key.toInt(); const QString& bank_name = QSettings::value(bank_key).toString(); synthv1_programs::Bank *pBank = pPrograms->add_bank(bank_id, bank_name); QSettings::beginGroup(BankPrefix + bank_key); const QStringList& prog_keys = QSettings::childKeys(); QStringListIterator prog_iter(prog_keys); while (prog_iter.hasNext()) { const QString& prog_key = prog_iter.next(); uint16_t prog_id = prog_key.toInt(); const QString& prog_name = QSettings::value(prog_key).toString(); pBank->add_prog(prog_id, prog_name); } QSettings::endGroup(); } QSettings::endGroup(); pPrograms->enabled(bProgramsEnabled); } void synthv1_config::savePrograms ( synthv1_programs *pPrograms ) { bProgramsEnabled = pPrograms->enabled(); clearPrograms(); QSettings::beginGroup(ProgramsGroup); const synthv1_programs::Banks& banks = pPrograms->banks(); synthv1_programs::Banks::ConstIterator bank_iter = banks.constBegin(); const synthv1_programs::Banks::ConstIterator& bank_end = banks.constEnd(); for ( ; bank_iter != bank_end; ++bank_iter) { synthv1_programs::Bank *pBank = bank_iter.value(); const QString& bank_key = QString::number(pBank->id()); const QString& bank_name = pBank->name(); QSettings::setValue(bank_key, bank_name); QSettings::beginGroup(BankPrefix + bank_key); const synthv1_programs::Progs& progs = pBank->progs(); synthv1_programs::Progs::ConstIterator prog_iter = progs.constBegin(); const synthv1_programs::Progs::ConstIterator& prog_end = progs.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { synthv1_programs::Prog *pProg = prog_iter.value(); const QString& prog_key = QString::number(pProg->id()); const QString& prog_name = pProg->name(); QSettings::setValue(prog_key, prog_name); } QSettings::endGroup(); } QSettings::endGroup(); QSettings::sync(); } void synthv1_config::clearPrograms (void) { QSettings::beginGroup(ProgramsGroup); const QStringList& bank_keys = QSettings::childKeys(); QStringListIterator bank_iter(bank_keys); while (bank_iter.hasNext()) { const QString& bank_key = bank_iter.next(); QSettings::beginGroup(BankPrefix + bank_key); const QStringList& prog_keys = QSettings::childKeys(); QStringListIterator prog_iter(prog_keys); while (prog_iter.hasNext()) { const QString& prog_key = prog_iter.next(); QSettings::remove(prog_key); } QSettings::endGroup(); QSettings::remove(bank_key); } QSettings::endGroup(); } // Controllers utility methods. void synthv1_config::loadControls ( synthv1_controls *pControls ) { pControls->clear(); QSettings::beginGroup(ControlsGroup); const QStringList& keys = QSettings::childKeys(); QStringListIterator iter(keys); while (iter.hasNext()) { const QString& sKey = '/' + iter.next(); const QStringList& clist = sKey.split('_'); if (clist.at(0) == ControlPrefix) { const unsigned short channel = clist.at(1).toInt(); const synthv1_controls::Type ctype = synthv1_controls::typeFromText(clist.at(2)); synthv1_controls::Key key; key.status = ctype | (channel & 0x1f); key.param = clist.at(3).toInt(); const QStringList& vlist = QSettings::value(sKey).toStringList(); synthv1_controls::Data data; data.index = vlist.at(0).toInt(); if (vlist.count() > 1) data.flags = vlist.at(1).toInt(); pControls->add_control(key, data); } } QSettings::endGroup(); pControls->enabled(bControlsEnabled); } void synthv1_config::saveControls ( synthv1_controls *pControls ) { bControlsEnabled = pControls->enabled(); clearControls(); QSettings::beginGroup(ControlsGroup); const synthv1_controls::Map& map = pControls->map(); synthv1_controls::Map::ConstIterator iter = map.constBegin(); const synthv1_controls::Map::ConstIterator& iter_end = map.constEnd(); for ( ; iter != iter_end; ++iter) { const synthv1_controls::Key& key = iter.key(); QString sKey = ControlPrefix; sKey += '_' + QString::number(key.channel()); sKey += '_' + synthv1_controls::textFromType(key.type()); sKey += '_' + QString::number(key.param); const synthv1_controls::Data& data = iter.value(); QStringList vlist; vlist.append(QString::number(data.index)); vlist.append(QString::number(data.flags)); QSettings::setValue(sKey, vlist); } QSettings::endGroup(); QSettings::sync(); } void synthv1_config::clearControls (void) { QSettings::beginGroup(ControlsGroup); const QStringList& keys = QSettings::childKeys(); QStringListIterator iter(keys); while (iter.hasNext()) { const QString& key = iter.next(); QSettings::remove(key); } QSettings::endGroup(); } // Explicit I/O methods. void synthv1_config::load (void) { QSettings::beginGroup("/Default"); sPreset = QSettings::value("/Preset").toString(); sPresetDir = QSettings::value("/PresetDir").toString(); iKnobDialMode = QSettings::value("/KnobDialMode", 0).toInt(); iKnobEditMode = QSettings::value("/KnobEditMode", 0).toInt(); fRandomizePercent = QSettings::value("/RandomizePercent", 20.0f).toFloat(); bControlsEnabled = QSettings::value("/ControlsEnabled", false).toBool(); bProgramsEnabled = QSettings::value("/ProgramsEnabled", false).toBool(); QSettings::endGroup(); if (!QFileInfo(sPresetDir).permission(QFile::WriteUser)) sPresetDir.clear(); QSettings::beginGroup("/Dialogs"); bPresetsPreview = QSettings::value("/PresetsPreview", false).toBool(); bProgramsPreview = QSettings::value("/ProgramsPreview", false).toBool(); bUseNativeDialogs = QSettings::value("/UseNativeDialogs", false).toBool(); // Run-time special non-persistent options. bDontUseNativeDialogs = !bUseNativeDialogs; QSettings::endGroup(); QSettings::beginGroup("/Custom"); sCustomColorTheme = QSettings::value("/ColorTheme").toString(); sCustomStyleTheme = QSettings::value("/StyleTheme").toString(); QSettings::endGroup(); // Micro-tuning options. QSettings::beginGroup("/Tuning"); bTuningEnabled = QSettings::value("/Enabled", false).toBool(); fTuningRefPitch = float(QSettings::value("/RefPitch", 440.0).toDouble()); iTuningRefNote = QSettings::value("/RefNote", 69).toInt(); sTuningScaleDir = QSettings::value("/ScaleDir").toString(); sTuningScaleFile = QSettings::value("/ScaleFile").toString(); sTuningKeyMapDir = QSettings::value("/KeyMapDir").toString(); sTuningKeyMapFile = QSettings::value("/KeyMapFile").toString(); QSettings::endGroup(); // Presets database. loadPresets(); } void synthv1_config::save (void) { QSettings::beginGroup("/Program"); QSettings::setValue("/Version", PROJECT_VERSION); QSettings::endGroup(); QSettings::beginGroup("/Default"); QSettings::setValue("/Preset", sPreset); QSettings::setValue("/PresetDir", sPresetDir); QSettings::setValue("/KnobDialMode", iKnobDialMode); QSettings::setValue("/KnobEditMode", iKnobEditMode); QSettings::setValue("/RandomizePercent", fRandomizePercent); QSettings::setValue("/ControlsEnabled", bControlsEnabled); QSettings::setValue("/ProgramsEnabled", bProgramsEnabled); QSettings::endGroup(); QSettings::beginGroup("/Dialogs"); QSettings::setValue("/PresetsPreview", bPresetsPreview); QSettings::setValue("/ProgramsPreview", bProgramsPreview); QSettings::setValue("/UseNativeDialogs", bUseNativeDialogs); QSettings::endGroup(); QSettings::beginGroup("/Custom"); QSettings::setValue("/ColorTheme", sCustomColorTheme); QSettings::setValue("/StyleTheme", sCustomStyleTheme); QSettings::endGroup(); // Micro-tuning options. QSettings::beginGroup("/Tuning"); QSettings::setValue("/Enabled", bTuningEnabled); QSettings::setValue("/RefPitch", double(fTuningRefPitch)); QSettings::setValue("/RefNote", iTuningRefNote); QSettings::setValue("/ScaleDir", sTuningScaleDir); QSettings::setValue("/ScaleFile", sTuningScaleFile); QSettings::setValue("/KeyMapDir", sTuningKeyMapDir); QSettings::setValue("/KeyMapFile", sTuningKeyMapFile); QSettings::endGroup(); // Presets database. savePresets(); } // end of synthv1_config.cpp synthv1-1.5.1/src/PaxHeaders/images0000644000000000000000000000013015270627745014204 xustar0029 mtime=1793273829.97269805 30 atime=1793273829.971582017 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/0000755000175000001440000000000015270627745014253 5ustar00rncbcuserssynthv1-1.5.1/src/images/PaxHeaders/synthv1_control.png0000644000000000000000000000012715270627745020151 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/synthv1_control.png0000644000175000001440000001143015270627745020134 0ustar00rncbcusers‰PNG  IHDR00Wù‡ßIDAThÞíYkpœWy~Î9ßm¿ýö~‘´–¬‹mY’m]ml'!äB’&3$tJRB.Ò´@™)?;´ô'Pf:ÍL;�@™vZ&\†’8N0��p1Žì8–%Û±dY’uójµ«Ýo÷»�súC+%ÆI ?y5gt޾Õ~ïsÞ罜÷”?Ê$ìÏhý·’N§ÕjµJt݈麕RF‡Z>ý™'ï9~ü¸ŸÉfìJEùðíù÷åâ²Ï¥K ÑÐß?°û¶Û>Ô?2rVH$É´ãÔ ëºÅ9Wêï oïYèæ?†A�»îº›PUUUòù<}≿èܳ{·F(µä¦¦gZ×ìëï;xmq! V,‡¤à[ßkÖ4m[WwÏs Û´Y‘HçòrÁ¹ï¾û¸®kiš`ÐêÉê è{£lZÇq�ÉdØ�ü#ÒÖÞ®]™œT)eʶí;’z(üWs óÇWV"@$l D#‰]n¹Ã!Åh,b*FøO²RZò¢ÑHãÌ ÊĔJ¹4~ë¡¥½£ý ¥ÔfŒL3\õ<—Aà×u�ÄÛæ7”ÍHÉîÝ{”‘‘³ò¿ÿçÙžG>ñÐL,‹¸®›p§ôàŸ>ôÏËÅJ÷ÏŽù¯{îûø[¶¶™ ¹æŒ¢ê Lñì•’™ÎfbnöjQ×Të•c/^~õ•×w÷îmÉd³{^>zøK¡�Éjµê%ÆØ5Îù2€ÀÀëCÔÈ÷â$™L*ù|žÜzë-wÞywÿóÏ?gHSJ;·oÛ–ÐÍÐÀo ·ßÁæLC.E)5ì•‚Q¼6kZѰº8;Í\§ª¤ÒYËŠÅ´–öNk`ßÛ|§ÒNá/LŒO!¸”R )¥÷ÉGí\‹‹‹n}Cå&+�÷@Éç󬱱Éúþ÷¿_޷gÝvëÏþ WUµ}ë¶=ûo¹ýþfU¡ …@©®,±Ží;hww/íÚÕOs[ZiWÏÚÞ¾ƒ†Ã&Y^š'Dr�1jîèé§§Úè¹SßóeLÜu×GvdÒ'Nüz©V«qJ©”Rþþ ‡Ãº®ëz±XŒD"‘l©TŠ?ùäSŸ°kn·•lÉuìØ™�ð5î9èìÚCãñ©Ø¹)J)¸XµÚÖÚüàßûq­æ¼©ªjÙ÷ý9EQ*A”Ôê”’7¢Ò ø¾Ï¸�:!`ªª)At��_2Ýõà=¹æ­QáUµL:K»zú¨”‚Úv™(”Ê¢0…PʈÂ(aT!�’Ôª"¤$Ù† ›aR)/i‰xJïéÛ—.æçâ/¼ðÜ×õUUcœó•ýû4LMMUU•BïÝøÁO=õ×íó…/ì<~â7}™tÓíùü¼wÛ�½wÏྷVVRÉF¶¥y+-–Hp±ÂDH€òÖ;äši%(c „ V³‰i†I,–ÄÜÌ$⩌‰¦Éå7Ï«f8í:pO_ïîZ<‘ˆŽ�ŽÎèº.‚ pïD§ †O «ܛͦ‡TÝ|ÄõƒôC�=µ7p«J:™a{÷öÓJéš›²ÄiÈʇ p.@6�Q„¬¾QJPJe$ÝЈ¹«SDP&5]·B:ËÆ¢‘àµß¼:yáÂù×u«œóZ�B^ý Å»)“ÃïÑáS§�›oÚ·mèÀ�Žë¨†ª°¡¡ú¿KN¾v’œ>}ªÂйc;®V`h*„ܨ¿\ß4"$$$cÄ G`ÛÚØØ¬\º8êüê?�(•жëºee6€J¿�6gbÍõ‚�SÓ+Û´EµŒè§¿|åerùòb™Ä¢)œ8~—ÎcKc5Ç­G¹þ³ŽB H"Aˆ�ïs„­aDPÉ [†®ë f}èõ ­¼SfÞ``p°©¯··¥ZµÍ¡Ø ê:e‰ÆÈÊÊ21Ì0(c’#�a~~FHG êjK Ⱥúbm @’ÕAJ%8çØºu»T×÷ߦd2�ÑD"™I$ ­­­[:;;›èŒ)j�%ô]•÷Ýwßm M²¬ˆnFbŠç:t{ç.bWl$“)L^™�i„ ¬”W�kÊÁ¶]°Uª¯*\çýÿW1HH")ÉÁ(!AàËt¦AÛÓÛ›V 놖šžšVkµjÀyJ‰Æ9œY`ƒLOÏ4––˜aÆú::w¥Šù¶cç.Z©Ø¤¹¹ ÂwP,¡ë ÝÝ;ѳgó‹¨*«#À[Š×A­ª/6XEEU ˆÁÌ•Kräì™ÊÕÙ«…S§†Çfgg)¥%Îù ¿�‚w´ÀâÂüâå‰ñ—yü/ï6ùR.A× ‹KÐŒÜôaì,,À0BY1,Ê Œ®+·a÷몯ûž�‚CJ)%(SVìÄ´’ðTU…'E Ù¡�¢÷ŽO¢ 4„¢µµµZ íííp][·nE<OJé¯rƒü‰] �MGξ¡æç&OePõ9œj*SV�s­ÔÙêKB)˜¢@H EQpq|����pœÊ¥ex® dÔ®yl3c0" 5 ¯æcxxÙlÃÃÃH¥RE,ƒÂ'„¸õ°ºÑ‰_?sf’¿|úÌ-Éx”¾{`¿p977…-[;H¹T€ª¨ëÅÂf.H)ÞÊR­ÕÐÑѧVƒï4„íîB2•D<‘A¹pçNG¦eLˆG ¡0¨K±wïÆÆÎchhçÏŸGWW®\¹‚º_¬97Pèg?ýÉø‰'.×ìòåLCsâÄÏ�ÊH<%¯][@µ\‚®‡ð`=뮕›‡” ¥…Ã( ¨Õj ” ¨-cìÍ Ì­(˜wbxþ?þ¥¹ ˜›½†dÔDÔÔA$œ8ñ*âñ8FFF�FqáÂhš†ú‰m}l é:£SÉT¦%“ihÖ¨X)-- šÈÈññ7¥”Œ)�ë¡ô­h-A­òk5ÛAß÷¡( ÇAØŠ�OÿГHÄ£èºãó(Õ$v Ý S#ˆ˜:4Mc aÛ6Òé48çH§Ó× x®»øèc�÷=öè£ZOWÇðó‡Ÿ;ó�ýê¥Àóˆ Ë cg¥ÂUç„«IL¾U…®[€äñxét¹\ŽSCº© S'þ5O ÷ླྀ÷É÷*ÐQË%BpŒ��!�£¥¥ÉdKKK`ŒÝÀÇz(�Íf¼¯ýŸ~òôÓO«Úö¤2J¯ýêG"%ÌHçÇFà{.ÌPŒ1PJ!¥ç"àB¬êO€d2‰³gÏ"ŸÏÃq¨ªØW±4;ŽH$Ц\®ã‚‰ˆ¡‚) ’0ôtw£R©`zzccchoo‡çy7.%¾óì³3JŒ±J‰&¥¤“““\UU�ÛÞaê᨜žž‚ik߀@­›�s¹9‡ÂŠËËà|5ÄÆâ1±¨…7Ž>��›_„&˘¿| · =ˆˆÆÐÖÜ„WG<çMÄâ ‹EÄãqÌÏÏCQlnEn i×4-¨T*‹Œ1ÀyøáOôÖœ=wöÍÅÅ|{WïPxGçnE ™r|âR‰ ¨¢R ZµQ^)¡êy ”‚q޶ö6.Ö˦¨hJ|ï+%Ai¥„ÿýn‡5 ËùkHX*bVB$‰õ ð}„åmÅÙ@*Î9õ<‘H„ÔjµÚ§>ýéþææææï<ûìèââB-dš†ÀzågÿW6Tb¤29i˜HJษ9®¬T+d~vñ¨ EQàû>@+lAS5ø¾J)Ñ”Ž"ªÔ¯@Ø×�µ(tQwmX‘(,ËB¡P€ïûhjj"™LF) ÓAœãœ_ànö šJ¥H±Xôÿö‹_ìlÈ6æ¾öµ¯¾V©TJ)ÛÝ7t R^)CúrüâyWòš65>*–ç‰[µQ³W°03�soœ$ÑH;vìXÑÜÜ Ã0‰D0;;‹ûï¿!ÓDsK FFÎAÚ‹‰‰Ë[ CG[[®\¹EQpÓM7annó¾ï�J)§T77w…¢(€ŒFãôË_þûŸJ)SÄÐÞ½Íý½»Æ¾õ­o^+‹‘h4šÐÆFSÝÝ=éѳö¢*ÊäåI¿)—3 ¤öØc�‘ååeBpõêUÜ}÷Ý8}ú4<ÏÃüü<y䜾\,N~p-·%W)‹+mmm$‹¡\.£³³+++H¥RÈçóèè耢(k !,ËBcc#\×E2™D,ƒïûˆF£€\.‡ùùy´¶¶Êîîî-²tµÍÏ”ß.ˆBá°åÛv¥À4³Ù†Ò™3gf¥” “¢QJÃŒ±ÈÄø%@@¨©©i¨»»;ÓÚÚŠ—~ôB¡Âá0"‘*• &''ÑÚÚŠp8 J)LÓ„b=Æ···cäÜ9xž+bÁ²,„ÌJ¥R©z{{ÕcÇŽí ‚ @½ÉPÛ®¸Àã§O�š:}êÔ"€8€&¥T8ççÜ`Œ™õ¶ˆÕßßȲ,¾eË6S�á<ð ÚÛÛ‘Ï_ÃÌÌ4ü „ëy¤µµR ŒŽžÃg?ûYtuuá…ÇA(AG{:„_<‚��A´¶¶’\.'úúúú‡‡‡“õ’ú·jJZ¯¡z�&Îk0Ms+!¤@'€�õÑ`€AJé->ö¹Ï}Î9räH ërôèѵ©´m[–ËåõõK/½´>?räÈú¼R©Èr¹,…RJ)�;¶þìå—_ö~øá2€ÏèP®sPZkoûu0‚sΫժW_Óëô*©eYf<·4MÓ9çâðáÃð}ËËË8r䤔¨T* ”" AÓ4\¼xq½¾c ®ë¢V«B¡t]Çùóçá8„(—Ë4—ËÒŒëPhˆµ„á¾ÓEC*• È–J%0ÆH4…mÛ¨T*ëÉÈ÷}hš†H$Ó4áy‰!p]ét¶m¯!c±LÓDH&“B`yy™¬¬¬Ðµû5åîäï˜ÿ–hšÆmÛårápº®ƒsŽP(Ã0À9‡aÐu†aÀ0 ˜¦ Ã0@YŸsÎáºîÚ…ã†!„@xµD—klx'ïZ<σëºÂq<óÌ3`Œ­ŸÖ ¾ÍÂ9lj'AàôéÓ«ný³k–XûÜjÑÈQ.—åu�”ˆ”ËeY©Tj‹‹‹•R©dB®K¹·‘R®+ù»:„�õgkbÛv­~1È Þ?a’¾ÐØØøwBùû^^߈ÒB™Ïç_ð<€WØûøåk×4@·¦i9BÞçAÊåò(çü$€S¦Èû €BRRÊ�„� õ^ÿû²û«Í?¹`À9Wßo“º_…ëtŠÖûšä}á(X`ÿ?X�¤ÓË«›3IEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/synthv1_preset.png0000644000000000000000000000012715270627745017773 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/synthv1_preset.png0000644000175000001440000000346615270627745017770 0ustar00rncbcusers‰PNG  IHDR szzôýIDATXí—_hTéÆßwÎÉœ™I2ÎÄMœh"´(©P¨)ÅÁ½ôÆB¡´B!©¥ØÙ¶`‹xÑ;wo¶4Vè•%b/-+¸!ÌZa³ &+š?&13“™0Μ9ߟ^LfŒq¢±ô…—søÎá;Ï÷<Ïû½ßlˆóçî?>´³µµMcB°•pGŒŽŽ®Ü¸1ºÄ{„»q ZUƒÝÝ;otvvzJ)µEBá�ŒŒÌc>¹yó¯ŸÁÿ@k�ŒÇãéD"�RjË+B�J¥RÃÃÃ× …ü®;wþñG üÞm­µZk¡µ6[•Àc÷îÝë|üño-¥½}ûöï�Õ÷P§Q‹-°ÖŠ……çv÷îÝÎåË—áºnâÖ­[¿V¶ @ˆ�ës«Xk™™™¡P(ØÁÁAqñâÅŸT«ÕðÎ�;¿Ù „Û|2�òý>^Ï••Q*•,`�9".]ºô3Çqªccc¿*[”@[P‹Ål&“Åb‘t:mûúúÄ… ι±±±?úˆ ù>,]]]D"{ÿþ}‰³³sf` _ŽŒŒüîùó…§ããŸÿù ¼úpÝ[´ H§Ó Ñß?@,#™LÊdr==½âàÁ'ÇÇ?¿ TßiB� )6†”×m®^*ÕA2™Z{Ïi0#¥¤»»»mã„îftJ)�R6}¦”"—Ë¡T¸ C¯¤³ÖZ@8ŽC­@ßÀÀÀ×ããã•·H@#×|?Ê£G�ý J©€ºg¬µ!±*@«7á†á÷Î�;÷E>Ÿÿl||ü§Mc0ÆlZ ZkR)rÙ,“_~I4ÇX‹5¦á£5&¶·u;zeÏT¬Eàû¾4ư²²’¸råŠ�ÍÌdŒÁZóF5Æh’Û’lko‡0ă C”ÒmÀhV_V(¥’8z–ª›@…Æ‚  P(�Íf§®_¿.›0`ÑÚ µÆZp]!Àںƚ†k-Û¢D”ØŽ>¢¢Œ]xŒwHù†¼ç"¨I³ÎÀ^}Ïh@£T¸Ö kFt‰h�U»5‡ÕªÁJÂÊK¢]ß¡-æ²ZÈÑÙÕÃížyJÇÁu]¤”ucbŒA6i(h­QJ£µBkµ¶ÊWF“B€` œƒ±q¡ðÍ¿ˆ%{9pò<�Ýß"÷p…! «¸®‹R Çq^óÕ¦´Vk jrc>p‰µ‚ªÖT*Êå2‡—‹_QÉNïØI.7�ãz- hÁhE¹\A)…µV(¥šWA�!@)Ù(G!\<×#ªTµF¶´�ljD"€¥ÝM1ûÅßYøúŸayñŸ”Jø‘Œµ$í‹E|ßoH&“o“ æìºjŒ(\Ïe~~ŽJ¥Ì‡}}$ zzzèèØN2•âßÿ.=-žOÓÛáSÍϲ»w©Tù|žcÇŽÑÚÚº`bbÂ4e@)µ¶ÉÈF[®¥DJÉÓ§Oéêêbÿþý<~ü˜ÞÞ^æççæÞ½{,ú)?üÑ�)–>app�0 ¹{÷®={ö¬X^^þöÇS@N¾ À«Õë5í år™ééoØ·o‹‹‹är9:;;™››#‘H µÆó<Êå2Éd’B¡€ïûxžG¥R¡¿¿ÿC o³C)a®c@½ÆÀÜÜ/_–øè£“LOOãº.ƒƒƒLMMqàÀ\×åèÑ£?~œÞÞ^Μ9ÃÌÌ CCCA víÚe„ÉÇÌd2÷7• Z ×R};XkÈårxžG,ãØ±c¤Ói„œ>}šx<Îìì,{öìáôéÓ$“IÒé4ÝÝݤÓiNœ8awìØa*•ŠïspÑPÚ|ðº¶� Ôq×™…5à0\u} Ø>¥–ÿEs­¸áõzñXì±®ë×mÛVUUµ ÃØ_XXxišføµ|/npû¸€ px�Ç r ‹‹‹ß€'À} ì®qà.p èmÛ=Š›õÈØØX|gggÚï÷³¹¹I©TÂï÷3;;K­VciiɑТ(ÍÌ<O-�N¿K&“Ï€œ[ª&—€ñ•••²ˆÈöö¶LNNJ,“™™I$öq»h�Íf«À#à2-‡®¹N¯¦i>€ÝÝ]B¡¡PˆÁÁAR©”R,ñù|ˆ8ÉÚ¶M__‘H„®®. ·¿�zãðš‘TU¥\.“Éd888àðð��� LÓDD*• Á`�H$‚ˆ(À ø d€O á&õz�\.Çêê*ýýýí�;UDd˜0 ÇÃOÓéôsµýÅjµÊÜÜÜ©DÛéîîV-ËêÔá��¦¦¦Î, °¼¼ü3“ɤœÚ<\[[3EDŠÅ⿚àD …‘®ë/€›€§‘q³¹u]?W¶Ñh4[*•Þß[uEµ@ ÐQ–Ó’H$~­¯¯¿²€‰{½{€é½½½ÚyJ`Y– ¿nãLFÀ¹ u ???ŸŠF£÷EéhÁ“PUÕÞÚÚú�Ïçß_p¸ePp®ã0‚3�ÚçôI˜Àgà=ÎXµ[…ÖëîàÔó÷'Po½˜ü‰¿áIEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/presetEdit.png0000644000000000000000000000012715270627745017105 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetEdit.png0000644000175000001440000000054115270627745017071 0ustar00rncbcusers‰PNG  IHDRóÿa(IDAT8Ëc`R %%åRZÚd1&â´²‹ÙÙ1ü6´þ?W@à¿‹‡Çb `d`àdOLüyçàÁ“ gµ3†oÜÊÀÎÊJ¼³µ´þÿÿ¿òÿìÙÌÿ'L`øïééÿŸ��!ŒgË2üùÿ¿ûß�¶ÿ++þkhØþRSS‰b ìlˆæËÿ—/ýŸœÌð/,,â¿€A{ÿÿÿÏ¡©ÉðÿÿÿîË—‹þŸ1ƒñ¿©©+Ng³ ñÙ˜ÙEOÿý¹€áàÁ¹Œ{ö¼þé’+ãÏŸ/bV4 ("ή9\K'3÷8ƒßa†ÏŸ#OŸ^ŽK3F4^ºpVôÚ³Ï òÿnÜϼ°cǶP|š1€»»û3QqÉY!‰N l #®Qp»yP�QIEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/synthv1.svg0000644000000000000000000000012715270627745016424 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/synthv1.svg0000644000175000001440000050104115270627745016411 0ustar00rncbcusers synthv1-1.5.1/src/images/PaxHeaders/presetExport.png0000644000000000000000000000012715270627745017501 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetExport.png0000644000175000001440000000145615270627745017473 0ustar00rncbcusers‰PNG  IHDRóÿasRGBÙÉ,gAMA±� üa cHRMz&€„ú€èu0ê`:˜pœºQ<¬IDAT8Ë�“[H”Q…¿ÿ¢ã%45KRÈ A’Y¤Ý£Ò¥,Ê(2Åò)ˆˆ^Ä$BÈ,²´$*•ˆ($ Ín”eä-5 Å™Ñq&gþùO3šv£ýrÎÙœ³öÚ묭ðè«”ÎjBÆEq!þ|S’$ XH€ ¿sãÂØ»ç÷ELdDÛÎÛ»�€¿Uò[¿66?=eã°õt^FcïÇ:1=Ò,Tãð�(..‚樞U¶],È-Ë:ºÍÃG�ÚÉœŒ¦Ô=› Ý]½tõØHIOCVƒ€ÅÔU@Ù—šXzîlî©ä¤uŠ,ËdgP-–)õEãcâ°ãÏÈ`aQ ,¿øœÈLÏÙ¼!^A–Ñ]Þ¶ákédwB$ÂËJëËjÚý(82†nŸ�~�$—ŽYŒ££t·<#)&ÿUá`s"T3.›…š½gp=©'bÔèi ¹\|íû‚ýS;»¢#0NÚiï™@Wº,øÜk‡doj#î–Hä·4;î³nMcÚ4EÌŠ`„´vš8œ\!Þà l’ Ä€Þì,Ùçu×¾3ÐVïa �Ó�u]@H„z»É*W耿 Ç2CÊUjKfåyÍùO+J 7îƒ/Vk”ÛšœNPü±Å ªÁÝ‚b©¿;oÓàNµ�ÊÂr&ûª¶ 9 ‰Õ+ äßË£­ß¬7Œ9Ç X&s»*ƒTÞµSQTŽ©¿0Ês"º5¡c³šÄëwÍï^==D_ót­“Ê«lT]ÇÔt&³XèËè¸�ê–Ö‰üš7ÅÀMY–ͺ®+ ÍPV6CSU9æþ+À°GRðhœ™ü&Ü‹z Þ“c~Nbw!8î<î™›ùøµÔšòiIEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/presetBankOpen.png0000644000000000000000000000012715270627745017715 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetBankOpen.png0000644000175000001440000000156215270627745017705 0ustar00rncbcusers‰PNG  IHDRóÿa9IDAT8Ë…’h”uÇ_ß{ž»çîv·›ûátSk‰#ZÍÚˆÚè R !pØsX¬‘•’âÊ’ Êf«Õ°I²Ž˜³D–m퇮»Úìnλæµ_m·»gwÏ=÷í�L†½þ{óáoÞ|àt]·/ÌJIII]MMÍñ¦¦¦ /¼Ûî�W�êºîÈËË«ž��=ÔøJÃͧNÔ/YœïÖ4m}mm­÷GÜÕl6[�FWF"‘a!DÜív?¶qÃ÷¾þêË;ï*-È4YáÁå[º«ººzÀ²¬ ���)¡ëº7 m0’ÉÞ?vt»ÝaWN}ü‰|©þYÏ#–)6R -*Y5Õ³l÷’%×£eŸ3�™N§ôz=‚ ˆBÌÁõÒR©étšx­¢‚p­¦IEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/presetNew.png0000644000000000000000000000012715270627745016751 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetNew.png0000644000175000001440000000055615270627745016743 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<ëIDAT8�Í“1N„@…?H$šhmŒÁÌ(¨¼¥½'à´TT$„dïÁ4lCA,lt&Œ…ì°0Å6¾ò½™/ïÿ'Ê;ã]×–÷ |ƈÒ4}M’äyžç@km”RǪª^€7`ÞkôÐ4�6 õ}oŠ¢0u]�Ë Âß÷OZEQÄ0 dYv_–å¸Ý¬Ç1yžÓ¶-ã8>7Ëüèƒ÷ú•)ìR©Ôí8Î}ÿ0ÆuÐð}g.—Û–Ïçgt]ßÜÞÞ¾{ïÞ=‡ö?¿/Òqg«váÂWgþäÑG2/Ž��\×ý-›ÍÖµžžgrrò± hlûäÄñ~SúñŸ†ûö<ݹµSŸšúƒÓg†› ýÐ.2éqÖÝÿz†‡4¥Ô[oxí¹—úŸl3›E�0 æù”çùah„ŸÎ�’¼{=}é ¬•ÓXm]D¶<û*ð&€¡”ª¼òBß–† Ñ©,×8?œãÔ�£+ÎÓ�w³iÍ»ˆÔ¤VI74M ÃÚUª W¸2s�oÏŽ‘_ x8ÝÍWÐj£P­ ¤@JÃR« B¯Â×Ù!pºØ˜ÚÁ3] LÁr)ÀŒ( Ã@Ó4·)ðë+äÿ*°ëÞ �ú0v}�†H)B ÉHç¿ußãZ¥Fà-�("l!¦i"¤ul£MÓô0 oBêõ:ÕjUU�)p?�è é†!ãã㬭­133ÃÒÒÒ€®xüóCQí=ÞzëOKSÄŸ¨�=ó#ÖûA�N§Y[[#“Éà8«««Çˆ(Šp]÷/£££?>¶ã¿X :"ƒ [;5Ç&›NÑéyÔju¼Ö�ç­â?ùä“ßzž÷»!„¸|ùò–e„ø¾O¯×#‹!¥$ CÚí6ªªb‡¤£(¢Ûíâ>–i¡( ½^�^¯‡eY¨ªŠïû´ÛmLÓœÕjµîÎÏÏOEQ¶@¯T*™ýý},ËbwwÇqX\\$™LÒh4H$T«U,Ë"•Jaš&™L†�� Â~H½^'›Í²²²B§ÓaccÛ¶Y^^fgg‡••Ç¡ßïS¯×Gãþ;04::ª=óÌ3\»v�B¡@*•bhhˆk×®Q,±m!¯¾ú*¯¼ò /¿ü2sss†�mÛ”J%æææH¥Rض�mÛÌÍÍQ,Éf³¤Ói^{í5fggÉf³�( ?7MS½zõ*a211Aœ9s)%ívÛ¶-‚€D"�¦i†A.—#4MÃ4MlÛ&Š"EáìÙ³èºN&“Áó<¦§§Éçó1Ã0žp]÷–e166Æûï¿ÏÄÄ­V‹çž{nðŽçr94M#‹–e!¥(RJR©¹\˲B033ÀÈÈóóóLNNbÛ6®ëþ@BèÃÃç³Ù,¦iòÑGÑé´Ùøbƒ‹/¢iº®ÓëuQU•X,†ïûLÓDUUšÍ&RJlÛ&‘°H§Ó!xþùç¿* ËÛï¼�”’R©ÄÈÈÈ9!D\êºþ½b±˜¸pᚦqýúu¤”4›M2™ š¦qëÖ-t]$õ}Ÿd2‰”’�?þ˜ÅÅE¢(´äÆ�?†!«««LMM±´´D¥R@Ó4¦¦¦øôÓOsÀˆ,•J¿J$bvv)%wïÞåæÍ›\¹r…D"�ªª¼ñÆœ?UU‘Rrpp0ˆ}öÙgܹs‡ññq¤”¼þúë,,,`cccH)y÷Ýwét:ƒ=ÓÓÓ¼÷Þ{ªã8/)Åbq6™Lª³,kðÆãq4M#�ã8ÎàDQ4x#‰®ë‹Åí*•J´Z-,ËBÓ4NŸ>M£Ñ “É ¥$—ËQ(xòÉ')®^½Z9uêTaxxxðR•ËeÆÇÇþúú:§N�z ¿»»{lÚy¦ÑhðÁüKzž§X–Å¥K—ÁC™m~~žóçÏ?Ð/—ËèºÎÐÐÐ#cÞ|óM„ìt:‘‚dòë‰Ü0Œ}Ó4�ù±Xì�Ñï÷ñ-|Ë$ÁûµZí–eÝN§Ó¾Õ‰ßÐêõú_=Ï{ép8mÛö])¥öÿHîy^wooï‡Q- &"!Ä%`ødèwfUàVEÑ¿>¸=E:d5òIEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/itemReset.png0000644000000000000000000000013015270627745016730 xustar0029 mtime=1793273829.97269805 30 atime=1793273829.971582017 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/itemReset.png0000644000175000001440000000110315270627745016715 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsbb8z™ÛtEXtSoftwarewww.inkscape.org›î<ÀIDAT8�ÕR¿«Ú`½_¢Ÿ_Ÿ]¤ ÁN‚ƒH�š¡ØMèäP ]Ú¥€C÷þŽ‚¥S'×(t©S7¡%O24b‚H©ÁiÓ/5�¡ÞË{øúclÏt‡{î�sÀ? D$ˆ(üM/¹‚L§ÓéÍår)˲|¦iÚ×ß .‰çy'ÃáðŽa¯E1'“ÉmÇq("Šˆ( âÑÂÔ¡ð}ÿšeY÷‹ÅËf³)Çq ¶mßE1;›Í’l6ær¹�–e}Öu=¹ô" £Ñ¨²Z­ÞU«ÕBl·[ ”c ’$Û¶¿ƒ×õzýi­Vs !ûKìv;àœçcEpÎ�R ”RÐ4M”$é~¯×;á9„?5 „ì‹ÅâŒsþ¸Ûí~Z¯×†!´Ûígý~ÿQ«Õ2Ã0J)AðÄó¼G"æóùH×õ7…Báa§Óy?ŸÏ÷ªª¾m4ƒJ¥ò‚1™LR©T.v$"!`ë8Ž©(Ê×uår¹|V*•¾�Çc×÷}Œ¢ˆÄqü%�Nï~™ƒƒ¥ç3Ï…rçºiš·6›MZ’¤¥ªª.:ñG’i†xUþsüàÖõª³ »IEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/ledOff.png0000644000000000000000000000012715270627745016174 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/ledOff.png0000644000175000001440000000123115270627745016155 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÔ,‹îQ &IDATxÚÍ’?oÓPÅÏ{ví¸®Ò¦ÂBRŠT))´‚¡ õÀ êP± &>#_„ï€�� ˆ¡Ll”©Ciì�&nÓÒ:Nâçç÷‡…J-3œéÞ«{~:Ò½À¿ùs°¶¶6ãûþ�Œç�x0¬ !¤m™»úãööö×���Ñù}ã|Óh4ªφcþ†ó è�.Dýø†Y'P÷n.×\­u³×ë%ß÷ç�‡ÏS.Ÿ:î\e/ìz‡'î~t4ý½¹gµÃVq¶8½°´´4Í9ÿÒï÷3 ç¢û݃þc óò핺}­|•:¶ Ã4@©AÃqáÓÖÖõ0 Ÿ,..Þ=ó™gEÆóG‚ÞA3´XÆÑþÑãR(%­0�ìããã+óóó›>\ð\UwÛ�”ó|f¿�q…ÓÁ5IDATxÚÍ’MkQ†ß{g2“ÉÔ6• jlÚJS‹±B#¢ƒt£D»(nŠ+�Kwþ þA]¹P]Ù¸ê¢Ø&“’&£I?&“Ì÷½×…´Tƒk=«sç}xçÿ:ÈŸ�J¥2fšæÕP‰« K˜êË[d(ÞÕ6j_777‡§ç¥ÓEµZ-¬®®>”¢'XÑîXËGÓögVÉM¾²8VÒ…;�Nǘ¦™­Þ»ûØ_bµ[Ù©z®•îœ9Ô÷ô^fÛ¨+»ó�ñ‰ýÌô™ËDQ´ÑívC 'Ö¯WÌöLw�-Ëç–² êEõÕ$‘@‰ös^úÃúû™f±ù 8[¼q¬;„j|ß[䯶ÒT>%5ìò–€!'  óCuÿÚþù ¬ëäã$JóÂZ~Äâ±=ØÇóÑŒ†4TÐv¾í§Õôô( …Ëûè%.¤XFLb„Ô‡/;HR.„8çH’DŒ¬@ú¼a¸ºê 'ŽR]¸©¼ÔbÅW†€Ä�|²>™¦}Zq@]¼Öjâ6f)O�4.E²ÿK õ@ µ�šMôÍÈ)¡½Ë™K9å…öüŽk® 5" ˆÁ•®”ž–ZFÍxÙl6_8Žþ°m; „|[¥ ÕRóã|\žpôŽîMmLñüóüwã‹ñªÑh<³,ëÇ__¹\.OŠÓWüµ@ ò,aœ8¤Eä­Ý¶?[–uˆÿ*~^¿í¼ƒ‘IEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/presetOpen.png0000644000000000000000000000012715270627745017121 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetOpen.png0000644000175000001440000000105215270627745017103 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<§IDAT8�¥“?‹AÆûGW—#É ‰Mšà!±8V–%ì‚} I!)mü *h°Éw°ðˆ,‚bac¥"¤“ ×¥HHÎsn&�wìy/!¼Í3ÏóÌÌûÎÀBû]P«öõÇ€ ¯Ï€—@¸~_�ë0ã ð0¢�M 6.œCàÎFÎÉj~g™4SC¬aãK8BT�i‚Ù×O±Ä�æÌÙÔ�|oÖ�mÓT¶ÂWá×qÚ�xEˆðvèˆý ÆaY�î�êÌõš†Z·§/ÅÖ%¶[aà57€2 X@@”¯Û¯ÆÛbùî�PYùÐwòvýàŠ¢G5©úŸ‡0Å"Š€ðc0°+sÚÄú8d%0ø³ |å ì”Fåø E¸&—Û®,IEND®B`‚synthv1-1.5.1/src/images/PaxHeaders/presetDelete.png0000644000000000000000000000012715270627745017422 xustar0029 mtime=1793273829.97269805 29 atime=1793273829.97269805 29 ctime=1793273829.97269805 synthv1-1.5.1/src/images/presetDelete.png0000644000175000001440000000110115270627745017377 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<¾IDAT8�Å�MkQ…Ÿ™LrcL˜246%t’’J‰‚I]”TJ)])]¤ ‚bE\ù ÝéBû\¹Å š�EÝ‚ÁJJ Iœ|½nÓdÝwq_8Ï98m… ˆI ôýÛàÐõoC·,ëÊ�ååÇ_Úí¿®ëÖsÚ¶7·î<:ª{ž÷Ý�PÔ²¬ÊËùy¯©”ìOMõ³¶ý°è8{?R©Áa4*{ssžiš¿å ¥î®­Uº.@~†Ãò6—ü²,i…Bòäµ®ËõÅÅ*�ˆ†Q¾Z.¼“oJIM)©kš|2 y‹Éz±øÙ0ŒË@dÔ¨àÂD2ùà`rRê 5�ãã’I§ŸNЬ] ™Ïf3v½NS)\¥HµZD•¿ƒàâîññF£×ãI>z™é[�7‰MàüÏi€}meåÝ!ÈíR©Tâñøö3Çé|Ðuqfgïæ(€\ÚÏåº;¥RØÎf"‘¸ñ¨Ppo--U�´6´Af{uõM,[÷Íšÿâ@˜£À„¿E0E•|ºú¼ì‚oؤÎEIEND®B`‚synthv1-1.5.1/src/PaxHeaders/synthv1_controls.cpp0000644000000000000000000000013215270627745017061 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_controls.cpp0000644000175000001440000004256615270627745017066 0ustar00rncbcusers// synthv1_controls.cpp // /**************************************************************************** Copyright (C) 2012-2024, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_controls.h" #include #define RPN_MSB 0x65 #define RPN_LSB 0x64 #define NRPN_MSB 0x63 #define NRPN_LSB 0x62 #define DATA_MSB 0x06 #define DATA_LSB 0x26 #define CC14_MSB_MIN 0x00 #define CC14_MSB_MAX 0x20 #define CC14_LSB_MIN CC14_MSB_MAX #define CC14_LSB_MAX (CC14_MSB_MAX << 1) //--------------------------------------------------------------------- // xrpn_data14 - decl. // class xrpn_data14 { public: xrpn_data14() : m_msb(0), m_lsb(0) {} xrpn_data14 ( const xrpn_data14& data ) : m_msb(data.m_msb), m_lsb(data.m_lsb) {} void clear() { m_msb = m_lsb = 0; } bool is_msb () const { return (m_msb & 0x80); } void set_msb ( unsigned char msb ) { m_msb = (msb & 0x7f) | 0x80; } unsigned msb() const { return (m_msb & 0x7f); } bool is_lsb () const { return (m_lsb & 0x80); } void set_lsb ( unsigned char lsb ) { m_lsb = (lsb & 0x7f) | 0x80; } unsigned lsb() const { return (m_lsb & 0x7f); } unsigned short data() const { unsigned short val = 0; if (is_lsb()) { val += (m_lsb & 0x7f); if (is_msb()) val += (m_msb & 0x7f) << 7; } else if (is_msb()) val += (m_msb & 0x7f); return val; } bool is_any() const { return is_msb() || is_lsb(); } bool is_7bit() const { return is_any() && !is_14bit(); } bool is_14bit() const { return is_msb() && is_lsb(); } private: unsigned char m_msb; unsigned char m_lsb; }; //--------------------------------------------------------------------- // xrpn_item - decl. // class xrpn_item { public: xrpn_item() : m_status(0) {} xrpn_item ( const xrpn_item& item ) : m_status(item.m_status), m_param(item.m_param), m_value(item.m_value) {} void clear() { m_status = 0; m_param.clear(); m_value.clear(); } bool is_status () const { return (m_status & 0x800) && (m_status & 0x700); } void set_status(unsigned short status) { m_status = (status & 0x7ff) | 0x800; } unsigned short status() const { return (m_status & 0x7ff); } synthv1_controls::Type type() const { return synthv1_controls::Type(status() & 0x700); } unsigned short channel() const { return (status() & 0x1f); } bool is_param_msb() const { return m_param.is_msb(); } bool is_param_lsb() const { return m_param.is_lsb(); } void set_param_msb(unsigned char msb) { m_param.set_msb(msb); } void set_param_lsb(unsigned char lsb) { m_param.set_lsb(lsb); } unsigned char param_msb() const { return m_param.msb(); } unsigned char param_lsb() const { return m_param.lsb(); } unsigned short param() const { return m_param.data(); } bool is_value_msb() const { return m_value.is_msb(); } bool is_value_lsb() const { return m_value.is_lsb(); } void set_value_msb(unsigned char msb) { m_value.set_msb(msb); } void set_value_lsb(unsigned char lsb) { m_value.set_lsb(lsb); } unsigned char value_msb() const { return m_value.msb(); } unsigned char value_lsb() const { return m_value.lsb(); } unsigned short value() const { return m_value.data(); } bool is_any() const { return m_param.is_any() || m_value.is_any(); } bool is_ready() const { return m_param.is_any() && m_value.is_any(); } bool is_7bit() const { return m_param.is_any() && m_value.is_7bit(); } bool is_14bit() const { return m_param.is_any() && m_value.is_14bit(); } void clear_value() { m_status &= 0x7ff; m_value.clear(); } private: unsigned short m_status; xrpn_data14 m_param; xrpn_data14 m_value; }; typedef QHash xrpn_cache; //--------------------------------------------------------------------- // xrpn_queue - decl. // class xrpn_queue { public: xrpn_queue ( unsigned int size = 0 ) : m_size(0), m_mask(0), m_read(0), m_write(0), m_events(0) { resize(size); } ~xrpn_queue () { if (m_events) delete [] m_events; } void resize ( unsigned int size ) { unsigned int new_size = 4; // must be a power-of-2... while (new_size < size) new_size <<= 1; if (new_size > m_size) { const unsigned int old_size = m_size; synthv1_controls::Event *new_events = new synthv1_controls::Event [new_size]; synthv1_controls::Event *old_events = m_events; if (old_events) { if (m_write > m_read) { ::memcpy( (void *) (new_events + m_read), (void *) (old_events + m_read), (m_write - m_read) * sizeof(synthv1_controls::Event)); } else if (m_write < m_read) { ::memcpy( (void *) (new_events + m_read), (void *) (old_events + m_read), (old_size - m_read) * sizeof(synthv1_controls::Event)); if (m_write > 0) { ::memcpy( (void *) (new_events + old_size), (void *) (old_events), m_write * sizeof(synthv1_controls::Event)); } m_write += old_size; } } m_size = new_size; m_mask = new_size - 1; m_events = new_events; if (old_events) delete [] old_events; } } void clear() { m_read = m_write = 0; } bool push ( unsigned short status, unsigned short param, unsigned short value ) { synthv1_controls::Event event; event.key.status = status; event.key.param = param; event.value = value; return push(event); } bool push ( const synthv1_controls::Event& event ) { if (count() >= m_mask) resize(m_size + 4); const unsigned int w = (m_write + 1) & m_mask; if (w == m_read) return false; m_events[m_write] = event; m_write = w; return true; } bool pop ( synthv1_controls::Event& event ) { const unsigned int r = m_read; if (r == m_write) return false; event = m_events[r]; m_read = (r + 1) & m_mask; return true; } bool is_pending () const { return (m_read != m_write); } unsigned int count() const { if (m_write < m_read) return (m_write + m_size - m_read) & m_mask; else return (m_write - m_read); } private: unsigned int m_size; unsigned int m_mask; unsigned int m_read; unsigned int m_write; synthv1_controls::Event *m_events; }; //--------------------------------------------------------------------- // synthv1_controls:Impl - decl. // class synthv1_controls::Impl { public: Impl() : m_count(0) {} bool is_pending () const { return m_queue.is_pending(); } bool dequeue ( synthv1_controls::Event& event ) { return m_queue.pop(event); } void flush() { if (m_count > 0) { xrpn_cache::Iterator iter = m_cache.begin(); const xrpn_cache::Iterator& iter_end = m_cache.end(); for ( ; iter != iter_end; ++iter) enqueue(iter.value()); m_cache.clear(); // m_count = 0; } } bool process ( const synthv1_controls::Event& event ) { const unsigned short channel = event.key.channel(); if (event.key.param == RPN_MSB) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::RPN) enqueue(item); if (item.is_status() // RPN nullptr [MSB] && item.type() == synthv1_controls::RPN && item.is_param_lsb() && item.param_lsb() == 0x7f && event.value == 0x7f) { item.clear(); --m_count; return true; } if (item.type() == synthv1_controls::NRPN) { item.clear(); item.set_status(synthv1_controls::RPN | channel); } else if (!item.is_status()) { item.set_status(synthv1_controls::RPN | channel); ++m_count; } item.set_param_msb(event.value); return true; } else if (event.key.param == RPN_LSB) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::RPN) enqueue(item); if (item.is_status() // RPN nullptr [LSB] && item.type() == synthv1_controls::RPN && item.is_param_msb() && item.param_msb() == 0x7f && event.value == 0x7f) { item.clear(); --m_count; return true; } if (item.type() == synthv1_controls::NRPN) { item.clear(); item.set_status(synthv1_controls::RPN | channel); } else if (!item.is_status()) { item.set_status(synthv1_controls::RPN | channel); ++m_count; } item.set_param_lsb(event.value); return true; } else if (event.key.param == NRPN_MSB) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::NRPN) enqueue(item); if (item.type() == synthv1_controls::RPN) { item.clear(); item.set_status(synthv1_controls::NRPN | channel); } else if (!item.is_status()) { item.set_status(synthv1_controls::NRPN | channel); ++m_count; } item.set_param_msb(event.value); return true; } else if (event.key.param == NRPN_LSB) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::NRPN) enqueue(item); if (item.type() == synthv1_controls::RPN) { item.clear(); item.set_status(synthv1_controls::NRPN | channel); } else if (!item.is_status()) { item.set_status(synthv1_controls::NRPN | channel); ++m_count; } item.set_param_lsb(event.value); return true; } else if (event.key.param == DATA_MSB) { xrpn_item& item = get_item(channel); if (item.type() != synthv1_controls::RPN && item.type() != synthv1_controls::NRPN) { enqueue(item); return false; } if (!item.is_status()) item.set_status(item.type() | channel); item.set_value_msb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.key.param == DATA_LSB) { xrpn_item& item = get_item(channel); if (item.type() != synthv1_controls::RPN && item.type() != synthv1_controls::NRPN) { enqueue(item); return false; } if (!item.is_status()) item.set_status(item.type() | channel); item.set_value_lsb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.key.param > CC14_MSB_MIN && event.key.param < CC14_MSB_MAX) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::CC14) { enqueue(item); item.clear(); --m_count; } else if ((item.is_param_msb() && item.is_value_msb()) || (item.type() == synthv1_controls::CC14 && item.is_param_lsb() && item.param_lsb() != event.key.param + CC14_LSB_MIN)) enqueue(item); if (!item.is_status()) { item.set_status(synthv1_controls::CC14 | channel); ++m_count; } item.set_param_lsb(event.key.param + CC14_LSB_MIN); item.set_param_msb(event.key.param); item.set_value_msb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.key.param > CC14_LSB_MIN && event.key.param < CC14_LSB_MAX) { xrpn_item& item = get_item(channel); if (item.is_any() && item.type() != synthv1_controls::CC14) { enqueue(item); item.clear(); --m_count; } else if ((item.is_param_lsb() && item.is_value_lsb()) || (item.type() == synthv1_controls::CC14 && item.is_param_msb() && item.param_msb() != event.key.param - CC14_LSB_MIN)) enqueue(item); if (!item.is_status()) { item.set_status(synthv1_controls::CC14 | channel); ++m_count; } item.set_param_msb(event.key.param - CC14_LSB_MIN); item.set_param_lsb(event.key.param); item.set_value_lsb(event.value); if (item.is_14bit()) enqueue(item); return true; } return false; } protected: xrpn_item& get_item ( unsigned short channel ) { return m_cache[channel]; } void enqueue ( xrpn_item& item ) { if (!item.is_status()) return; if (item.type() == synthv1_controls::CC14) { if (item.is_14bit()) { m_queue.push(item.status(), item.param_msb(), item.value()); const unsigned char value_msb = item.value_msb(); item.clear_value(); item.set_value_msb(value_msb); // --m_count; } else { const unsigned short status = synthv1_controls::CC | item.channel(); if (item.is_param_msb() && item.is_value_msb()) m_queue.push(status, item.param_msb(), item.value_msb()); if (item.is_param_lsb() && item.is_value_lsb()) m_queue.push(status, item.param_lsb(), item.value_lsb()); item.clear(); --m_count; } } else if (item.is_ready()) { m_queue.push(item.status(), item.param(), item.value()); item.clear_value(); // --m_count; } else { const unsigned short status = synthv1_controls::CC | item.channel(); if (item.type() == synthv1_controls::RPN) { if (item.is_param_msb()) m_queue.push(status, RPN_MSB, item.param_msb()); if (item.is_param_lsb()) m_queue.push(status, RPN_LSB, item.param_lsb()); } else if (item.type() == synthv1_controls::NRPN) { if (item.is_param_msb()) m_queue.push(status, NRPN_MSB, item.param_msb()); if (item.is_param_lsb()) m_queue.push(status, NRPN_LSB, item.param_lsb()); } if (item.is_value_msb()) m_queue.push(status, DATA_MSB, item.value_msb()); if (item.is_value_lsb()) m_queue.push(status, DATA_LSB, item.value_lsb()); item.clear(); --m_count; } } private: unsigned int m_count; xrpn_cache m_cache; xrpn_queue m_queue; }; //--------------------------------------------------------------------- // synthv1_controls - impl. // synthv1_controls::synthv1_controls ( synthv1 *pSynth ) : m_pImpl(new synthv1_controls::Impl()), m_enabled(false), m_sched_in(pSynth), m_sched_out(pSynth), m_timeout(0), m_timein(0) { } synthv1_controls::~synthv1_controls (void) { delete m_pImpl; } // controller queue methods. void synthv1_controls::process_enqueue ( unsigned short channel, unsigned short param, unsigned short value ) { if (!enabled()) return; Event event; event.key.status = CC | (channel & 0x1f); event.key.param = param; event.value = value; if (!m_pImpl->process(event)) process_event(event); if (m_timeout < 1) // make timeout ~200ms... m_timeout = (unsigned int) (0.2f * m_sched_in.instance()->sampleRate()); } void synthv1_controls::process_dequeue (void) { if (!enabled()) return; while (m_pImpl->is_pending()) { Event event; if (m_pImpl->dequeue(event)) process_event(event); } } // controller action. void synthv1_controls::process_event ( const Event& event ) { Key key(event.key); m_sched_in.schedule_key(key); const Map::Iterator& iter_end = m_map.end(); Map::Iterator iter = m_map.find(key); if (iter == iter_end && key.channel() > 0) { key.status = key.type(); // channel=0 (Auto) iter = m_map.find(key); } if (iter == iter_end) return; // reference to payload... Data& data = iter.value(); // process controller event... float fScale = float(event.value) / 127.0f; if (key.type() != CC) fScale /= 127.0f; if (fScale > 1.0f) fScale = 1.0f; else if (fScale < 0.0f) fScale = 0.0f; if (data.flags & Invert) fScale = 1.0f - fScale; if (data.flags & Logarithmic) fScale *= (fScale * fScale); const synthv1::ParamIndex index = synthv1::ParamIndex(data.index); // catch-up testing begin... bool bSync = (data.flags & Hook) || !synthv1_param::paramFloat(index); if (!bSync) bSync = data.sync; if (!bSync) { const float v0 = data.val; const float v1 = synthv1_param::paramScale(index, m_sched_in.instance()->paramValue(index)); const float d1 = qAbs(v1 - fScale); const float d2 = qAbs(v1 - v0) * d1; bSync = (d2 < 0.001f); if (bSync) { data.val = fScale; data.sync = true; } } if (bSync) { m_sched_out.schedule_event(index, synthv1_param::paramValue(index, fScale)); } } // process timer counter. void synthv1_controls::process ( unsigned int nframes ) { if (!enabled() || m_timeout < 1) return; m_timein += nframes; if (m_timein > m_timeout) { m_timein = 0; m_pImpl->flush(); process_dequeue(); } } // reset all controllers. void synthv1_controls::reset (void) { if (!enabled()) return; const Map::Iterator& iter_end = m_map.end(); Map::Iterator iter = m_map.begin(); for ( ; iter != iter_end; ++iter) { Data& data = iter.value(); if (data.flags & Hook) continue; const synthv1::ParamIndex index = synthv1::ParamIndex(data.index); data.val = synthv1_param::paramScale(index, m_sched_in.instance()->paramValue(index)); data.sync = false; } } // text utilities. synthv1_controls::Type synthv1_controls::typeFromText ( const QString& sText ) { if (sText == "CC") return CC; else if (sText == "RPN") return RPN; else if (sText == "NRPN") return NRPN; else if (sText == "CC14") return CC14; else return None; } QString synthv1_controls::textFromType ( Type ctype ) { QString sText; switch (ctype) { case CC: sText = "CC"; break; case RPN: sText = "RPN"; break; case NRPN: sText = "NRPN"; break; case CC14: sText = "CC14"; break; default: break; } return sText; } // current/last controller accessor. const synthv1_controls::Key& synthv1_controls::current_key (void) const { return m_sched_in.current_key(); } // end of synthv1_controls.cpp synthv1-1.5.1/src/PaxHeaders/synthv1widget_control.ui0000644000000000000000000000013215270627745017735 xustar0030 mtime=1793273829.974582027 30 atime=1793273829.974582027 30 ctime=1793273829.974582027 synthv1-1.5.1/src/synthv1widget_control.ui0000644000175000001440000001206415270627745017730 0ustar00rncbcusers rncbc aka Rui Nuno Capela synthv1 - an old-school polyphonic synthesizer Copyright (C) 2012-2020, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. synthv1widget_control 0 0 320 120 MIDI Controller :/images/synthv1_control.png &Type: ControlTypeComboBox MIDI event type &Channel: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ControlChannelSpinBox MIDI channel Auto false 0 16 &Parameter: ControlParamComboBox 220 0 MIDI parameter &Logarithmic &Invert &Hook Qt::Vertical 20 8 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset ControlTypeComboBox ControlChannelSpinBox ControlParamComboBox ControlLogarithmicCheckBox ControlInvertCheckBox ControlHookCheckBox DialogButtonBox synthv1-1.5.1/src/PaxHeaders/synthv1_tuning.cpp0000644000000000000000000000013215270627745016522 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_tuning.cpp0000644000175000001440000002011615270627745016512 0ustar00rncbcusers// synthv1_tuning.cpp // /**************************************************************************** Copyright (C) 2012-2021, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ //------------------------------------------------------------------------- // TuningMap // // -- borrowed, stirred and refactored from amsynth -- // https://github.com/amsynth/amsynth // Copyright (C) 2001-2012 Nick Dowell // /* * A TuningMap consists of two parts. * * The "key map" maps from MIDI note numbers to logical note numbers * for the scale. This is often the identity mapping, but if your * scale has, for example, 11 notes in it, you'll want to skip one * per octave so the scale lines up with the pattern of keys on a * standard keyboard. * * The "scale" maps from those logical note numbers to actual pitches. * In terms of member variables, "scale" and "scaleDesc" belong to the * scale, and everything else belongs to the mapping. * * For more information, refer to http://www.huygens-fokker.org/scala/ */ #include "synthv1_tuning.h" #include #include #include // Default ctor. synthv1_tuning::synthv1_tuning ( float refPitch, int refNote ) { reset(refPitch, refNote); } // Default is 12-tone equal temperament, wstern standard mapping. void synthv1_tuning::reset ( float refPitch, int refNote ) { m_refPitch = refPitch; m_refNote = refNote; m_zeroNote = 0; m_scale.clear(); for (int i = 0; i < 12; ++i) m_scale.push_back(::powf(2.0f, (i + 1) / 12.0f)); m_mapRepeatInc = 1; m_mapping.clear(); m_mapping.push_back(0); updateBasePitch(); } void synthv1_tuning::updateBasePitch (void) { // Clever, huh? m_basePitch = 1.0f; m_basePitch = m_refPitch / noteToPitch(m_refNote); } // Load custom Scala key-map file (.kbm) bool synthv1_tuning::loadKeyMapFile ( const QString& keyMapFile ) { QFile file(keyMapFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; QTextStream fs(&file); int mapSize = -1; int firstNote = -1; int lastNote = -1; int zeroNote = -1; int refNote = -1; float refPitch = 0.0f; int mapRepeatInc = -1; QVector mapping; while (!fs.atEnd()) { const QString& line = fs.readLine().simplified(); // Skip all-whitespace lines... if (line.isEmpty()) continue; // Skip comment lines... if (line.at(0) == '!') continue; bool ok = false; const QString& val = line.section(' ', 0, 0); // An active range should be defined on this line... if (line.at(0) == '<') { // No overlap is checked for; // it wouldn't hurt anything if ranges overlapped. const int min = line.section(' ', 1, 1).toInt(&ok); if (!ok || min < 0) return false; ok = false; const int max = line.section(' ', 2, 2).toInt(&ok); if (!ok || max < min || max > 127) return false; } else if (mapSize < 0) { mapSize = val.toInt(&ok); if (!ok || mapSize < 0) return false; } else if (firstNote < 0) { firstNote = val.toInt(&ok); if (!ok || firstNote < 0 || firstNote > 127) return false; } else if (lastNote < 0) { lastNote = val.toInt(&ok); if (!ok || lastNote < 0 || lastNote > 127) return false; } else if (zeroNote < 0) { zeroNote = val.toInt(&ok); if (!ok || zeroNote < 0 || zeroNote > 127) return false; } else if (refNote < 0) { refNote = val.toInt(&ok); if (!ok || refNote < 0 || refNote > 127) return false; } else if (refPitch <= 0.0f) { refPitch = val.toFloat(&ok); if (!ok || refPitch < 0.001f) return false; } else if (mapRepeatInc < 0) { mapRepeatInc = val.toInt(&ok); if (!ok || mapRepeatInc < 0) return false; } else if (line.at(0).toLower() == 'x') { mapping.push_back(-1); // unmapped key } else { const int mapEntry = val.toInt(&ok); if (!ok || mapEntry < 0) return false; mapping.push_back(mapEntry); } } // Didn't get far enough? if (mapRepeatInc < 0) return false; // Special case for "automatic" linear mapping if (mapSize == 0) { if (!mapping.empty()) return false; m_keyMapFile = keyMapFile; m_zeroNote = zeroNote; m_refNote = refNote; m_refPitch = refPitch; m_mapRepeatInc = 1; m_mapping.clear(); m_mapping.push_back(0); updateBasePitch(); return true; } // Some of the kbm files included with Scala have // extra x's at the end for no good reason //if (mapping.size() > mapSize) // return false; mapping.resize(mapSize); // Check to make sure reference pitch is actually mapped int refIndex = (refNote - zeroNote) % mapSize; if (refIndex < 0) refIndex += mapSize; if (mapping.at(refIndex) < 0) return false; m_keyMapFile = keyMapFile; m_zeroNote = zeroNote; m_refNote = refNote; m_refPitch = refPitch; if (mapRepeatInc == 0) m_mapRepeatInc = mapSize; else m_mapRepeatInc = mapRepeatInc; m_mapping = mapping; updateBasePitch(); return true; } // Load custom Scala scale file (.scl) bool synthv1_tuning::loadScaleFile ( const QString& scaleFile ) { QFile file(scaleFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; QTextStream fs(&file); QString scaleDesc; int scaleSize = -1; QVector scale; while (!fs.atEnd()) { const QString& line = fs.readLine().simplified(); // Skip all-whitespace lines after description... if (line.isEmpty() && !scaleDesc.isEmpty()) continue; // Skip comment lines if (line.at(0) == '!') continue; if (scaleDesc.isEmpty()) scaleDesc = line; else if (scaleSize < 0) { bool ok = false; scaleSize = line.section(' ', 0, 0).toInt(&ok); if (!ok || scaleSize < 0) return false; } else scale.push_back(parseScaleLine(line)); } if (scaleDesc.isEmpty() || scale.size() != scaleSize) return false; m_scaleFile = scaleFile; m_scaleDesc = scaleDesc; m_scale = scale; updateBasePitch(); return true; } // Convert a single line of a Scala scale file to a frequency relative to 1/1. float synthv1_tuning::parseScaleLine ( const QString& line ) const { bool ok = false; if (line.contains('.')) { // Treat as cents... const float cents = line.section(' ', 0, 0).toFloat(&ok); if (!ok || cents < 0.001f) return 0.0f; else return ::powf(2.0f, cents / 1200.0f); } else { // Treat as ratio... const long n = line.section('/', 0, 0).toLong(&ok); if (!ok || n < 0) return 0.0f; ok = false; const long d = line.section('/', 1, 1).toLong(&ok); if (!ok || d < 0) return 0.0f; else return float(n) / float(d); } } // The main pitch/frequency (Hz) getter. float synthv1_tuning::noteToPitch ( int note ) const { if (note < 0 || note > 127 || m_mapping.empty()) return 0.0f; const int mapSize = m_mapping.size(); int nRepeats = (note - m_zeroNote) / mapSize; int mapIndex = (note - m_zeroNote) % mapSize; if (mapIndex < 0) { mapIndex += mapSize; --nRepeats; } if (m_mapping.at(mapIndex) < 0) return 0.0f; // unmapped note const int scaleDegree = nRepeats * m_mapRepeatInc + m_mapping.at(mapIndex); const int scaleSize = m_scale.size(); int nOctaves = scaleDegree / scaleSize; int scaleIndex = scaleDegree % scaleSize; if (scaleIndex < 0) { scaleIndex += scaleSize; --nOctaves; } const float pitch = m_basePitch * ::powf(m_scale.at(scaleSize - 1), nOctaves); if (scaleIndex > 0) return pitch * m_scale.at(scaleIndex - 1); else return pitch; } // end of synthv1_tuning.cpp synthv1-1.5.1/src/PaxHeaders/synthv1_wave.cpp0000644000000000000000000000013215270627745016160 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_wave.cpp0000644000175000001440000002512215270627745016152 0ustar00rncbcusers// synthv1_wave.cpp // /**************************************************************************** Copyright (C) 2012-2023, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_wave.h" #include #include //------------------------------------------------------------------------- // synthv1_wave_sched - local module schedule thread stuff. // #include "synthv1_sched.h" class synthv1_wave_sched : public synthv1_sched { public: // ctor. synthv1_wave_sched (synthv1_wave *wave) : synthv1_sched(nullptr, Wave), m_wave(wave) {} // process reset (virtual). void process(int) { m_wave->reset_sync(); } private: // instance variables. synthv1_wave *m_wave; }; //------------------------------------------------------------------------- // synthv1_wave - smoothed (integrating oversampled) wave table. // // ctor. synthv1_wave::synthv1_wave ( uint32_t nsize, uint16_t nover, uint16_t ntabs ) : m_nsize(nsize), m_nover(nover), m_ntabs(ntabs), m_shape(Saw), m_width(1.0f), m_bandl(false), m_srate(44100.0f), m_phase0(0.0f), m_srand(0), m_min_freq(0.0f), m_max_freq(0.0f), m_sched(nullptr) { const uint16_t ntabs1 = m_ntabs + 1; m_tables = new float * [ntabs1]; for (uint16_t itab = 0; itab < ntabs1; ++itab) m_tables[itab] = new float [m_nsize + 4]; if (m_ntabs > 0) m_sched = new synthv1_wave_sched(this); reset_sync(); } // dtor. synthv1_wave::~synthv1_wave (void) { if (m_sched) delete m_sched; const uint16_t ntabs1 = m_ntabs + 1; for (uint16_t itab = 0; itab < ntabs1; ++itab) delete [] m_tables[itab]; delete [] m_tables; } // init. void synthv1_wave::reset ( Shape shape, float width, bool bandl ) { m_shape = shape; m_width = width; m_bandl = bandl; if (m_sched) m_sched->schedule(); else reset_sync(); } void synthv1_wave::reset_sync (void) { switch (m_shape) { case Pulse: reset_pulse(); break; case Saw: reset_saw(); break; case Sine: reset_sine(); break; case Rand: reset_rand(); break; case Noise: reset_noise(); // Fall thru... default: break; } } // init pulse tables. void synthv1_wave::reset_pulse (void) { reset_pulse_part(m_ntabs); if (m_bandl) { for (uint16_t itab = 0; itab < m_ntabs; ++itab) reset_pulse_part(itab); m_max_freq = (0.25f * m_srate); m_min_freq = m_max_freq / float(1 << m_ntabs); } else { m_max_freq = (0.5f * m_srate); m_min_freq = m_max_freq; } } // init saw tables. void synthv1_wave::reset_saw (void) { reset_saw_part(m_ntabs); if (m_bandl) { for (uint16_t itab = 0; itab < m_ntabs; ++itab) reset_saw_part(itab); m_max_freq = (0.25f * m_srate); m_min_freq = m_max_freq / float(1 << m_ntabs); } else { m_max_freq = (0.5f * m_srate); m_min_freq = m_max_freq; } } // init sine tables. void synthv1_wave::reset_sine (void) { reset_sine_part(m_ntabs); if (m_bandl && m_width < 1.0f) { for (uint16_t itab = 0; itab < m_ntabs; ++itab) reset_sine_part(itab); m_max_freq = (0.25f * m_srate); m_min_freq = m_max_freq / float(1 << m_ntabs); } else { m_max_freq = (0.5f * m_srate); m_min_freq = m_max_freq; } } // init random tables. void synthv1_wave::reset_rand (void) { reset_rand_part(m_ntabs); if (m_bandl) { for (uint16_t itab = 0; itab < m_ntabs; ++itab) reset_rand_part(itab); m_max_freq = (0.25f * m_srate); m_min_freq = m_max_freq / float(1 << m_ntabs); } else { m_max_freq = (0.5f * m_srate); m_min_freq = m_max_freq; } } // init noise tables. void synthv1_wave::reset_noise (void) { reset_noise_part(m_ntabs); if (m_bandl) { for (uint16_t itab = 0; itab < m_ntabs; ++itab) reset_noise_part(itab); m_max_freq = (0.25f * m_srate); m_min_freq = m_max_freq / float(1 << m_ntabs); } else { m_max_freq = (0.5f * m_srate); m_min_freq = m_max_freq; } } // init pulse partial table. void synthv1_wave::reset_pulse_part ( uint16_t itab ) { const uint16_t nparts = (itab < m_ntabs ? 1 << itab : 0); const float p0 = float(m_nsize); const float w2 = p0 * m_width * 0.5f + 0.001f; float *frames = m_tables[itab]; for (uint32_t i = 0; i < m_nsize; ++i) { const float p = float(i); if (nparts > 0) { const float gibbs = 0.5f * M_PI / float(nparts); float sum = 0.0f; for (uint32_t n = 0; n < nparts; ++n) { const float gn = ::cosf(gibbs * float(n)); const float dn = float(n + 1) * M_PI; const float wn = 2.0f * dn; const float g2 = gn * gn / dn; sum += g2 * ::sinf(wn * (w2 - p) / p0); sum += g2 * ::sinf(wn * (p - p0) / p0); } frames[i] = 2.0f * sum; } else { frames[i] = (p < w2 ? 1.0f : -1.0f); } } reset_filter(itab); reset_normalize(itab); reset_interp(itab); } // init saw partial table. void synthv1_wave::reset_saw_part ( uint16_t itab ) { const uint16_t nparts = (itab < m_ntabs ? 1 << itab : 0); const float p0 = float(m_nsize); const float w0 = p0 * m_width; float *frames = m_tables[itab]; for (uint32_t i = 0; i < m_nsize; ++i) { const float p = float(i); if (nparts > 0) { const float gibbs = 0.5f * M_PI / float(nparts); float sum = 0.0f; float sgn = 2.0f; for (uint32_t n = 0; n < nparts; ++n) { const float gn = ::cosf(gibbs * float(n)); const float dn = float(n + 1) * M_PI; const float wn = 2.0f * dn; const float g2 = gn * gn / dn; if (w0 < 1.0f) sum += g2 * ::sinf(wn * p / p0); else if (w0 >= p0) sum += g2 * ::sinf(wn * (p0 - p) / p0); else { sum -= sgn * g2 * ::cosf(wn * (w0 - p) / p0) / dn; sum += sgn * g2 * ::cosf(wn * (p - p0) / p0) / dn; sgn = -sgn; } } frames[i] = 2.0f * sum; } else if (p < w0) { frames[i] = 2.0f * p / w0 - 1.0f; } else { frames[i] = 1.0f - 2.0f * (1.0f + (p - w0)) / (p0 - w0); } } reset_filter(itab); reset_normalize(itab); reset_interp(itab); } // init sine partial table. void synthv1_wave::reset_sine_part ( uint16_t itab ) { const float width = (itab >= m_ntabs ? m_width : 1.0f + float(itab) * (m_width - 1.0f) / float(m_ntabs)); const float p0 = float(m_nsize); const float w0 = p0 * width; const float w2 = w0 * 0.5f; float *frames = m_tables[itab]; for (uint32_t i = 0; i < m_nsize; ++i) { float p = float(i); if (p < w2) frames[i] = ::sinf(2.0f * M_PI * p / w0); else frames[i] = ::sinf(M_PI * (p + (p0 - w0)) / (p0 - w2)); } if (width < 1.0f) { reset_filter(itab); reset_normalize(itab); } reset_interp(itab); } // init random partial table. void synthv1_wave::reset_rand_part ( uint16_t itab ) { const uint16_t nparts = (itab < m_ntabs ? 1 << itab : 0); const float p0 = float(m_nsize); const float w0 = p0 * m_width; const uint32_t ihold = (uint32_t(p0 - w0) >> 3) + 1; float *frames = m_tables[itab]; if (nparts > 0) { const float *pntabs = m_tables[m_ntabs]; const uint32_t nholds = m_nsize / ihold; const uint32_t ntabs2 = m_ntabs << itab; uint32_t npart2 = nparts; uint32_t nhold2 = nholds; while (npart2 * nhold2 > ntabs2) { if (npart2 > m_ntabs) npart2 >>= 1; else if (nhold2 > m_ntabs) nhold2 >>= 1; } const float wk = p0 / float(nhold2); const float w2 = 0.5f * wk; const float gibbs = 0.5f * M_PI / float(npart2); for (uint32_t i = 0; i < m_nsize; ++i) { const float p = float(i); float sum = 0.0f; for (uint32_t n = 0; n < npart2; ++n) { const float gn = ::cosf(gibbs * float(n)); const float dn = float(n + 1) * M_PI; const float wn = 2.0f * dn; const float g2 = gn * gn / dn; float pk = 0.0f; for (uint32_t k = 0; k < nhold2; ++k) { const float gk = g2 * pntabs[uint32_t(pk + w2)]; sum += gk * ::sinf(wn * (wk - p + pk) / p0); sum += gk * ::sinf(wn * (p - p0 - pk) / p0); pk += wk; } } frames[i] = 2.0f * sum; } } else { m_srand = uint32_t(w0); float phold = 0.0f; for (uint32_t i = 0; i < m_nsize; ++i) { if ((i % ihold) == 0) phold = pseudo_randf(); frames[i] = phold; } } reset_filter(itab); reset_normalize(itab); reset_interp(itab); } // init random partial table. void synthv1_wave::reset_noise_part ( uint16_t itab ) { if (itab == m_ntabs) { const float p0 = float(m_nsize); const float w0 = p0 * m_width; m_srand = uint32_t(w0) ^ 0x9631; // magic! } float *frames = m_tables[itab]; for (uint32_t i = 0; i < m_nsize; ++i) frames[i] = pseudo_randf(); // reset_filter(itab); // reset_normalize(itab); reset_interp(itab); } // post-processors. void synthv1_wave::reset_filter ( uint16_t itab ) { float *frames = m_tables[itab]; uint32_t i, k = 0; for (i = 1; i < m_nsize; ++i) { const float p1 = frames[i - 1]; const float p2 = frames[i]; if (p1 < 0.0f && p2 >= 0.0f) { k = i; break; } } for (uint16_t n = 0; n < m_nover; ++n) { float p = frames[k]; for (i = 0; i < m_nsize; ++i) { if (++k >= m_nsize) k = 0; p = 0.5f * (frames[k] + p); frames[k] = p; } } } void synthv1_wave::reset_normalize ( uint16_t itab ) { float *frames = m_tables[itab]; uint32_t i; float pmax = 0.0f; float pmid = 0.0f; if (m_ntabs > 0) { for (i = 0; i < m_nsize; ++i) pmid += frames[i]; pmid /= float(m_nsize); } else { for (i = 0; i < m_nsize; ++i) { const float p = frames[i]; if (pmax < p) pmax = p; else if (pmid > p) pmid = p; } pmid = 0.5f * (pmax + pmid); pmax = 0.0f; } pmax = 0.0f; for (i = 0; i < m_nsize; ++i) { frames[i] -= pmid; const float p = ::fabsf(frames[i]); if (pmax < p) pmax = p; } if (pmax > 0.0f) { const float gain = 1.0f / pmax; for (i = 0; i < m_nsize; ++i) frames[i] *= gain; } } void synthv1_wave::reset_interp ( uint16_t itab ) { float *frames = m_tables[itab]; uint32_t i; for (i = m_nsize; i < m_nsize + 4; ++i) frames[i] = frames[i - m_nsize]; if (itab == m_ntabs) { uint32_t k = 0; for (i = 1; i < m_nsize; ++i) { const float p1 = frames[i - 1]; const float p2 = frames[i]; if (p1 < 0.0f && p2 >= 0.0f) k = i; } m_phase0 = float(k) / float(m_nsize); } } // end of synthv1_wave.cpp synthv1-1.5.1/src/PaxHeaders/synthv1_formant.cpp0000644000000000000000000000013215270627745016664 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_formant.cpp0000644000175000001440000002012115270627745016650 0ustar00rncbcusers// synthv1_formant.cpp // /**************************************************************************** Copyright (C) 2012-2021, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_formant.h" //--------------------------------------------------------------------- // formant filter. // // A formant parallel filter after Dennis H. Klatt's // Software for a cascade/parallel formant synthesizer // 1979 MIT; 1980 Acoustical Society of America. // // formant tables. // // The Canonical Csound Reference Manual // Appendix D. Formant Values // http://www.csounds.com/manual/html/MiscFormants.html // synthv1_formant::Vtab synthv1_formant::g_alto_vtab[NUM_VOWELS] = { // Table D.1. alto “aâ€� {{ 800.0f, 1150.0f, 2800.0f, 3500.0f, 4950.0f }, { 0.0f, -4.0f, -20.0f, -36.0f, -60.0f }, { 80.0f, 90.0f, 120.0f, 130.0f, 140.0f }}, // Table D.2. alto “eâ€� {{ 400.0f, 1600.0f, 2700.0f, 3300.0f, 4950.0f }, { 0.0f, -24.0f, -30.0f, -35.0f, -60.0f }, { 60.0f, 80.0f, 120.0f, 150.0f, 200.0f }}, // Table D.3. alto “iâ€� {{ 350.0f, 1700.0f, 2700.0f, 3700.0f, 4950.0f }, { 0.0f, -20.0f, -30.0f, -36.0f, -60.0f }, { 50.0f, 100.0f, 120.0f, 150.0f, 200.0f }}, // Table D.4. alto “oâ€� {{ 450.0f, 800.0f, 2830.0f, 3500.0f, 4950.0f }, { 0.0f, -9.0f, -16.0f, -28.0f, -55.0f }, { 70.0f, 80.0f, 100.0f, 130.0f, 135.0f }}, // Table D.5. alto “uâ€� {{ 325.0f, 700.0f, 2530.0f, 3500.0f, 4950.0f }, { 0.0f, -12.0f, -30.0f, -40.0f, -64.0f }, { 50.0f, 60.0f, 170.0f, 180.0f, 200.0f }} }; synthv1_formant::Vtab synthv1_formant::g_bass_vtab[NUM_VOWELS] = { // Table D.6. bass “aâ€� {{ 600.0f, 1040.0f, 2250.0f, 2450.0f, 2750.0f }, { 0.0f, -7.0f, -9.0f, -9.0f, -20.0f }, { 60.0f, 70.0f, 110.0f, 120.0f, 130.0f }}, // Table D.7. bass “eâ€� {{ 400.0f, 1620.0f, 2400.0f, 2800.0f, 3100.0f }, { 0.0f, -12.0f, -9.0f, -12.0f, -18.0f }, { 40.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.8. bass “iâ€� {{ 250.0f, 1750.0f, 2600.0f, 3050.0f, 3340.0f }, { 0.0f, -30.0f, -16.0f, -22.0f, -28.0f }, { 60.0f, 90.0f, 100.0f, 120.0f, 120.0f }}, // Table D.9. bass “oâ€� {{ 400.0f, 750.0f, 2400.0f, 2600.0f, 2900.0f }, { 0.0f, -11.0f, -21.0f, -20.0f, -40.0f }, { 40.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.10. bass “uâ€� {{ 350.0f, 600.0f, 2400.0f, 2675.0f, 2950.0f }, { 0.0f, -20.0f, -32.0f, -28.0f, -36.0f }, { 40.0f, 80.0f, 100.0f, 120.0f, 120.0f }} }; synthv1_formant::Vtab synthv1_formant::g_countertenor_vtab[NUM_VOWELS] = { // Table D.11. countertenor “aâ€� {{ 660.0f, 1120.0f, 2750.0f, 3000.0f, 3350.0f }, { 0.0f, -6.0f, -23.0f, -24.0f, -38.0f }, { 80.0f, 90.0f, 120.0f, 130.0f, 140.0f }}, // Table D.12. countertenor “eâ€� {{ 440.0f, 1800.0f, 2700.0f, 3000.0f, 3300.0f }, { 0.0f, -14.0f, -18.0f, -20.0f, -20.0f }, { 70.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.13. countertenor “iâ€� {{ 270.0f, 1850.0f, 2900.0f, 3350.0f, 3590.0f }, { 0.0f, -24.0f, -24.0f, -36.0f, -36.0f }, { 40.0f, 90.0f, 100.0f, 120.0f, 120.0f }}, // Table D.14. countertenor “oâ€� {{ 430.0f, 820.0f, 2700.0f, 3000.0f, 3300.0f }, { 0.0f, -10.0f, -26.0f, -22.0f, -34.0f }, { 40.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.15. countertenor “uâ€� {{ 370.0f, 630.0f, 2750.0f, 3000.0f, 3400.0f }, { 0.0f, -20.0f, -23.0f, -30.0f, -34.0f }, { 40.0f, 60.0f, 100.0f, 120.0f, 120.0f }} }; synthv1_formant::Vtab synthv1_formant::g_soprano_vtab[NUM_VOWELS] = { // Table D.16. soprano “aâ€� {{ 800.0f, 1150.0f, 2900.0f, 3900.0f, 4950.0f }, { 0.0f, -6.0f, -32.0f, -20.0f, -50.0f }, { 80.0f, 90.0f, 120.0f, 130.0f, 140.0f }}, // Table D.17. soprano “eâ€� {{ 350.0f, 2000.0f, 2800.0f, 3600.0f, 4950.0f }, { 0.0f, -20.0f, -15.0f, -40.0f, -56.0f }, { 60.0f, 100.0f, 120.0f, 150.0f, 200.0f }}, // Table D.18. soprano “iâ€� {{ 270.0f, 2140.0f, 2950.0f, 3900.0f, 4950.0f }, { 0.0f, -12.0f, -26.0f, -26.0f, -44.0f }, { 60.0f, 90.0f, 100.0f, 120.0f, 120.0f }}, // Table D.19. soprano “oâ€� {{ 450.0f, 800.0f, 2830.0f, 3800.0f, 4950.0f }, { 0.0f, -11.0f, -22.0f, -22.0f, -50.0f }, { 40.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.20. soprano “uâ€� {{ 325.0f, 700.0f, 2700.0f, 3800.0f, 4950.0f }, { 0.0f, -16.0f, -35.0f, -40.0f, -60.0f }, { 50.0f, 60.0f, 170.0f, 180.0f, 200.0f }} }; synthv1_formant::Vtab synthv1_formant::g_tenor_vtab[NUM_VOWELS] = { // Table D.21. tenor “aâ€� {{ 650.0f, 1080.0f, 2650.0f, 2900.0f, 3250.0f }, { 0.0f, -6.0f, -7.0f, -8.0f, -22.0f }, { 80.0f, 90.0f, 120.0f, 130.0f, 140.0f }}, // Table D.22. tenor “eâ€� {{ 400.0f, 1700.0f, 2600.0f, 3200.0f, 3580.0f }, { 0.0f, -14.0f, -12.0f, -14.0f, -20.0f }, { 70.0f, 80.0f, 100.0f, 120.0f, 120.0f }}, // Table D.23. tenor “iâ€� {{ 290.0f, 1870.0f, 2800.0f, 3250.0f, 3540.0f }, { 0.0f, -15.0f, -18.0f, -20.0f, -30.0f }, { 40.0f, 90.0f, 100.0f, 120.0f, 120.0f }}, // Table D.24. tenor “oâ€� {{ 400.0f, 800.0f, 2600.0f, 2800.0f, 3000.0f }, { 0.0f, -10.0f, -12.0f, -12.0f, -26.0f }, { 70.0f, 80.0f, 100.0f, 130.0f, 135.0f }}, // Table D.25. tenor “uâ€� {{ 350.0f, 600.0f, 2700.0f, 2900.0f, 3300.0f }, { 0.0f, -20.0f, -17.0f, -14.0f, -26.0f }, { 40.0f, 60.0f, 100.0f, 120.0f, 120.0f }} }; // base vocal tables. synthv1_formant::Vtab *synthv1_formant::g_vtabs[NUM_VTABS] = { g_bass_vtab, g_tenor_vtab, g_countertenor_vtab, g_soprano_vtab, g_alto_vtab }; // compute coeffs. for given vocal formant table void synthv1_formant::Impl::vtab_coeffs ( Coeffs& coeffs, const Vtab *vtab, uint32_t i, float p ) { const float Fi = vtab->freq[i]; const float Gi = vtab->gain[i]; const float Bi = vtab->band[i] * p; const float Ai = ::powf(10.0f, (0.05f * Gi)); const float Ri = ::expf(-M_PI * Bi / m_srate); coeffs.b2 = Ri * Ri; coeffs.b1 = 2.0f * Ri * ::cosf(2.0f * M_PI * Fi / m_srate); coeffs.a0 = Ai * (1.0f - coeffs.b1 + coeffs.b2); } // reset method impl. void synthv1_formant::Impl::reset_coeffs ( float cutoff, float reso ) { const float fK = cutoff * float(NUM_VTABS - 1); const uint32_t k = uint32_t(fK); const float fJ = (fK - float(k)) * float(NUM_VOWELS - 1); const uint32_t j = uint32_t(fJ); const float dJ = (fJ - float(j)); // vowel morph fraction const float q = 4.0f * reso * reso + 1.0f; const float p = 1.0f / q; // vocal/vowel formant morphing const Vtab *vtabs = g_vtabs[k]; const Vtab *vtab1 = &vtabs[j]; const Vtab *vtab2 = vtab1; if (j < NUM_VOWELS - 1) vtab2 = &vtabs[j + 1]; else if (k < NUM_VTABS - 1) vtab2 = &g_vtabs[k + 1][0]; Coeffs coeff2; for (uint32_t i = 0; i < NUM_FORMANTS; ++i) { Coeffs& coeff1 = m_ctabs[i]; vtab_coeffs(coeff1, vtab1, i, p); vtab_coeffs(coeff2, vtab2, i, p); coeff1.a0 += dJ * (coeff2.a0 - coeff1.a0); coeff1.b1 += dJ * (coeff2.b1 - coeff1.b1); coeff1.b2 += dJ * (coeff2.b2 - coeff1.b2); } } // reset coeffs. method void synthv1_formant::reset_coeffs (void) { if (m_pImpl) { m_pImpl->reset_coeffs(m_cutoff, m_reso); for (uint32_t i = 0; i < NUM_FORMANTS; ++i) m_filters[i].reset_coeffs(m_pImpl->coeffs(i)); } } // end of synthv1_formant.cpp synthv1-1.5.1/src/PaxHeaders/synthv1widget.ui0000644000000000000000000000013215270627745016175 xustar0030 mtime=1793273829.974582027 30 atime=1793273829.974582027 30 ctime=1793273829.974582027 synthv1-1.5.1/src/synthv1widget.ui0000644000175000001440000023054015270627745016171 0ustar00rncbcusers rncbc aka Rui Nuno Capela synthv1 - an old-school polyphonic synthesizer Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. synthv1widget :/images/synthv1.svg 0 0 Randomize Random Swap A A true Swap B B true Panic Panic QTabBar::RoundedNorth Qt::Horizontal 20 20 0 0 &Help 0 Synth 1 DCO1 DCO1 Wave 1 DCO1 Wave Shape 1 Shape 1 DCO1 Wave Width 1 Width 1 Qt::Vertical 20 8 DCO1 Wave Bandlimit 1 Bandl.1 DCO1 Wave Sync 1 Sync 1 Qt::Horizontal 8 8 DCO1 Wave 2 DCO1 Wave Shape 2 Shape 2 DCO1 Wave Width 2 Width 2 Qt::Vertical 20 8 DCO1 Wave Bandlimit 2 Bandl.2 DCO1 Wave Sync 2 Sync 2 Qt::Horizontal 8 8 DCO1 Balance Balance DCO1 Detune Detune DCO1 Phase Phase Qt::Horizontal 8 8 DCO1 Ring Mod Ring Mod Qt::Horizontal 8 8 DCO1 Octave Octave DCO1 Tuning Tuning Qt::Horizontal 8 8 DCO1 Glide Glide Qt::Horizontal 8 8 DCO1 Env.Time Env.Time Qt::Vertical 20 0 DCF1 Enabled DCF1 true DCF1 DCF1 Cutoff Cutoff DCF1 Resonance Reso DCF1 Type Type DCF1 Slope Slope DCF1 Envelope Envelope DCF1 Envelope DCF1 Attack Attack DCF1 Decay Decay DCF1 Sustain Sustain DCF1 Release Release LFO1 Enabled LFO1 true LFO1 Wave LFO1 Wave Shape Shape LFO1 Wave Width Width LFO1 Envelope LFO1 Attack Attack LFO1 Decay Decay LFO1 Sustain Sustain LFO1 Release Release LFO1 BPM BPM LFO1 Rate Rate LFO1 Sync Sync Qt::Horizontal 8 8 LFO1 Sweep Sweep LFO1 Pitch Pitch Qt::Horizontal 8 8 LFO1 Balance Balance LFO1 Ring Mod Ring Mod Qt::Horizontal 8 8 LFO1 Cutoff Cutoff LFO1 Resonance Reso Qt::Horizontal 8 8 LFO1 Panning Panning LFO1 Volume Volume DCA1 DCA1 Volume Volume DCA1 Envelope DCA1 Attack Attack DCA1 Decay Decay DCA1 Sustain Sustain DCA1 Release Release DEF1 DEF1 Pitchbend Pitchbend DEF1 Modwheel Modwheel DEF1 Pressure Pressure DEF1 Velocity Velocity DEF1 Channel Channel DEF1 Mono Mono OUT1 OUT1 Stereo Width Width OUT1 Panning Panning Qt::Horizontal 8 8 OUT1 FX Send Effects Qt::Horizontal 8 8 OUT1 Volume Volume Synth 2 DCO2 DCO2 Wave 1 DCO2 Wave Shape 1 Shape 1 DCO2 Wave Width 1 Width 1 Qt::Vertical 20 8 DCO2 Wave Bandlimit 1 Bandl.1 DCO2 Wave Sync 1 Sync 1 Qt::Horizontal 8 8 DCO2 Wave 2 DCO2 Wave Shape 2 Shape 2 DCO2 Wave Width 2 Width 2 Qt::Vertical 20 8 DCO2 Wave Bandlimit 2 Bandl.2 DCO2 Wave Sync 2 Sync 2 Qt::Horizontal 8 8 DCO2 Balance Balance DCO2 Detune Detune DCO2 Phase Phase Qt::Horizontal 8 8 DCO2 Ring Mod Ring Mod Qt::Horizontal 8 8 DCO2 Octave Octave DCO2 Tuning Tuning Qt::Horizontal 8 8 DCO2 Glide Glide Qt::Horizontal 8 8 DCO2 Env.Time Env.Time Qt::Vertical 8 0 DCF2 Enabled DCF2 true DCF2 DCF2 Cutoff Cutoff DCF2 Resonance Reso DCF2 Type Type DCF2 Slope Slope DCF2 Envelope Envelope DCF2 Envelope DCF2 Attack Attack DCF2 Decay Decay DCF2 Sustain Sustain DCF2 Release Release LFO2 Enabled LFO2 true LFO2 Wave LFO2 Wave Shape Shape LFO2 Wave Width Width LFO2 Envelope LFO2 Attack Attack LFO2 Decay Decay LFO2 Sustain Sustain LFO2 Release Release LFO2 BPM BPM LFO2 Rate Rate LFO2 Sync Sync Qt::Horizontal 8 8 LFO2 Sweep Sweep LFO2 Pitch Pitch Qt::Horizontal 8 8 LFO2 Balance Balance LFO1 Ring Mod Ring Mod Qt::Horizontal 8 8 LFO2 Cutoff Cutoff LFO2 Resonance Reso Qt::Horizontal 8 8 LFO2 Panning Panning LFO2 Volume Volume DCA2 DCA2 Volume Volume DCA2 Envelope DCA2 Attack Attack DCA2 Decay Decay DCA2 Sustain Sustain DCA2 Release Release DEF2 DEF2 Pitchbend Pitchbend DEF2 Modwheel Modwheel DEF2 Pressure Pressure DEF2 Velocity Velocity DEF2 Channel Channel DEF2 Mono Mono OUT2 OUT2 Stereo Width Width OUT2 Panning Panning Qt::Horizontal 8 8 OUT2 FX Send Effects Qt::Horizontal 8 8 OUT2 Volume Volume Effects Qt::Horizontal 20 20 Chorus Chorus Wet Wet Qt::Horizontal QSizePolicy::Fixed 8 8 Chorus Delay Delay Chorus Feedback Feedback Chorus Rate Rate Chorus Modulation Modulation Flanger Flanger Wet Wet Qt::Horizontal QSizePolicy::Fixed 8 8 Flanger Delay Delay Flanger Feedback Feedback Flanger Daft Daft Phaser Phaser Wet Wet Qt::Horizontal QSizePolicy::Fixed 8 8 Phaser Rate Rate Phaser Feedback Feedback Phaser Depth Depth Phaser Daft Daft Delay Delay Wet Wet Qt::Horizontal QSizePolicy::Fixed 8 8 Delay Delay Delay Delay Feedback Feedback Delay BPM BPM Qt::Horizontal 20 20 Reverb Reverb Wet Wet Qt::Horizontal QSizePolicy::Fixed 8 8 Reverb Room Room Reverb Damp Damp Reverb Feedback Feedback Reverb Width Width Dynamic Dynamic Compressor Compress Dynamic Limiter Limiter Qt::Vertical 8 8 &Configure... Configure Configure options &About... About Show information about this application program About &Qt... About Qt Show information about the Qt toolkit synthv1widget_preset QComboBox
synthv1widget_preset.h
synthv1widget_status QStatusBar
synthv1widget_status.h
synthv1widget_spin QDial
synthv1widget_param.h
synthv1widget_combo QDial
synthv1widget_param.h
synthv1widget_radio QDial
synthv1widget_param.h
synthv1widget_check QCheckBox
synthv1widget_param.h
synthv1widget_group QGroupBox
synthv1widget_param.h
synthv1widget_env QFrame
synthv1widget_env.h
1
synthv1widget_filt QFrame
synthv1widget_filt.h
1
synthv1widget_wave QFrame
synthv1widget_wave.h
1
synthv1-1.5.1/src/PaxHeaders/palette0000644000000000000000000000013215270627745014377 xustar0030 mtime=1793273829.973346489 30 atime=1793273829.973264068 30 ctime=1793273829.973346489 synthv1-1.5.1/src/palette/0000755000175000001440000000000015270627745014444 5ustar00rncbcuserssynthv1-1.5.1/src/palette/PaxHeaders/KXStudio.conf0000644000000000000000000000013215270627745017035 xustar0030 mtime=1793273829.973264068 30 atime=1793273829.973264068 30 ctime=1793273829.973264068 synthv1-1.5.1/src/palette/KXStudio.conf0000644000175000001440000000165515270627745017034 0ustar00rncbcusers[ColorThemes] KXStudio\AlternateBase=#0e0e0e, #0e0e0e, #0c0c0c KXStudio\Base=#070707, #070707, #060606 KXStudio\BrightText=#ffffff, #ffffff, #ffffff KXStudio\Button=#1c1c1c, #1c1c1c, #181818 KXStudio\ButtonText=#f0f0f0, #f0f0f0, #5a5a5a KXStudio\Dark=#818181, #818181, #818181 KXStudio\Highlight=#3c3c3c, #222222, #0e0e0e KXStudio\HighlightedText=#ffffff, #f0f0f0, #535353 KXStudio\Light=#bfbfbf, #bfbfbf, #bfbfbf KXStudio\Link=#6464e6, #6464e6, #22224a KXStudio\LinkVisited=#e664e6, #e664e6, #4a224a KXStudio\Mid=#5e5e5e, #5e5e5e, #5e5e5e KXStudio\Midlight=#9b9b9b, #9b9b9b, #9b9b9b KXStudio\NoRole=#000000, #000000, #000000 KXStudio\PlaceholderText=#000000, #000000, #000000 KXStudio\Shadow=#9b9b9b, #9b9b9b, #9b9b9b KXStudio\Text=#e6e6e6, #e6e6e6, #4a4a4a KXStudio\ToolTipBase=#040404, #040404, #040404 KXStudio\ToolTipText=#e6e6e6, #e6e6e6, #e6e6e6 KXStudio\Window=#111111, #111111, #0e0e0e KXStudio\WindowText=#f0f0f0, #f0f0f0, #535353 synthv1-1.5.1/src/palette/PaxHeaders/Wonton Soup.conf0000644000000000000000000000013215270627745017516 xustar0030 mtime=1793273829.973346489 30 atime=1793273829.973264068 30 ctime=1793273829.973346489 synthv1-1.5.1/src/palette/Wonton Soup.conf0000644000175000001440000000202615270627745017506 0ustar00rncbcusers[ColorThemes] Wonton%20Soup\AlternateBase=#434750, #434750, #3b3e46 Wonton%20Soup\Base=#3c4048, #3c4048, #34383f Wonton%20Soup\BrightText=#ffffff, #ffffff, #ffffff Wonton%20Soup\Button=#525863, #525863, #484d57 Wonton%20Soup\ButtonText=#d2def0, #d2def0, #6f7682 Wonton%20Soup\Dark=#282b31, #282b31, #23262b Wonton%20Soup\Highlight=#78889c, #515a67, #40444d Wonton%20Soup\HighlightedText=#d1e1f4, #b6c1d0, #616872 Wonton%20Soup\Light=#5f6572, #5f6572, #565c68 Wonton%20Soup\Link=#9cd4ff, #9cd4ff, #526677 Wonton%20Soup\LinkVisited=#4080ff, #4080ff, #364c77 Wonton%20Soup\Mid=#3f444c, #3f444c, #383b43 Wonton%20Soup\Midlight=#545a65, #545a65, #4b515b Wonton%20Soup\NoRole=#000000, #000000, #000000 Wonton%20Soup\PlaceholderText=#000000, #000000, #000000 Wonton%20Soup\Shadow=#1d1f23, #1d1f23, #191b1e Wonton%20Soup\Text=#d2def0, #d2def0, #636973 Wonton%20Soup\ToolTipBase=#b6c1d0, #b6c1d0, #b6c1d0 Wonton%20Soup\ToolTipText=#2a2c30, #2a2c30, #2a2c30 Wonton%20Soup\Window=#494e58, #494e58, #40444d Wonton%20Soup\WindowText=#b6c1d0, #b6c1d0, #616872 synthv1-1.5.1/src/PaxHeaders/appdata0000644000000000000000000000013215270627745014353 xustar0030 mtime=1793273829.969840478 30 atime=1793273829.969653869 30 ctime=1793273829.969840478 synthv1-1.5.1/src/appdata/0000755000175000001440000000000015270627745014420 5ustar00rncbcuserssynthv1-1.5.1/src/appdata/PaxHeaders/org.rncbc.synthv1.desktop0000644000000000000000000000013215270627745021313 xustar0030 mtime=1793273829.969823513 30 atime=1793273829.969823513 30 ctime=1793273829.969823513 synthv1-1.5.1/src/appdata/org.rncbc.synthv1.desktop0000644000175000001440000000100115270627745021273 0ustar00rncbcusers[Desktop Entry] Name=synthv1 Version=1.0 GenericName=MIDI Comment=synthv1 is an old school polyphonic synthesizer Comment[fr]=synthv1 est un synthétiseur polyphonique à l'ancienne Exec=synthv1_jack %f Icon=org.rncbc.synthv1 Categories=Audio;AudioVideo;Midi;X-Alsa;X-Jack;Qt; MimeType=application/x-synthv1-preset; Keywords=Audio;MIDI;ALSA;JACK;Synthesizer;LV2;Qt; Terminal=false Type=Application StartupWMClass=synthv1_jack X-Window-Icon=synthv1 X-SuSE-translate=true X-NSM-Capable=true X-NSM-Exec=synthv1_jack synthv1-1.5.1/src/appdata/PaxHeaders/org.rncbc.synthv1.metainfo.xml0000644000000000000000000000013215270627745022243 xustar0030 mtime=1793273829.969840478 30 atime=1793273829.969823513 30 ctime=1793273829.969840478 synthv1-1.5.1/src/appdata/org.rncbc.synthv1.metainfo.xml0000644000175000001440000000332015270627745022231 0ustar00rncbcusers org.rncbc.synthv1 FSFAP GPL-2.0+ synthv1 an old-school polyphonic synthesizer

synthv1 is an old-school 4-oscillator subtractive polyphonic synthesizer with stereo fx.

features:

  • pure stand-alone JACK client with JACK-session, NSM and both JACK MIDI and ALSA MIDI input support;
  • LV2 instrument plugin. URI: http\://synthv1.sourceforge.net/lv2

org.rncbc.synthv1.desktop synthv1_jack https://synthv1.sourceforge.io/image/synthv1-screenshot9.png The main window showing the application in action Audio MIDI ALSA JACK Synthesizer LV2 Qt https://synthv1.sourceforge.io https://github.com/rncbc/synthv1 rncbc.org rncbc aka. Rui Nuno Capela rncbc@rncbc.org
synthv1-1.5.1/src/PaxHeaders/synthv1widget_preset.cpp0000644000000000000000000000013215270627745017724 xustar0030 mtime=1793273829.975582031 30 atime=1793273829.975582031 30 ctime=1793273829.975582031 synthv1-1.5.1/src/synthv1widget_preset.cpp0000644000175000001440000002615315270627745017723 0ustar00rncbcusers// synthv1widget_preset.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1widget_preset.h" #include "synthv1_config.h" #include #include #include #include #include //------------------------------------------------------------------------- // synthv1widget_preset - Custom preset-box widget. // // Constructor. synthv1widget_preset::synthv1widget_preset ( QWidget *pParent ) : QWidget(pParent) { m_pNewButton = new QToolButton(); m_pOpenButton = new QToolButton(); m_pComboBox = new synthv1widget_presets::ComboBox(); m_pSaveButton = new QToolButton(); m_pDeleteButton = new QToolButton(); m_pResetButton = new QToolButton(); m_pPresetsView = m_pComboBox->presetsView(); m_pNewButton->setIcon(QIcon(":/images/presetNew.png")); m_pOpenButton->setIcon(QIcon(":/images/presetOpen.png")); m_pComboBox->setEditable(true); m_pComboBox->setValidator(m_pPresetsView->presetsValidator()); m_pComboBox->setCompleter(nullptr); m_pComboBox->setMinimumWidth(240); m_pSaveButton->setIcon(QIcon(":/images/presetSave.png")); m_pDeleteButton->setIcon(QIcon(":/images/presetDelete.png")); m_pResetButton->setText("Reset"); m_pNewButton->setToolTip(tr("New Preset")); m_pOpenButton->setToolTip(tr("Open Preset")); m_pSaveButton->setToolTip(tr("Save Preset")); m_pDeleteButton->setToolTip(tr("Delete Preset")); m_pResetButton->setToolTip(tr("Reset Preset")); QHBoxLayout *pHBoxLayout = new QHBoxLayout(); pHBoxLayout->setContentsMargins(2, 2, 2, 2); pHBoxLayout->setSpacing(4); pHBoxLayout->addWidget(m_pNewButton); pHBoxLayout->addWidget(m_pOpenButton); pHBoxLayout->addWidget(m_pComboBox); pHBoxLayout->addWidget(m_pSaveButton); pHBoxLayout->addWidget(m_pDeleteButton); pHBoxLayout->addSpacing(4); pHBoxLayout->addWidget(m_pResetButton); QWidget::setLayout(pHBoxLayout); m_iInitPreset = 0; m_iDirtyPreset = 0; // UI signal/slot connections... QObject::connect(m_pNewButton, SIGNAL(clicked()), SLOT(newPreset())); QObject::connect(m_pOpenButton, SIGNAL(clicked()), SLOT(openPreset())); QObject::connect(m_pComboBox, SIGNAL(currentTextChanged(const QString&)), SLOT(activatePreset(const QString&))); #if 0 QObject::connect(m_pComboBox, #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) SIGNAL(textActivated(const QString&)), #else SIGNAL(activated(const QString&)), #endif SLOT(activatePreset(const QString&))); #endif QObject::connect(m_pSaveButton, SIGNAL(clicked()), SLOT(savePreset())); QObject::connect(m_pDeleteButton, SIGNAL(clicked()), SLOT(deletePreset())); QObject::connect(m_pResetButton, SIGNAL(clicked()), SLOT(resetPreset())); loadPresets(); } // Preset name/text accessors. void synthv1widget_preset::clearPreset (void) { ++m_iInitPreset; const bool bBlockSignals = m_pComboBox->blockSignals(true); m_pComboBox->clearEditText(); m_pComboBox->blockSignals(bBlockSignals); } void synthv1widget_preset::setPreset ( const QString& sPreset ) { const bool bBlockSignals = m_pComboBox->blockSignals(true); m_pComboBox->setCurrentPreset(sPreset); m_pComboBox->blockSignals(bBlockSignals); } QString synthv1widget_preset::preset (void) const { return m_pComboBox->currentPreset(); } // Check whether current preset may be reset. bool synthv1widget_preset::queryPreset (void) { if (m_iInitPreset == 0) return true; synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return false; if (m_iDirtyPreset > 0) { const QString& sPreset(pConfig->sPreset); if (sPreset.isEmpty()) { if (QMessageBox::warning(this, tr("Warning"), tr("Some parameters have been changed.\n\n" "Do you want to discard the changes?"), QMessageBox::Discard | QMessageBox::Cancel) == QMessageBox::Cancel) return false; } else { switch (QMessageBox::warning(this, tr("Warning"), tr("Some preset parameters have been changed:\n\n" "\"%1\".\n\nDo you want to save the changes?") .arg(sPreset), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Save: savePreset(sPreset); // Fall thru... case QMessageBox::Discard: break; default: // Cancel... setPreset(sPreset); return false; } } } return true; } // Preset management slots... void synthv1widget_preset::activatePreset ( const QString& sPreset ) { QTreeWidgetItem *pPresetItem = m_pPresetsView->presetItem(sPreset); if (pPresetItem && queryPreset()) { loadPreset(sPreset); emit presetActivated(sPreset); return; } if (pPresetItem == nullptr && m_pPresetsView->bankItem(sPreset)) m_pComboBox->resetCurrentPreset(); stabilizePreset(); } void synthv1widget_preset::loadPreset ( const QString& sPreset ) { if (sPreset.isEmpty()) return; synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig) { emit loadPresetFile(sPreset, pConfig->presetFile(sPreset)); ++m_iInitPreset; pConfig->sPreset = sPreset; setPreset(sPreset); } stabilizePreset(); } void synthv1widget_preset::newPreset (void) { if (!queryPreset()) return; synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig) { emit newPresetFile(); pConfig->sPreset.clear(); clearPreset(); } stabilizePreset(); } void synthv1widget_preset::openPreset (void) { synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return; m_pPresetsView->setPresetDir(pConfig->sPresetDir); m_pPresetsView->setPresetExt(PROJECT_NAME); m_pPresetsView->setDontUseNativeDialogs(pConfig->bDontUseNativeDialogs); const QStringList& files = m_pPresetsView->openPresetFiles(); if (files.isEmpty()) return; if (!queryPreset()) return; int iPreset = 0; QString sAfterPreset = m_pComboBox->currentPreset(); QStringListIterator iter(files); while (iter.hasNext()) { const QString& sPresetFile = iter.next(); const QFileInfo fi(sPresetFile); const QString& sPreset = m_pPresetsView->presetRenum(fi.completeBaseName()); pConfig->setPresetFile(sPreset, sPresetFile); synthv1_presets::Preset *pPreset = pConfig->presets.add_preset(sPreset, sAfterPreset); if (pPreset) { pPreset->set_file(sPresetFile); if (++iPreset == 1) { ++m_iInitPreset; emit loadPresetFile(sPreset, sPresetFile); pConfig->sPreset = sPreset; const QString& sPresetDir = fi.absolutePath(); if (QFileInfo(sPresetDir).permission(QFile::WriteUser)) pConfig->sPresetDir = sPresetDir; } sAfterPreset = sPreset; } } if (iPreset > 0) loadPresets(); } void synthv1widget_preset::savePreset (void) { savePreset(m_pComboBox->currentPreset()); } void synthv1widget_preset::savePreset ( const QString& sPreset ) { synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return; m_pPresetsView->setPresetDir(pConfig->sPresetDir); m_pPresetsView->setPresetExt(PROJECT_NAME); m_pPresetsView->setDontUseNativeDialogs(pConfig->bDontUseNativeDialogs); if (sPreset.isEmpty()) return; const QString& sPresetFile = m_pPresetsView->savePresetFile(sPreset); int iPreset = 0; if (!sPresetFile.isEmpty()) { emit savePresetFile(sPreset, sPresetFile); pConfig->setPresetFile(sPreset, sPresetFile); synthv1_presets::Preset *pPreset = pConfig->presets.add_preset(sPreset); if (pPreset) { pPreset->set_file(sPresetFile); ++m_iInitPreset; pConfig->sPreset = sPreset; pConfig->sPresetDir = QFileInfo(sPresetFile).absolutePath(); ++iPreset; } } if (iPreset > 0) loadPresets(); } void synthv1widget_preset::deletePreset (void) { const QString& sPreset = m_pComboBox->currentPreset(); if (sPreset.isEmpty()) return; synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return; if (QMessageBox::warning(QWidget::window(), tr("Warning"), tr("About to remove preset:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(sPreset), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; pConfig->presets.remove_preset(sPreset); pConfig->removePreset(sPreset); pConfig->sPreset.clear(); loadPresets(); } void synthv1widget_preset::resetPreset (void) { const QString& sPreset = m_pComboBox->currentPreset(); const bool bLoadPreset = (!sPreset.isEmpty() && m_pPresetsView->presetItem(sPreset) != nullptr); if (bLoadPreset && !queryPreset()) return; if (bLoadPreset) { loadPreset(sPreset); } else { emit resetPresetFile(); m_iDirtyPreset = 0; stabilizePreset(); } } // Widget refreshner-loader. void synthv1widget_preset::loadPresets (void) { synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return; const bool bBlockSignals = m_pComboBox->blockSignals(true); m_pPresetsView->loadPresets(&(pConfig->presets)); if (!pConfig->sPreset.isEmpty()) { if (m_pPresetsView->presetItem(pConfig->sPreset) != nullptr) { m_pComboBox->setCurrentPreset(pConfig->sPreset); m_iDirtyPreset = 0; } else { pConfig->sPreset.clear(); } } if (pConfig->sPreset.isEmpty()) { m_pComboBox->clearEditText(); ++m_iDirtyPreset; } m_pComboBox->blockSignals(bBlockSignals); stabilizePreset(); } void synthv1widget_preset::savePresets (void) { synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig == nullptr) return; if (m_pPresetsView->isDirtyPresets()) { m_pPresetsView->savePresets(&(pConfig->presets)); m_pPresetsView->setDirtyPresets(false); } } // Preset control. void synthv1widget_preset::initPreset (void) { synthv1_config *pConfig = synthv1_config::getInstance(); if (pConfig && !pConfig->sPreset.isEmpty()) loadPreset(pConfig->sPreset); else newPreset(); } // Dirty flag accessor. void synthv1widget_preset::setDirtyPreset ( bool bDirtyPreset ) { if (bDirtyPreset) ++m_iDirtyPreset; else m_iDirtyPreset = 0; stabilizePreset(); } bool synthv1widget_preset::isDirtyPreset (void) const { return (m_iDirtyPreset > 0); } void synthv1widget_preset::stabilizePreset (void) { const QString& sPreset = m_pComboBox->currentPreset(); const bool bEnabled = (!sPreset.isEmpty()); const bool bExists = (m_pPresetsView->presetItem(sPreset) != nullptr); const bool bDirty = (m_iDirtyPreset > 0); m_pSaveButton->setEnabled(bEnabled && (!bExists || bDirty)); m_pDeleteButton->setEnabled(bEnabled && bExists); m_pResetButton->setEnabled(bDirty); } // end of synthv1widget_preset.cpp synthv1-1.5.1/src/PaxHeaders/synthv1.cpp0000644000000000000000000000013215270627745015136 xustar0030 mtime=1793273829.973346489 30 atime=1793273829.973346489 30 ctime=1793273829.973346489 synthv1-1.5.1/src/synthv1.cpp0000644000175000001440000021556615270627745015145 0ustar00rncbcusers// synthv1.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1.h" #include "synthv1_wave.h" #include "synthv1_ramp.h" #include "synthv1_list.h" #include "synthv1_filter.h" #include "synthv1_formant.h" #include "synthv1_fx.h" #include "synthv1_reverb.h" #include "synthv1_config.h" #include "synthv1_controls.h" #include "synthv1_programs.h" #include "synthv1_tuning.h" #include "synthv1_sched.h" #ifdef CONFIG_DEBUG_0 #include #endif #include //------------------------------------------------------------------------- // synthv1_impl // // -- borrowed and revamped from synth.h of synth4 // Copyright (C) 2007 jorgen, linux-vst.com // const uint8_t MAX_VOICES = 64; // max polyphony const uint8_t MAX_NOTES = 128; const float MIN_ENV_MSECS = 0.5f; // min 500 usec per stage const float MAX_ENV_MSECS = 5000.0f; // max 5 sec per stage (default) const float DETUNE_SCALE = 0.5f; const float PHASE_SCALE = 0.5f; const float OCTAVE_SCALE = 12.0f; const float TUNING_SCALE = 1.0f; const float SWEEP_SCALE = 0.5f; const float PITCH_SCALE = 0.5f; const uint8_t MAX_DIRECT_NOTES = (MAX_VOICES >> 2); // maximum helper inline float synthv1_max ( float a, float b ) { return (a > b ? a : b); } // hyperbolic-tangent fast approximation inline float synthv1_tanhf ( const float x ) { const float x2 = x * x; return x * (27.0f + x2) / (27.0f + 9.0f * x2); } // sigmoids inline float synthv1_sigmoid ( const float x ) { // return 2.0f / (1.0f + ::expf(-5.0f * x)) - 1.0f; return synthv1_tanhf(2.0f * x); } inline float synthv1_sigmoid_0 ( const float x, const float t0 ) { const float t1 = 1.0f - t0; #if 0 if (x > +t1) return +t1 + t0 * synthv1_tanhf(+(x - t1) / t0); else if (x < -t1) return -t1 - t0 * synthv1_tanhf(-(x + t1) / t0); else return x; #else return (x < -1.0f ? -t1 : (x > +1.0f ? t1 : t1 * x * (1.5f - 0.5f * x * x))); #endif } inline float synthv1_sigmoid_1 ( const float x, const float t0 = 0.01f ) { return 0.5f * (1.0f + synthv1_sigmoid_0(2.0f * x - 1.0f, t0)); } // velocity hard-split curve inline float synthv1_velocity ( const float x, const float p = 0.2f ) { return ::powf(x, (1.0f - p)); } // pitchbend curve inline float synthv1_pow2f ( const float x ) { // simplest power-of-2 straight linearization // -- x argument valid in [-1, 1] interval // return 1.0f + (x < 0.0f ? 0.5f : 1.0f) * x; return ::powf(2.0f, x); } // convert note to frequency (hertz) inline float synthv1_freq2 ( float delta ) { return ::powf(2.0f, delta / 12.0f); } inline float synthv1_freq ( int note ) { return (440.0f / 32.0f) * synthv1_freq2(float(note - 9)); } // parameter port (basic) class synthv1_port { public: synthv1_port() : m_port(nullptr), m_value(0.0f), m_vport(0.0f) {} virtual ~synthv1_port() {} void set_port(float *port) { m_port = port; } float *port() const { return m_port; } virtual void set_value(float value) { m_value = value; if (m_port) m_vport = *m_port; } float value() const { return m_value; } float *value_ptr() { tick(1); return &m_value; } virtual float tick(uint32_t /*nstep*/) { if (m_port && ::fabsf(*m_port - m_vport) > 0.001f) set_value(*m_port); return m_value; } float operator *() { return tick(1); } private: float *m_port; float m_value; float m_vport; }; // parameter port (smoothed) class synthv1_port2 : public synthv1_port { public: synthv1_port2() : m_vtick(0.0f), m_vstep(0.0f), m_nstep(0) {} static const uint32_t NSTEP = 32; void set_value(float value) { m_vtick = synthv1_port::value(); m_nstep = NSTEP; m_vstep = (value - m_vtick) / float(m_nstep); synthv1_port::set_value(value); } float tick(uint32_t nstep) { if (m_nstep == 0) return synthv1_port::tick(nstep); if (m_nstep >= nstep) { m_vtick += m_vstep * float(nstep); m_nstep -= nstep; } else { m_vtick += m_vstep * float(m_nstep); m_nstep = 0; } return m_vtick; } private: float m_vtick; float m_vstep; uint32_t m_nstep; }; // envelope struct synthv1_env { // envelope stages enum Stage { Idle = 0, Attack, Decay, Sustain, Release, End }; // per voice struct State { // ctor. State() : running(false), stage(Idle), phase(0.0f), delta(0.0f), value(0.0f), c1(1.0f), c0(0.0f), frames(0) {} // process float tick() { if (running && frames > 0) { phase += delta; value = c1 * phase * (2.0f - phase) + c0; --frames; } return value; } // state bool running; Stage stage; float phase; float delta; float value; float c1, c0; uint32_t frames; }; void start(State *p) { p->running = true; p->stage = Attack; p->frames = uint32_t(*attack * *attack * max_frames); if (p->frames < min_frames1) // prevent click on too fast attack p->frames = min_frames1; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->value = 0.0f; p->c1 = 1.0f; p->c0 = 0.0f; } void next(State *p) { if (p->stage == Attack) { p->stage = Decay; p->frames = uint32_t(*decay * *decay * max_frames); if (p->frames < min_frames2) // prevent click on too fast decay p->frames = min_frames2; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->c1 = *sustain - 1.0f; p->c0 = p->value; } else if (p->stage == Decay) { p->running = false; // stay at this stage until note_off received p->stage = Sustain; p->frames = 0; p->phase = 0.0f; p->delta = 0.0f; p->c1 = 0.0f; p->c0 = p->value; } else if (p->stage == Release) { p->running = false; p->stage = End; p->frames = 0; p->phase = 0.0f; p->delta = 0.0f; p->value = 0.0f; p->c1 = 0.0f; p->c0 = 0.0f; } } void note_off(State *p) { p->running = true; p->stage = Release; p->frames = uint32_t(*release * *release * max_frames); if (p->frames < min_frames2) // prevent click on too fast release p->frames = min_frames2; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->c1 = -(p->value); p->c0 = p->value; } void note_off_fast(State *p) { p->running = true; p->stage = Release; p->frames = min_frames2; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->c1 = -(p->value); p->c0 = p->value; } void restart(State *p, bool legato) { p->running = true; if (legato) { p->stage = Decay; p->frames = min_frames2; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->c1 = *sustain - p->value; p->c0 = 0.0f; } else { p->stage = Attack; p->frames = uint32_t(*attack * *attack * max_frames); if (p->frames < min_frames1) p->frames = min_frames1; p->phase = 0.0f; p->delta = 1.0f / float(p->frames); p->c1 = 1.0f; p->c0 = 0.0f; } } // parameters synthv1_port attack; synthv1_port decay; synthv1_port sustain; synthv1_port release; uint32_t min_frames1; uint32_t min_frames2; uint32_t max_frames; }; // midi control struct synthv1_ctl { synthv1_ctl() { reset(); } void reset() { pressure = 0.0f; pitchbend = 1.0f; modwheel = 0.0f; panning = 0.0f; volume = 1.0f; sustain = false; } float pressure; float pitchbend; float modwheel; float panning; float volume; bool sustain; }; // dco struct synthv1_dco { synthv1_port shape1; synthv1_port width1; synthv1_port bandl1; synthv1_port sync1; synthv1_port shape2; synthv1_port width2; synthv1_port bandl2; synthv1_port sync2; synthv1_port balance; synthv1_port detune; synthv1_port phase; synthv1_port ringmod; synthv1_port octave; synthv1_port tuning; synthv1_port glide; synthv1_port envtime; float envtime0; }; // dcf struct synthv1_dcf { synthv1_port enabled; synthv1_port2 cutoff; synthv1_port2 reso; synthv1_port type; synthv1_port slope; synthv1_port2 envelope; synthv1_env env; }; // lfo struct synthv1_voice; struct synthv1_lfo { synthv1_port enabled; synthv1_port shape; synthv1_port width; synthv1_port2 bpm; synthv1_port2 rate; synthv1_port sync; synthv1_port2 sweep; synthv1_port2 pitch; synthv1_port2 balance; synthv1_port2 ringmod; synthv1_port2 cutoff; synthv1_port2 reso; synthv1_port2 panning; synthv1_port2 volume; synthv1_env env; synthv1_voice *psync; }; // dca struct synthv1_dca { synthv1_port volume; synthv1_env env; }; // def (ranges) struct synthv1_def { synthv1_port pitchbend; synthv1_port modwheel; synthv1_port pressure; synthv1_port velocity; synthv1_port channel; synthv1_port mono; }; // out (mix) struct synthv1_out { synthv1_port width; synthv1_port panning; synthv1_port fxsend; synthv1_port volume; }; // chorus (fx) struct synthv1_cho { synthv1_port wet; synthv1_port delay; synthv1_port feedb; synthv1_port rate; synthv1_port mod; }; // flanger (fx) struct synthv1_fla { synthv1_port wet; synthv1_port delay; synthv1_port feedb; synthv1_port daft; }; // phaser (fx) struct synthv1_pha { synthv1_port wet; synthv1_port rate; synthv1_port feedb; synthv1_port depth; synthv1_port daft; }; // delay (fx) struct synthv1_del { synthv1_port wet; synthv1_port delay; synthv1_port feedb; synthv1_port bpm; }; // reverb struct synthv1_rev { synthv1_port wet; synthv1_port room; synthv1_port damp; synthv1_port feedb; synthv1_port width; }; // dynamic(compressor/limiter) struct synthv1_dyn { synthv1_port compress; synthv1_port limiter; }; // keyboard/note range struct synthv1_key { synthv1_port low; synthv1_port high; bool is_note(int key) { return (key >= int(*low) && int(*high) >= key); } }; // glide (portamento) class synthv1_glide { public: synthv1_glide(float& last) : m_last(last) { reset(); } void reset( uint32_t frames = 0, float freq = 0.0f ) { m_frames = frames; if (m_frames > 0) { m_freq = m_last; m_step = (m_last - freq) / float(m_frames); } else { m_freq = 0.0f; m_step = 0.0f; } m_last = freq; } float tick() { if (m_frames > 0) { m_freq -= m_step; --m_frames; } return m_freq; } private: uint32_t m_frames; float m_step; float m_freq; float& m_last; }; // balancing smoother (1 parameter) class synthv1_bal1 : public synthv1_ramp1 { public: synthv1_bal1() : synthv1_ramp1(2) {} protected: float evaluate(uint16_t i) { synthv1_ramp1::update(); const float wbal = 0.25f * M_PI * (1.0f + m_param1_v); return M_SQRT2 * (i & 1 ? ::sinf(wbal) : ::cosf(wbal)); } }; // balancing smoother (2 parameters) class synthv1_bal2 : public synthv1_ramp2 { public: synthv1_bal2() : synthv1_ramp2(2) {} protected: float evaluate(uint16_t i) { synthv1_ramp2::update(); const float wbal = 0.25f * M_PI * (1.0f + m_param1_v) * (1.0f + m_param2_v); return M_SQRT2 * (i & 1 ? ::sinf(wbal) : ::cosf(wbal)); } }; // pressure smoother (3 parameters) class synthv1_pre : public synthv1_ramp3 { public: synthv1_pre() : synthv1_ramp3() {} protected: float evaluate(uint16_t) { synthv1_ramp3::update(); return m_param1_v * synthv1_max(m_param2_v, m_param3_v); } }; // common phasor (LFO sync) class synthv1_phasor { public: synthv1_phasor(uint32_t nsize = 1024) : m_nsize(nsize), m_nframes(0) {} void process(uint32_t nframes) { m_nframes += nframes; while (m_nframes >= m_nsize) m_nframes -= m_nsize; } float pshift() const { return float(m_nframes) / float(m_nsize); } private: uint32_t m_nsize; uint32_t m_nframes; }; // forward decl. class synthv1_impl; // voice struct synthv1_voice : public synthv1_list { synthv1_voice(synthv1_impl *pImpl); int note1, note2; // voice note float vel1, vel2; // key velocity float pre1, pre2; // key pressure/after-touch synthv1_oscillator dco11, dco12; // oscillators synthv1_oscillator dco21, dco22; synthv1_oscillator lfo1, lfo2; // low frequency oscillators float dco1_freq1, dco1_sample1; // frequency and phase float dco1_freq2, dco1_sample2; float dco2_freq1, dco2_sample1; float dco2_freq2, dco2_sample2; float lfo1_sample, lfo2_sample; float dco1_balance, dco2_balance; synthv1_bal2 dco1_bal, dco2_bal; // oscillators balance synthv1_filter1 dcf11, dcf12, dcf21, dcf22; // filters synthv1_filter2 dcf13, dcf14, dcf23, dcf24; synthv1_filter3 dcf15, dcf16, dcf25, dcf26; synthv1_formant dcf17, dcf18, dcf27, dcf28; synthv1_env::State dca1_env, dca2_env; // envelope states synthv1_env::State dcf1_env, dcf2_env; synthv1_env::State lfo1_env, lfo2_env; synthv1_glide dco1_glide1, dco1_glide2; // glides (portamento) synthv1_glide dco2_glide1, dco2_glide2; synthv1_pre dca1_pre, dca2_pre; float out1_panning, out2_panning; float out1_volume, out2_volume; synthv1_bal1 out1_pan, out2_pan; // output panning synthv1_ramp1 out1_vol, out2_vol; // output volume bool sustain1, sustain2; }; // MIDI input asynchronous status notification class synthv1_midi_in : public synthv1_sched { public: synthv1_midi_in (synthv1 *pSynth) : synthv1_sched(pSynth, MidiIn), m_enabled(false), m_count(0) {} void schedule_event() { if (m_enabled && ++m_count < 2) schedule(-1); } void schedule_note(int key, int vel) { if (m_enabled) schedule((vel << 7) | key); } void process(int) {} void enabled(bool on) { m_enabled = on; m_count = 0; } uint32_t count() { const uint32_t ret = m_count; m_count = 0; return ret; } private: bool m_enabled; uint32_t m_count; }; // micro-tuning/instance implementation class synthv1_tun { public: synthv1_tun() : enabled(false), refPitch(440.0f), refNote(69) {} bool enabled; float refPitch; int refNote; QString scaleFile; QString keyMapFile; }; // polyphonic synth implementation class synthv1_impl { public: synthv1_impl(synthv1 *pSynth, uint16_t nchannels, float srate, uint32_t nsize); ~synthv1_impl(); void setChannels(uint16_t nchannels); uint16_t channels() const; void setSampleRate(float srate); float sampleRate() const; void setBufferSize(uint32_t nsize); uint32_t bufferSize() const; void setTempo(float bpm); float tempo() const; void setParamPort(synthv1::ParamIndex index, float *pfParam); synthv1_port *paramPort(synthv1::ParamIndex index); void setParamValue(synthv1::ParamIndex index, float fValue); float paramValue(synthv1::ParamIndex index); synthv1_controls *controls(); synthv1_programs *programs(); void setTuningEnabled(bool enabled); bool isTuningEnabled() const; void setTuningRefPitch(float refPitch); float tuningRefPitch() const; void setTuningRefNote(int refNote); int tuningRefNote() const; void setTuningScaleFile(const char *pszScaleFile); const char *tuningScaleFile() const; void setTuningKeyMapFile(const char *pszKeyMapFile); const char *tuningKeyMapFile() const; void resetTuning(); void process_midi(uint8_t *data, uint32_t size); void process(float **ins, float **outs, uint32_t nframes); void stabilize(); void reset(); void midiInEnabled(bool on); uint32_t midiInCount(); void directNoteOn(int note, int vel); bool running(bool on); synthv1_wave dco1_wave1, dco1_wave2; synthv1_wave dco2_wave1, dco2_wave2; synthv1_wave_lf lfo1_wave, lfo2_wave; float dco1_last1; float dco1_last2; float dco2_last1; float dco2_last2; synthv1_formant::Impl dcf1_formant; synthv1_formant::Impl dcf2_formant; protected: void updateEnvTimes(); void updateEnvTimes_1(); void updateEnvTimes_2(); void allSoundOff(); void allControllersOff(); void allControllersOff_1(); void allControllersOff_2(); void allNotesOff(); void allNotesOff_1(); void allNotesOff_2(); void allSustainOff_1(); void allSustainOff_2(); void allSustainOn_1(); void allSustainOn_2(); float get_bpm ( float bpm ) const { return (bpm > 0.0f ? bpm : m_bpm); } synthv1_voice *alloc_voice () { synthv1_voice *pv = m_free_list.next(); if (pv) { m_free_list.remove(pv); m_play_list.append(pv); ++m_nvoices; } return pv; } void free_voice ( synthv1_voice *pv ) { if (m_lfo1.psync == pv) m_lfo1.psync = nullptr; if (m_lfo2.psync == pv) m_lfo2.psync = nullptr; m_play_list.remove(pv); m_free_list.append(pv); --m_nvoices; } void alloc_sfxs(uint32_t nsize); private: synthv1_config m_config; synthv1_controls m_controls; synthv1_programs m_programs; synthv1_midi_in m_midi_in; synthv1_tun m_tun; uint16_t m_nchannels; float m_srate; float m_bpm; float m_freqs[MAX_NOTES]; synthv1_ctl m_ctl1, m_ctl2; synthv1_dco m_dco1, m_dco2; synthv1_dcf m_dcf1, m_dcf2; synthv1_lfo m_lfo1, m_lfo2; synthv1_dca m_dca1, m_dca2; synthv1_out m_out1, m_out2; synthv1_def m_def1, m_def2; synthv1_cho m_cho; synthv1_fla m_fla; synthv1_pha m_pha; synthv1_del m_del; synthv1_rev m_rev; synthv1_dyn m_dyn; synthv1_key m_key; synthv1_voice **m_voices; synthv1_voice *m_note1[MAX_NOTES]; synthv1_voice *m_note2[MAX_NOTES]; synthv1_list m_free_list; synthv1_list m_play_list; synthv1_ramp1 m_wid1, m_wid2; synthv1_bal2 m_pan1, m_pan2; synthv1_ramp2 m_vol1, m_vol2; float **m_sfxs; uint32_t m_nsize; synthv1_fx_chorus m_chorus; synthv1_fx_flanger *m_flanger; synthv1_fx_phaser *m_phaser; synthv1_fx_delay *m_delay; synthv1_fx_comp *m_comp; synthv1_reverb m_reverb; // process direct note on/off... volatile uint16_t m_direct_note; struct direct_note { uint8_t status, note, vel; } m_direct_notes[MAX_DIRECT_NOTES]; volatile int m_nvoices; volatile bool m_running; }; // voice constructor synthv1_voice::synthv1_voice ( synthv1_impl *pImpl ) : note1(-1), note2(-1), vel1(0.0f), vel2(0.0f), pre1(0.0f), pre2(0.0f), dco11(&pImpl->dco1_wave1), dco12(&pImpl->dco1_wave2), dco21(&pImpl->dco2_wave1), dco22(&pImpl->dco2_wave2), lfo1(&pImpl->lfo1_wave), lfo2(&pImpl->lfo2_wave), dco1_freq1(0.0f), dco1_sample1(0.0f), dco1_freq2(0.0f), dco1_sample2(0.0f), dco2_freq1(0.0f), dco2_sample1(0.0f), dco2_freq2(0.0f), dco2_sample2(0.0f), lfo1_sample(0.0f), lfo2_sample(0.0f), dcf17(&pImpl->dcf1_formant), dcf18(&pImpl->dcf1_formant), dcf27(&pImpl->dcf2_formant), dcf28(&pImpl->dcf2_formant), dco1_glide1(pImpl->dco1_last1), dco1_glide2(pImpl->dco1_last2), dco2_glide1(pImpl->dco2_last1), dco2_glide2(pImpl->dco2_last2), out1_panning(0.0f), out2_panning(0.0f), out1_volume(1.0f), out2_volume(1.0f), sustain1(false), sustain2(false) { } // engine constructor synthv1_impl::synthv1_impl ( synthv1 *pSynth, uint16_t nchannels, float srate, uint32_t nsize ) : m_controls(pSynth), m_programs(pSynth), m_midi_in(pSynth), m_bpm(180.0f), m_nvoices(0), m_running(false) { // max env. stage length (default) m_dco1.envtime0 = m_dco2.envtime0 = 0.0001f * MAX_ENV_MSECS; // glide notes dco1_last1 = 0.0f; dco1_last2 = 0.0f; dco2_last1 = 0.0f; dco2_last2 = 0.0f; // allocate voice pool. m_voices = new synthv1_voice * [MAX_VOICES]; for (int i = 0; i < MAX_VOICES; ++i) { m_voices[i] = new synthv1_voice(this); m_free_list.append(m_voices[i]); } for (int note = 0; note < MAX_NOTES; ++note) m_note1[note] = m_note2[note] = nullptr; // local buffers none yet m_sfxs = nullptr; m_nsize = 0; // flangers none yet m_flanger = nullptr; // phasers none yet m_phaser = nullptr; // delays none yet m_delay = nullptr; // compressors none yet m_comp = nullptr; // Micro-tuning support, if any... resetTuning(); // load controllers & programs database... m_config.loadControls(&m_controls); m_config.loadPrograms(&m_programs); // number of channels setChannels(nchannels); // set default sample rate setSampleRate(srate); // set default buffer size setBufferSize(nsize); // reset all voices allControllersOff(); allNotesOff(); running(true); } // destructor synthv1_impl::~synthv1_impl (void) { #if 0 // DO NOT save programs database here: // prevent multi-instance clash... m_config.savePrograms(&m_programs); #endif // deallocate voice pool. for (int i = 0; i < MAX_VOICES; ++i) delete m_voices[i]; delete [] m_voices; // deallocate local buffers alloc_sfxs(0); // deallocate channels setChannels(0); } void synthv1_impl::setChannels ( uint16_t nchannels ) { m_nchannels = nchannels; // deallocate flangers if (m_flanger) { delete [] m_flanger; m_flanger = nullptr; } // deallocate phasers if (m_phaser) { delete [] m_phaser; m_phaser = nullptr; } // deallocate delays if (m_delay) { delete [] m_delay; m_delay = nullptr; } // deallocate compressors if (m_comp) { delete [] m_comp; m_comp = nullptr; } } uint16_t synthv1_impl::channels (void) const { return m_nchannels; } void synthv1_impl::setSampleRate ( float srate ) { // set internal sample rate m_srate = srate; // update waves sample rate dco1_wave1.setSampleRate(m_srate); dco1_wave2.setSampleRate(m_srate); dco2_wave1.setSampleRate(m_srate); dco2_wave2.setSampleRate(m_srate); dcf1_formant.setSampleRate(m_srate); dcf2_formant.setSampleRate(m_srate); lfo1_wave.setSampleRate(m_srate); lfo2_wave.setSampleRate(m_srate); updateEnvTimes(); } float synthv1_impl::sampleRate (void) const { return m_srate; } void synthv1_impl::setBufferSize ( uint32_t nsize ) { // set nominal buffer size if (m_nsize < nsize) alloc_sfxs(nsize); } uint32_t synthv1_impl::bufferSize (void) const { return m_nsize; } void synthv1_impl::setTempo ( float bpm ) { // set nominal tempo (BPM) m_bpm = bpm; } float synthv1_impl::tempo (void) const { return m_bpm; } // allocate local buffers void synthv1_impl::alloc_sfxs ( uint32_t nsize ) { if (m_sfxs) { for (uint16_t k = 0; k < m_nchannels; ++k) delete [] m_sfxs[k]; delete [] m_sfxs; m_sfxs = nullptr; m_nsize = 0; } if (m_nsize < nsize) { m_nsize = nsize; m_sfxs = new float * [m_nchannels]; for (uint16_t k = 0; k < m_nchannels; ++k) m_sfxs[k] = new float [m_nsize]; } } void synthv1_impl::updateEnvTimes_1 (void) { // update envelope range times in frames const float srate_ms = 0.001f * m_srate; float envtime_msecs = 10000.0f * m_dco1.envtime0; if (envtime_msecs < MIN_ENV_MSECS) envtime_msecs = MIN_ENV_MSECS * 4.0f; const uint32_t min_frames1 = uint32_t(srate_ms * MIN_ENV_MSECS); const uint32_t min_frames2 = (min_frames1 << 2); const uint32_t max_frames = uint32_t(srate_ms * envtime_msecs); m_dcf1.env.min_frames1 = min_frames1; m_dcf1.env.min_frames2 = min_frames2; m_dcf1.env.max_frames = max_frames; m_lfo1.env.min_frames1 = min_frames1; m_lfo1.env.min_frames2 = min_frames2; m_lfo1.env.max_frames = max_frames; m_dca1.env.min_frames1 = min_frames1; m_dca1.env.min_frames2 = min_frames2; m_dca1.env.max_frames = max_frames; } void synthv1_impl::updateEnvTimes_2 (void) { // update envelope range times in frames const float srate_ms = 0.001f * m_srate; float envtime_msecs = 10000.0f * m_dco2.envtime0; if (envtime_msecs < MIN_ENV_MSECS) envtime_msecs = MIN_ENV_MSECS * 4.0f; const uint32_t min_frames1 = uint32_t(srate_ms * MIN_ENV_MSECS); const uint32_t min_frames2 = (min_frames1 << 2); const uint32_t max_frames = uint32_t(srate_ms * envtime_msecs); m_dcf2.env.min_frames1 = min_frames1; m_dcf2.env.min_frames2 = min_frames2; m_dcf2.env.max_frames = max_frames; m_lfo2.env.min_frames1 = min_frames1; m_lfo2.env.min_frames2 = min_frames2; m_lfo2.env.max_frames = max_frames; m_dca2.env.min_frames1 = min_frames1; m_dca2.env.min_frames2 = min_frames2; m_dca2.env.max_frames = max_frames; } void synthv1_impl::updateEnvTimes (void) { updateEnvTimes_1(); updateEnvTimes_2(); } void synthv1_impl::setParamPort ( synthv1::ParamIndex index, float *pfParam ) { static float s_fDummy = 0.0f; if (pfParam == nullptr) pfParam = &s_fDummy; synthv1_port *pParamPort = paramPort(index); if (pParamPort) pParamPort->set_port(pfParam); // check null connections. if (pfParam == &s_fDummy) return; // reset ramps after port (re)connection. switch (index) { case synthv1::OUT1_VOLUME: case synthv1::DCA1_VOLUME: m_vol1.reset( m_out1.volume.value_ptr(), &m_ctl1.volume); break; case synthv1::OUT1_WIDTH: m_wid1.reset( m_out1.width.value_ptr()); break; case synthv1::OUT1_PANNING: m_pan1.reset( m_out1.panning.value_ptr(), &m_ctl1.panning); break; case synthv1::OUT2_VOLUME: case synthv1::DCA2_VOLUME: m_vol2.reset( m_out2.volume.value_ptr(), &m_ctl2.volume); break; case synthv1::OUT2_WIDTH: m_wid2.reset( m_out2.width.value_ptr()); break; case synthv1::OUT2_PANNING: m_pan2.reset( m_out2.panning.value_ptr(), &m_ctl2.panning); break; default: break; } } synthv1_port *synthv1_impl::paramPort ( synthv1::ParamIndex index ) { synthv1_port *pParamPort = nullptr; switch (index) { case synthv1::DCO1_SHAPE1: pParamPort = &m_dco1.shape1; break; case synthv1::DCO1_WIDTH1: pParamPort = &m_dco1.width1; break; case synthv1::DCO1_BANDL1: pParamPort = &m_dco1.bandl1; break; case synthv1::DCO1_SYNC1: pParamPort = &m_dco1.sync1; break; case synthv1::DCO1_SHAPE2: pParamPort = &m_dco1.shape2; break; case synthv1::DCO1_WIDTH2: pParamPort = &m_dco1.width2; break; case synthv1::DCO1_BANDL2: pParamPort = &m_dco1.bandl2; break; case synthv1::DCO1_SYNC2: pParamPort = &m_dco1.sync2; break; case synthv1::DCO1_BALANCE: pParamPort = &m_dco1.balance; break; case synthv1::DCO1_DETUNE: pParamPort = &m_dco1.detune; break; case synthv1::DCO1_PHASE: pParamPort = &m_dco1.phase; break; case synthv1::DCO1_RINGMOD: pParamPort = &m_dco1.ringmod; break; case synthv1::DCO1_OCTAVE: pParamPort = &m_dco1.octave; break; case synthv1::DCO1_TUNING: pParamPort = &m_dco1.tuning; break; case synthv1::DCO1_GLIDE: pParamPort = &m_dco1.glide; break; case synthv1::DCO1_ENVTIME: pParamPort = &m_dco1.envtime; break; case synthv1::DCF1_ENABLED: pParamPort = &m_dcf1.enabled; break; case synthv1::DCF1_CUTOFF: pParamPort = &m_dcf1.cutoff; break; case synthv1::DCF1_RESO: pParamPort = &m_dcf1.reso; break; case synthv1::DCF1_TYPE: pParamPort = &m_dcf1.type; break; case synthv1::DCF1_SLOPE: pParamPort = &m_dcf1.slope; break; case synthv1::DCF1_ENVELOPE: pParamPort = &m_dcf1.envelope; break; case synthv1::DCF1_ATTACK: pParamPort = &m_dcf1.env.attack; break; case synthv1::DCF1_DECAY: pParamPort = &m_dcf1.env.decay; break; case synthv1::DCF1_SUSTAIN: pParamPort = &m_dcf1.env.sustain; break; case synthv1::DCF1_RELEASE: pParamPort = &m_dcf1.env.release; break; case synthv1::LFO1_ENABLED: pParamPort = &m_lfo1.enabled; break; case synthv1::LFO1_SHAPE: pParamPort = &m_lfo1.shape; break; case synthv1::LFO1_WIDTH: pParamPort = &m_lfo1.width; break; case synthv1::LFO1_BPM: pParamPort = &m_lfo1.bpm; break; case synthv1::LFO1_RATE: pParamPort = &m_lfo1.rate; break; case synthv1::LFO1_SYNC: pParamPort = &m_lfo1.sync; break; case synthv1::LFO1_SWEEP: pParamPort = &m_lfo1.sweep; break; case synthv1::LFO1_PITCH: pParamPort = &m_lfo1.pitch; break; case synthv1::LFO1_BALANCE: pParamPort = &m_lfo1.balance; break; case synthv1::LFO1_RINGMOD: pParamPort = &m_lfo1.ringmod; break; case synthv1::LFO1_CUTOFF: pParamPort = &m_lfo1.cutoff; break; case synthv1::LFO1_RESO: pParamPort = &m_lfo1.reso; break; case synthv1::LFO1_PANNING: pParamPort = &m_lfo1.panning; break; case synthv1::LFO1_VOLUME: pParamPort = &m_lfo1.volume; break; case synthv1::LFO1_ATTACK: pParamPort = &m_lfo1.env.attack; break; case synthv1::LFO1_DECAY: pParamPort = &m_lfo1.env.decay; break; case synthv1::LFO1_SUSTAIN: pParamPort = &m_lfo1.env.sustain; break; case synthv1::LFO1_RELEASE: pParamPort = &m_lfo1.env.release; break; case synthv1::DCA1_VOLUME: pParamPort = &m_dca1.volume; break; case synthv1::DCA1_ATTACK: pParamPort = &m_dca1.env.attack; break; case synthv1::DCA1_DECAY: pParamPort = &m_dca1.env.decay; break; case synthv1::DCA1_SUSTAIN: pParamPort = &m_dca1.env.sustain; break; case synthv1::DCA1_RELEASE: pParamPort = &m_dca1.env.release; break; case synthv1::OUT1_WIDTH: pParamPort = &m_out1.width; break; case synthv1::OUT1_PANNING: pParamPort = &m_out1.panning; break; case synthv1::OUT1_FXSEND: pParamPort = &m_out1.fxsend; break; case synthv1::OUT1_VOLUME: pParamPort = &m_out1.volume; break; case synthv1::DEF1_PITCHBEND: pParamPort = &m_def1.pitchbend; break; case synthv1::DEF1_MODWHEEL: pParamPort = &m_def1.modwheel; break; case synthv1::DEF1_PRESSURE: pParamPort = &m_def1.pressure; break; case synthv1::DEF1_VELOCITY: pParamPort = &m_def1.velocity; break; case synthv1::DEF1_CHANNEL: pParamPort = &m_def1.channel; break; case synthv1::DEF1_MONO: pParamPort = &m_def1.mono; break; case synthv1::DCO2_SHAPE1: pParamPort = &m_dco2.shape1; break; case synthv1::DCO2_WIDTH1: pParamPort = &m_dco2.width1; break; case synthv1::DCO2_BANDL1: pParamPort = &m_dco2.bandl1; break; case synthv1::DCO2_SYNC1: pParamPort = &m_dco2.sync1; break; case synthv1::DCO2_SHAPE2: pParamPort = &m_dco2.shape2; break; case synthv1::DCO2_WIDTH2: pParamPort = &m_dco2.width2; break; case synthv1::DCO2_BANDL2: pParamPort = &m_dco2.bandl2; break; case synthv1::DCO2_SYNC2: pParamPort = &m_dco2.sync2; break; case synthv1::DCO2_BALANCE: pParamPort = &m_dco2.balance; break; case synthv1::DCO2_DETUNE: pParamPort = &m_dco2.detune; break; case synthv1::DCO2_PHASE: pParamPort = &m_dco2.phase; break; case synthv1::DCO2_RINGMOD: pParamPort = &m_dco2.ringmod; break; case synthv1::DCO2_OCTAVE: pParamPort = &m_dco2.octave; break; case synthv1::DCO2_TUNING: pParamPort = &m_dco2.tuning; break; case synthv1::DCO2_GLIDE: pParamPort = &m_dco2.glide; break; case synthv1::DCO2_ENVTIME: pParamPort = &m_dco2.envtime; break; case synthv1::DCF2_ENABLED: pParamPort = &m_dcf2.enabled; break; case synthv1::DCF2_CUTOFF: pParamPort = &m_dcf2.cutoff; break; case synthv1::DCF2_RESO: pParamPort = &m_dcf2.reso; break; case synthv1::DCF2_TYPE: pParamPort = &m_dcf2.type; break; case synthv1::DCF2_SLOPE: pParamPort = &m_dcf2.slope; break; case synthv1::DCF2_ENVELOPE: pParamPort = &m_dcf2.envelope; break; case synthv1::DCF2_ATTACK: pParamPort = &m_dcf2.env.attack; break; case synthv1::DCF2_DECAY: pParamPort = &m_dcf2.env.decay; break; case synthv1::DCF2_SUSTAIN: pParamPort = &m_dcf2.env.sustain; break; case synthv1::DCF2_RELEASE: pParamPort = &m_dcf2.env.release; break; case synthv1::LFO2_ENABLED: pParamPort = &m_lfo2.enabled; break; case synthv1::LFO2_SHAPE: pParamPort = &m_lfo2.shape; break; case synthv1::LFO2_WIDTH: pParamPort = &m_lfo2.width; break; case synthv1::LFO2_BPM: pParamPort = &m_lfo2.bpm; break; case synthv1::LFO2_RATE: pParamPort = &m_lfo2.rate; break; case synthv1::LFO2_SYNC: pParamPort = &m_lfo2.sync; break; case synthv1::LFO2_SWEEP: pParamPort = &m_lfo2.sweep; break; case synthv1::LFO2_PITCH: pParamPort = &m_lfo2.pitch; break; case synthv1::LFO2_BALANCE: pParamPort = &m_lfo2.balance; break; case synthv1::LFO2_RINGMOD: pParamPort = &m_lfo2.ringmod; break; case synthv1::LFO2_CUTOFF: pParamPort = &m_lfo2.cutoff; break; case synthv1::LFO2_RESO: pParamPort = &m_lfo2.reso; break; case synthv1::LFO2_PANNING: pParamPort = &m_lfo2.panning; break; case synthv1::LFO2_VOLUME: pParamPort = &m_lfo2.volume; break; case synthv1::LFO2_ATTACK: pParamPort = &m_lfo2.env.attack; break; case synthv1::LFO2_DECAY: pParamPort = &m_lfo2.env.decay; break; case synthv1::LFO2_SUSTAIN: pParamPort = &m_lfo2.env.sustain; break; case synthv1::LFO2_RELEASE: pParamPort = &m_lfo2.env.release; break; case synthv1::DCA2_VOLUME: pParamPort = &m_dca2.volume; break; case synthv1::DCA2_ATTACK: pParamPort = &m_dca2.env.attack; break; case synthv1::DCA2_DECAY: pParamPort = &m_dca2.env.decay; break; case synthv1::DCA2_SUSTAIN: pParamPort = &m_dca2.env.sustain; break; case synthv1::DCA2_RELEASE: pParamPort = &m_dca2.env.release; break; case synthv1::OUT2_WIDTH: pParamPort = &m_out2.width; break; case synthv1::OUT2_PANNING: pParamPort = &m_out2.panning; break; case synthv1::OUT2_FXSEND: pParamPort = &m_out2.fxsend; break; case synthv1::OUT2_VOLUME: pParamPort = &m_out2.volume; break; case synthv1::DEF2_PITCHBEND: pParamPort = &m_def2.pitchbend; break; case synthv1::DEF2_MODWHEEL: pParamPort = &m_def2.modwheel; break; case synthv1::DEF2_PRESSURE: pParamPort = &m_def2.pressure; break; case synthv1::DEF2_VELOCITY: pParamPort = &m_def2.velocity; break; case synthv1::DEF2_CHANNEL: pParamPort = &m_def2.channel; break; case synthv1::DEF2_MONO: pParamPort = &m_def2.mono; break; case synthv1::CHO1_WET: pParamPort = &m_cho.wet; break; case synthv1::CHO1_DELAY: pParamPort = &m_cho.delay; break; case synthv1::CHO1_FEEDB: pParamPort = &m_cho.feedb; break; case synthv1::CHO1_RATE: pParamPort = &m_cho.rate; break; case synthv1::CHO1_MOD: pParamPort = &m_cho.mod; break; case synthv1::FLA1_WET: pParamPort = &m_fla.wet; break; case synthv1::FLA1_DELAY: pParamPort = &m_fla.delay; break; case synthv1::FLA1_FEEDB: pParamPort = &m_fla.feedb; break; case synthv1::FLA1_DAFT: pParamPort = &m_fla.daft; break; case synthv1::PHA1_WET: pParamPort = &m_pha.wet; break; case synthv1::PHA1_RATE: pParamPort = &m_pha.rate; break; case synthv1::PHA1_FEEDB: pParamPort = &m_pha.feedb; break; case synthv1::PHA1_DEPTH: pParamPort = &m_pha.depth; break; case synthv1::PHA1_DAFT: pParamPort = &m_pha.daft; break; case synthv1::DEL1_WET: pParamPort = &m_del.wet; break; case synthv1::DEL1_DELAY: pParamPort = &m_del.delay; break; case synthv1::DEL1_FEEDB: pParamPort = &m_del.feedb; break; case synthv1::DEL1_BPM: pParamPort = &m_del.bpm; break; case synthv1::REV1_WET: pParamPort = &m_rev.wet; break; case synthv1::REV1_ROOM: pParamPort = &m_rev.room; break; case synthv1::REV1_DAMP: pParamPort = &m_rev.damp; break; case synthv1::REV1_FEEDB: pParamPort = &m_rev.feedb; break; case synthv1::REV1_WIDTH: pParamPort = &m_rev.width; break; case synthv1::DYN1_COMPRESS: pParamPort = &m_dyn.compress; break; case synthv1::DYN1_LIMITER: pParamPort = &m_dyn.limiter; break; case synthv1::KEY1_LOW: pParamPort = &m_key.low; break; case synthv1::KEY1_HIGH: pParamPort = &m_key.high; break; default: break; } return pParamPort; } void synthv1_impl::setParamValue ( synthv1::ParamIndex index, float fValue ) { synthv1_port *pParamPort = paramPort(index); if (pParamPort) pParamPort->set_value(fValue); } float synthv1_impl::paramValue ( synthv1::ParamIndex index ) { synthv1_port *pParamPort = paramPort(index); return (pParamPort ? pParamPort->value() : 0.0f); } // handle midi input void synthv1_impl::process_midi ( uint8_t *data, uint32_t size ) { for (uint32_t i = 0; i < size; ++i) { // channel status const int channel = (data[i] & 0x0f) + 1; const int status = (data[i] & 0xf0); // channel filter const int ch1 = int(*m_def1.channel); const int ch2 = int(*m_def2.channel); const int on1 = (ch1 == 0 || ch1 == channel); const int on2 = (ch2 == 0 || ch2 == channel); // all system common/real-time ignored if (status == 0xf0) continue; // check data size (#1) if (++i >= size) break; const int key = (data[i] & 0x7f); // program change if (status == 0xc0) { if (on1 || on2) m_programs.prog_change(key); continue; } // channel aftertouch if (status == 0xd0) { const float pre = float(key) / 127.0f; if (on1) m_ctl1.pressure = pre; if (on2) m_ctl2.pressure = pre; continue; } // check data size (#2) if (++i >= size) break; // channel value const int value = (data[i] & 0x7f); // channel/controller filter if (!on1 && !on2) { if (status == 0xb0) m_controls.process_enqueue(channel, key, value); continue; } // note on if (status == 0x90 && value > 0) { if (!m_key.is_note(key)) continue; synthv1_voice *pv; // synth 1 if (on1) { // mono voice modes if (*m_def1.mono > 0.0f) { int n1 = 0; for (pv = m_play_list.next(); pv; pv = pv->next()) { if (pv->note1 >= 0 && pv->dca1_env.stage != synthv1_env::Release) { m_dcf1.env.note_off_fast(&pv->dcf1_env); m_lfo1.env.note_off_fast(&pv->lfo1_env); m_dca1.env.note_off_fast(&pv->dca1_env); if (++n1 > 1) { // there shall be only one m_note1[pv->note1] = nullptr; pv->note1 = -1; } } } } // note retrigger pv = m_note1[key]; if (pv && pv->note1 >= 0/* && !m_ctl1.sustain*/) { // retrigger fast release m_dcf1.env.note_off_fast(&pv->dcf1_env); m_lfo1.env.note_off_fast(&pv->lfo1_env); m_dca1.env.note_off_fast(&pv->dca1_env); m_note1[pv->note1] = nullptr; pv->note1 = -1; } } // synth 2 if (on2) { // mono voice modes if (*m_def2.mono > 0.0f) { int n2 = 0; for (pv = m_play_list.next(); pv; pv = pv->next()) { if (pv->note2 >= 0 && pv->dca2_env.stage != synthv1_env::Release) { m_dcf2.env.note_off_fast(&pv->dcf2_env); m_lfo2.env.note_off_fast(&pv->lfo2_env); m_dca2.env.note_off_fast(&pv->dca2_env); if (++n2 > 1) { // there shall be only one m_note2[pv->note2] = nullptr; pv->note2 = -1; } } } } // note retrigger pv = m_note2[key]; if (pv && pv->note2 >= 0/* && !m_ctl2.sustain*/) { // retrigger fast release m_dcf2.env.note_off_fast(&pv->dcf2_env); m_lfo2.env.note_off_fast(&pv->lfo2_env); m_dca2.env.note_off_fast(&pv->dca2_env); m_note2[pv->note2] = nullptr; pv->note2 = -1; } } // find free voice pv = alloc_voice(); if (pv) { // velocity (quadratic velocity law) float vel = float(value) / 127.0f; vel *= vel; // synth 1 if (on1) { // waveform pv->note1 = key; pv->vel1 = synthv1_velocity(vel, *m_def1.velocity); // balance pv->dco1_balance = 0.0f; pv->dco1_bal.reset(m_dco1.balance.value_ptr(), &pv->dco1_balance); // pressure/after-touch pv->pre1 = 0.0f; pv->dca1_pre.reset( m_def1.pressure.value_ptr(), &m_ctl1.pressure, &pv->pre1); // frequencies const float dco1_tuning = *m_dco1.octave * OCTAVE_SCALE + *m_dco1.tuning * TUNING_SCALE; const float dco1_detune = *m_dco1.detune * DETUNE_SCALE; const float dco1_freq = m_freqs[key] * synthv1_freq2(dco1_tuning); pv->dco1_freq1 = dco1_freq; pv->dco1_freq2 = dco1_freq; // syncs if (*m_dco1.sync1 > 0.5f) { pv->dco12.sync(&pv->dco11); } else { pv->dco1_freq2 *= synthv1_freq2(+ dco1_detune); pv->dco12.sync(nullptr); } if (*m_dco1.sync2 > 0.5f) { pv->dco11.sync(&pv->dco12); } else { pv->dco1_freq1 *= synthv1_freq2(- dco1_detune); pv->dco11.sync(nullptr); } // phases const float dco1_phase = *m_dco1.phase * PHASE_SCALE; pv->dco1_sample1 = pv->dco11.start( 0.0f, pv->dco1_freq1); pv->dco1_sample2 = pv->dco12.start(dco1_phase, pv->dco1_freq2); // filters const int dcf1_type = int(*m_dcf1.type); pv->dcf11.reset(synthv1_filter1::Type(dcf1_type)); pv->dcf12.reset(synthv1_filter1::Type(dcf1_type)); pv->dcf13.reset(synthv1_filter2::Type(dcf1_type)); pv->dcf14.reset(synthv1_filter2::Type(dcf1_type)); pv->dcf15.reset(synthv1_filter3::Type(dcf1_type)); pv->dcf16.reset(synthv1_filter3::Type(dcf1_type)); // formant filters const float dcf1_cutoff = *m_dcf1.cutoff; const float dcf1_reso = *m_dcf1.reso; pv->dcf17.reset_filters(dcf1_cutoff, dcf1_reso); pv->dcf18.reset_filters(dcf1_cutoff, dcf1_reso); // envelopes m_dcf1.env.start(&pv->dcf1_env); m_lfo1.env.start(&pv->lfo1_env); m_dca1.env.start(&pv->dca1_env); // lfos const float lfo1_pshift = (m_lfo1.psync ? m_lfo1.psync->lfo1.pshift() : 0.0f); pv->lfo1_sample = pv->lfo1.start(lfo1_pshift); if (*m_lfo1.sync > 0.0f && m_lfo1.psync == nullptr) m_lfo1.psync = pv; // glides (portamento) const float dco1_frames = uint32_t(*m_dco1.glide * *m_dco1.glide * m_srate); pv->dco1_glide1.reset(dco1_frames, pv->dco1_freq1); pv->dco1_glide2.reset(dco1_frames, pv->dco1_freq2); // panning pv->out1_panning = 0.0f; pv->out1_pan.reset(&pv->out1_panning); // volume pv->out1_volume = 1.0f; pv->out1_vol.reset(&pv->out1_volume); // sustain pv->sustain1 = false; // allocated m_note1[key] = pv; } // synth 2 if (on2) { // waveform pv->note2 = key; pv->vel2 = synthv1_velocity(vel, *m_def2.velocity); // balance pv->dco2_balance = 0.0f; pv->dco2_bal.reset(m_dco2.balance.value_ptr(), &pv->dco2_balance); // pressure/after-touch pv->pre2 = 0.0f; pv->dca2_pre.reset( m_def2.pressure.value_ptr(), &m_ctl2.pressure, &pv->pre2); // frequencies const float dco2_tuning = *m_dco2.octave * OCTAVE_SCALE + *m_dco2.tuning * TUNING_SCALE; const float dco2_detune = *m_dco2.detune * DETUNE_SCALE; const float dco2_freq = m_freqs[key] * synthv1_freq2(dco2_tuning); pv->dco2_freq1 = dco2_freq; pv->dco2_freq2 = dco2_freq; // syncs if (*m_dco2.sync1 > 0.5f) { pv->dco22.sync(&pv->dco21); } else { pv->dco2_freq2 *= synthv1_freq2(+ dco2_detune); pv->dco22.sync(nullptr); } if (*m_dco2.sync2 > 0.5f) { pv->dco21.sync(&pv->dco22); } else { pv->dco2_freq1 *= synthv1_freq2(- dco2_detune); pv->dco21.sync(nullptr); } // phases const float dco2_phase = *m_dco2.phase * PHASE_SCALE; pv->dco2_sample1 = pv->dco21.start( 0.0f, pv->dco2_freq1); pv->dco2_sample2 = pv->dco22.start(dco2_phase, pv->dco2_freq2); // filters const int dcf2_type = int(*m_dcf2.type); pv->dcf21.reset(synthv1_filter1::Type(dcf2_type)); pv->dcf22.reset(synthv1_filter1::Type(dcf2_type)); pv->dcf23.reset(synthv1_filter2::Type(dcf2_type)); pv->dcf24.reset(synthv1_filter2::Type(dcf2_type)); pv->dcf25.reset(synthv1_filter3::Type(dcf2_type)); pv->dcf26.reset(synthv1_filter3::Type(dcf2_type)); // formant filters const float dcf2_cutoff = *m_dcf2.cutoff; const float dcf2_reso = *m_dcf2.reso; pv->dcf27.reset_filters(dcf2_cutoff, dcf2_reso); pv->dcf28.reset_filters(dcf2_cutoff, dcf2_reso); // envelopes m_dcf2.env.start(&pv->dcf2_env); m_lfo2.env.start(&pv->lfo2_env); m_dca2.env.start(&pv->dca2_env); // lfos const float lfo2_pshift = (m_lfo2.psync ? m_lfo2.psync->lfo2.pshift() : 0.0f); pv->lfo2_sample = pv->lfo2.start(lfo2_pshift); if (*m_lfo2.sync > 0.0f && m_lfo2.psync == nullptr) m_lfo2.psync = pv; // glides (portamento) const float dco2_frames = uint32_t(*m_dco2.glide * *m_dco2.glide * m_srate); pv->dco2_glide1.reset(dco2_frames, pv->dco2_freq1); pv->dco2_glide2.reset(dco2_frames, pv->dco2_freq2); // sustain pv->sustain2 = false; // panning pv->out2_panning = 0.0f; pv->out2_pan.reset(&pv->out2_panning); // volume pv->out2_volume = 1.0f; pv->out2_vol.reset(&pv->out2_volume); // allocated m_note2[key] = pv; } } m_midi_in.schedule_note(key, value); } // note off else if (status == 0x80 || (status == 0x90 && value == 0)) { if (!m_key.is_note(key)) continue; synthv1_voice *pv; // synth 1 if (on1) { pv = m_note1[key]; if (pv && pv->note1 >= 0) { if (m_ctl1.sustain) pv->sustain1 = true; else if (!pv->sustain1) { if (pv->dca1_env.stage != synthv1_env::Release) { m_dca1.env.note_off(&pv->dca1_env); m_dcf1.env.note_off(&pv->dcf1_env); m_lfo1.env.note_off(&pv->lfo1_env); } m_note1[pv->note1] = nullptr; pv->note1 = -1; // mono legato? if (*m_def1.mono > 0.0f) { do pv = pv->prev(); while (pv && pv->note1 < 0); if (pv && pv->note1 >= 0) { const bool legato1 = (*m_def1.mono > 1.0f); m_dcf1.env.restart(&pv->dcf1_env, legato1); m_lfo1.env.restart(&pv->lfo1_env, legato1); m_dca1.env.restart(&pv->dca1_env, legato1); m_note1[pv->note1] = pv; } } } } } // synth 2 if (on2) { pv = m_note2[key]; if (pv && pv->note2 >= 0) { if (m_ctl2.sustain) pv->sustain2 = true; else if (!pv->sustain2) { if (pv->dca2_env.stage != synthv1_env::Release) { m_dca2.env.note_off(&pv->dca2_env); m_dcf2.env.note_off(&pv->dcf2_env); m_lfo2.env.note_off(&pv->lfo2_env); } m_note2[pv->note2] = nullptr; pv->note2 = -1; // mono legato? if (*m_def2.mono > 0.0f) { do pv = pv->prev(); while (pv && pv->note2 < 0); if (pv && pv->note2 >= 0) { const bool legato2 = (*m_def2.mono > 1.0f); m_dcf2.env.restart(&pv->dcf2_env, legato2); m_lfo2.env.restart(&pv->lfo2_env, legato2); m_dca2.env.restart(&pv->dca2_env, legato2); m_note2[pv->note2] = pv; } } } } } m_midi_in.schedule_note(key, 0); } // key pressure/poly.aftertouch else if (status == 0xa0) { if (!m_key.is_note(key)) continue; const float pre = float(value) / 127.0f; synthv1_voice *pv; // synth 1 if (on1) { pv = m_note1[key]; if (pv && pv->note1 >= 0) pv->pre1 = *m_def1.pressure * pre; } // synth 2 if (on2) { pv = m_note2[key]; if (pv && pv->note2 >= 0) pv->pre2 = *m_def2.pressure * pre; } } // control change else if (status == 0xb0) { switch (key) { case 0x00: // bank-select MSB (cc#0) m_programs.bank_select_msb(value); break; case 0x01: { // modulation wheel (cc#1) const float mod = float(value) / 127.0f; if (on1) m_ctl1.modwheel = *m_def1.modwheel * mod; if (on2) m_ctl2.modwheel = *m_def2.modwheel * mod; break; } case 0x07: { // channel volume (cc#7) const float vol = float(value) / 127.0f; if (on1) m_ctl1.volume = vol; if (on2) m_ctl2.volume = vol; break; } case 0x0a: { // channel panning (cc#10) const float pan = float(value - 64) / 64.0f; if (on1) m_ctl1.panning = pan; if (on2) m_ctl2.panning = pan; break; } case 0x20: // bank-select LSB (cc#32) m_programs.bank_select_lsb(value); break; case 0x40: // sustain/damper pedal (cc#64) if (on1) { if (m_ctl1.sustain && value < 64) allSustainOff_1(); m_ctl1.sustain = bool(value >= 64); } if (on2) { if (m_ctl2.sustain && value < 64) allSustainOff_2(); m_ctl2.sustain = bool(value >= 64); } break; case 0x42: // sustenuto pedal (cc#66) if (on1) { if (value < 64) allSustainOff_1(); else allSustainOn_1(); } if (on2) { if (value < 64) allSustainOff_2(); else allSustainOn_1(); } break; case 0x78: // all sound off (cc#120) allSoundOff(); break; case 0x79: // all controllers off (cc#121) if (on1) allControllersOff_1(); if (on2) allControllersOff_2(); break; case 0x7b: // all notes off (cc#123) if (on1) allNotesOff_1(); if (on2) allNotesOff_2(); break; } // process controllers... m_controls.process_enqueue(channel, key, value); } // pitch bend else if (status == 0xe0) { const float pitchbend = float(key + (value << 7) - 0x2000) / 8192.0f; if (on1) m_ctl1.pitchbend = synthv1_pow2f(*m_def1.pitchbend * pitchbend); if (on2) m_ctl2.pitchbend = synthv1_pow2f(*m_def2.pitchbend * pitchbend); } } // process pending controllers... m_controls.process_dequeue(); // asynchronous event notification... m_midi_in.schedule_event(); } // all sound off void synthv1_impl::allSoundOff (void) { m_chorus.setSampleRate(m_srate); m_chorus.reset(); for (uint16_t k = 0; k < m_nchannels; ++k) { m_phaser[k].setSampleRate(m_srate); m_delay[k].setSampleRate(m_srate); m_comp[k].setSampleRate(m_srate); m_flanger[k].reset(); m_phaser[k].reset(); m_delay[k].reset(); m_comp[k].reset(); } m_reverb.setSampleRate(m_srate); m_reverb.reset(); } // all controllers off void synthv1_impl::allControllersOff (void) { m_ctl1.reset(); m_ctl2.reset(); } void synthv1_impl::allControllersOff_1 (void) { m_ctl1.reset(); } void synthv1_impl::allControllersOff_2 (void) { m_ctl2.reset(); } // all notes off void synthv1_impl::allNotesOff (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note1 >= 0) m_note1[pv->note1] = nullptr; if (pv->note2 >= 0) m_note2[pv->note2] = nullptr; free_voice(pv); pv = m_play_list.next(); } dco1_last1 = 0.0f; dco1_last2 = 0.0f; dco2_last1 = 0.0f; dco2_last2 = 0.0f; m_lfo1.psync = nullptr; m_lfo2.psync = nullptr; m_direct_note = 0; } void synthv1_impl::allNotesOff_1 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note1 >= 0) { m_dca1.env.note_off_fast(&pv->dca1_env); m_dcf1.env.note_off_fast(&pv->dcf1_env); m_lfo1.env.note_off_fast(&pv->lfo1_env); m_note1[pv->note1] = nullptr; pv->note1 = -1; } pv = pv->next(); } dco1_last1 = 0.0f; dco1_last2 = 0.0f; } void synthv1_impl::allNotesOff_2 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note2 >= 0) { m_dca2.env.note_off_fast(&pv->dca2_env); m_dcf2.env.note_off_fast(&pv->dcf2_env); m_lfo2.env.note_off_fast(&pv->lfo2_env); m_note2[pv->note2] = nullptr; pv->note2 = -1; } pv = pv->next(); } dco2_last1 = 0.0f; dco2_last2 = 0.0f; } // all sustained notes off void synthv1_impl::allSustainOff_1 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note1 >= 0 && pv->sustain1) { pv->sustain1 = false; if (pv->dca1_env.stage != synthv1_env::Release) { m_dca1.env.note_off(&pv->dca1_env); m_dcf1.env.note_off(&pv->dcf1_env); m_lfo1.env.note_off(&pv->lfo1_env); m_note1[pv->note1] = nullptr; pv->note1 = -1; } } pv = pv->next(); } } void synthv1_impl::allSustainOff_2 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note2 >= 0 && pv->sustain2) { pv->sustain2 = false; if (pv->dca2_env.stage != synthv1_env::Release) { m_dca2.env.note_off(&pv->dca2_env); m_dcf2.env.note_off(&pv->dcf2_env); m_lfo2.env.note_off(&pv->lfo2_env); m_note2[pv->note2] = nullptr; pv->note2 = -1; } } pv = pv->next(); } } // sustain all notes on (sustenuto) void synthv1_impl::allSustainOn_1 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note1 >= 0 && !pv->sustain1) pv->sustain1 = true; pv = pv->next(); } } void synthv1_impl::allSustainOn_2 (void) { synthv1_voice *pv = m_play_list.next(); while (pv) { if (pv->note2 >= 0 && !pv->sustain2) pv->sustain2 = true; pv = pv->next(); } } // direct note-on triggered on next cycle... void synthv1_impl::directNoteOn ( int note, int vel ) { if (vel > 0 && m_nvoices >= MAX_DIRECT_NOTES) return; const uint32_t i = m_direct_note; if (i < MAX_DIRECT_NOTES) { const int ch1 = int(*m_def1.channel); const int ch2 = int(*m_def2.channel); const int chan = (ch1 > 0 ? ch1 - 1 : (ch2 > 0 ? ch2 - 1 : 0)) & 0x0f; direct_note& data = m_direct_notes[i]; data.status = (vel > 0 ? 0x90 : 0x80) | chan; data.note = note; data.vel = vel; ++m_direct_note; } } // controllers accessor synthv1_controls *synthv1_impl::controls (void) { return &m_controls; } // programs accessor synthv1_programs *synthv1_impl::programs (void) { return &m_programs; } // Micro-tuning support void synthv1_impl::setTuningEnabled ( bool enabled ) { m_tun.enabled = enabled; } bool synthv1_impl::isTuningEnabled (void) const { return m_tun.enabled; } void synthv1_impl::setTuningRefPitch ( float refPitch ) { m_tun.refPitch = refPitch; } float synthv1_impl::tuningRefPitch (void) const { return m_tun.refPitch; } void synthv1_impl::setTuningRefNote ( int refNote ) { m_tun.refNote = refNote; } int synthv1_impl::tuningRefNote (void) const { return m_tun.refNote; } void synthv1_impl::setTuningScaleFile ( const char *pszScaleFile ) { m_tun.scaleFile = QString::fromUtf8(pszScaleFile); } const char *synthv1_impl::tuningScaleFile (void) const { return m_tun.scaleFile.toUtf8().constData(); } void synthv1_impl::setTuningKeyMapFile ( const char *pszKeyMapFile ) { m_tun.keyMapFile = QString::fromUtf8(pszKeyMapFile); } const char *synthv1_impl::tuningKeyMapFile (void) const { return m_tun.keyMapFile.toUtf8().constData(); } void synthv1_impl::resetTuning (void) { if (m_tun.enabled) { // Instance micro-tuning, possibly from Scala keymap and scale files... synthv1_tuning tuning( m_tun.refPitch, m_tun.refNote); if (m_tun.keyMapFile.isEmpty()) if (!m_tun.keyMapFile.isEmpty()) tuning.loadKeyMapFile(m_tun.keyMapFile); if (!m_tun.scaleFile.isEmpty()) tuning.loadScaleFile(m_tun.scaleFile); for (int note = 0; note < MAX_NOTES; ++note) m_freqs[note] = tuning.noteToPitch(note); // Done instance tuning. } else if (m_config.bTuningEnabled) { // Global/config micro-tuning, possibly from Scala keymap and scale files... synthv1_tuning tuning( m_config.fTuningRefPitch, m_config.iTuningRefNote); if (!m_config.sTuningKeyMapFile.isEmpty()) tuning.loadKeyMapFile(m_config.sTuningKeyMapFile); if (!m_config.sTuningScaleFile.isEmpty()) tuning.loadScaleFile(m_config.sTuningScaleFile); for (int note = 0; note < MAX_NOTES; ++note) m_freqs[note] = tuning.noteToPitch(note); // Done global/config tuning. } else { // Native/default tuning, 12-tone equal temperament western standard... for (int note = 0; note < MAX_NOTES; ++note) m_freqs[note] = synthv1_freq(note); // Done native/default tuning. } } // all stabilize void synthv1_impl::stabilize (void) { for (int i = 0; i < synthv1::NUM_PARAMS; ++i) { synthv1_port *pParamPort = paramPort(synthv1::ParamIndex(i)); if (pParamPort) pParamPort->tick(synthv1_port2::NSTEP); } } // all reset clear void synthv1_impl::reset (void) { m_vol1.reset( m_out1.volume.value_ptr(), &m_ctl1.volume); m_pan1.reset( m_out1.panning.value_ptr(), &m_ctl1.panning); m_wid1.reset( m_out1.width.value_ptr()); m_vol2.reset( m_out2.volume.value_ptr(), &m_ctl2.volume); m_pan2.reset( m_out2.panning.value_ptr(), &m_ctl2.panning); m_wid2.reset( m_out2.width.value_ptr()); // flangers if (m_flanger == nullptr) m_flanger = new synthv1_fx_flanger [m_nchannels]; // phasers if (m_phaser == nullptr) m_phaser = new synthv1_fx_phaser [m_nchannels]; // delays if (m_delay == nullptr) m_delay = new synthv1_fx_delay [m_nchannels]; // compressors if (m_comp == nullptr) m_comp = new synthv1_fx_comp [m_nchannels]; // reverbs m_reverb.reset(); // controllers reset. m_controls.reset(); allSoundOff(); // allControllersOff(); allNotesOff(); } // MIDI input asynchronous status notification accessors void synthv1_impl::midiInEnabled ( bool on ) { m_midi_in.enabled(on); } uint32_t synthv1_impl::midiInCount (void) { return m_midi_in.count(); } // synthesize void synthv1_impl::process ( float **ins, float **outs, uint32_t nframes ) { if (!m_running) return; float *v_outs[m_nchannels]; float *v_sfxs[m_nchannels]; // FIXME: fx-send buffer reallocation... seriously? if (m_nsize < nframes) alloc_sfxs(nframes); uint16_t k; for (k = 0; k < m_nchannels; ++k) { ::memset(m_sfxs[k], 0, nframes * sizeof(float)); ::memcpy(outs[k], ins[k], nframes * sizeof(float)); } // process direct note on/off... while (m_direct_note > 0) { const direct_note& data = m_direct_notes[--m_direct_note]; process_midi((uint8_t *) &data, sizeof(data)); } // controls const bool lfo1_enabled = (*m_lfo1.enabled > 0.0f); const bool lfo2_enabled = (*m_lfo2.enabled > 0.0f); const float lfo1_freq = (lfo1_enabled ? get_bpm(*m_lfo1.bpm) / (60.01f - *m_lfo1.rate * 60.0f) : 0.0f); const float lfo2_freq = (lfo2_enabled ? get_bpm(*m_lfo2.bpm) / (60.01f - *m_lfo2.rate * 60.0f) : 0.0f); const float modwheel1 = (lfo1_enabled ? m_ctl1.modwheel + PITCH_SCALE * *m_lfo1.pitch : 0.0f); const float modwheel2 = (lfo2_enabled ? m_ctl2.modwheel + PITCH_SCALE * *m_lfo2.pitch : 0.0f); const bool dcf1_enabled = (*m_dcf1.enabled > 0.0f); const bool dcf2_enabled = (*m_dcf2.enabled > 0.0f); const float fxsend1 = *m_out1.fxsend * *m_out1.fxsend; const float fxsend2 = *m_out2.fxsend * *m_out2.fxsend; if (m_dco1.envtime0 != *m_dco1.envtime) { m_dco1.envtime0 = *m_dco1.envtime; updateEnvTimes_1(); } if (m_dco2.envtime0 != *m_dco2.envtime) { m_dco2.envtime0 = *m_dco2.envtime; updateEnvTimes_2(); } dco1_wave1.reset_test( synthv1_wave::Shape(*m_dco1.shape1), *m_dco1.width1, *m_dco1.bandl1 > 0.0f); dco1_wave2.reset_test( synthv1_wave::Shape(*m_dco1.shape2), *m_dco1.width2, *m_dco1.bandl2 > 0.0f); dco2_wave1.reset_test( synthv1_wave::Shape(*m_dco2.shape1), *m_dco2.width1, *m_dco2.bandl1 > 0.0f); dco2_wave2.reset_test( synthv1_wave::Shape(*m_dco2.shape2), *m_dco2.width2, *m_dco2.bandl2 > 0.0f); if (lfo1_enabled) { lfo1_wave.reset_test( synthv1_wave::Shape(*m_lfo1.shape), *m_lfo1.width); } if (lfo2_enabled) { lfo2_wave.reset_test( synthv1_wave::Shape(*m_lfo2.shape), *m_lfo2.width); } // per voice synthv1_voice *pv = m_play_list.next(); while (pv) { synthv1_voice *pv_next = pv->next(); // output buffers for (k = 0; k < m_nchannels; ++k) { v_outs[k] = outs[k]; v_sfxs[k] = m_sfxs[k]; } uint32_t nblock = nframes; while (nblock > 0) { uint32_t ngen = nblock; // process envelope stages if (pv->dca1_env.running && pv->dca1_env.frames < ngen) ngen = pv->dca1_env.frames; if (pv->dca2_env.running && pv->dca2_env.frames < ngen) ngen = pv->dca2_env.frames; if (pv->dcf1_env.running && pv->dcf1_env.frames < ngen) ngen = pv->dcf1_env.frames; if (pv->dcf2_env.running && pv->dcf2_env.frames < ngen) ngen = pv->dcf2_env.frames; if (pv->lfo1_env.running && pv->lfo1_env.frames < ngen) ngen = pv->lfo1_env.frames; if (pv->lfo2_env.running && pv->lfo2_env.frames < ngen) ngen = pv->lfo2_env.frames; for (uint32_t j = 0; j < ngen; ++j) { // velocities const float vel1 = (pv->vel1 + (1.0f - pv->vel1) * pv->dca1_pre.value(j)); const float vel2 = (pv->vel2 + (1.0f - pv->vel2) * pv->dca2_pre.value(j)); // generators const float lfo1_env = (lfo1_enabled ? pv->lfo1_env.tick() : 0.0f); const float lfo2_env = (lfo1_enabled ? pv->lfo2_env.tick() : 0.0f); const float lfo1 = (lfo1_enabled ? pv->lfo1_sample * lfo1_env : 0.0f); const float lfo2 = (lfo2_enabled ? pv->lfo2_sample * lfo2_env : 0.0f); const float dco11 = pv->dco1_sample1 * pv->dco1_bal.value(j, 0); const float dco12 = pv->dco1_sample2 * pv->dco1_bal.value(j, 1); const float dco21 = pv->dco2_sample1 * pv->dco2_bal.value(j, 0); const float dco22 = pv->dco2_sample2 * pv->dco2_bal.value(j, 1); const float dco1_fmod = (m_ctl1.pitchbend + modwheel1 * lfo1); const float dco2_fmod = (m_ctl2.pitchbend + modwheel2 * lfo2); pv->dco1_sample1 = pv->dco11.sample(pv->dco1_freq1 * dco1_fmod + pv->dco1_glide1.tick()); pv->dco1_sample2 = pv->dco12.sample(pv->dco1_freq2 * dco1_fmod + pv->dco1_glide2.tick()); pv->dco2_sample1 = pv->dco21.sample(pv->dco2_freq1 * dco2_fmod + pv->dco2_glide1.tick()); pv->dco2_sample2 = pv->dco22.sample(pv->dco2_freq2 * dco2_fmod + pv->dco2_glide2.tick()); if (lfo1_enabled) { pv->lfo1_sample = pv->lfo1.sample(lfo1_freq * (1.0f + SWEEP_SCALE * *m_lfo1.sweep * lfo1_env)); } if (lfo2_enabled) { pv->lfo2_sample = pv->lfo2.sample(lfo2_freq * (1.0f + SWEEP_SCALE * *m_lfo2.sweep * lfo2_env)); } // ring modulators const float ringmod1 = synthv1_sigmoid_1( *m_dco1.ringmod * (1.0f + *m_lfo1.ringmod * lfo1)); const float ringmod2 = synthv1_sigmoid_1( *m_dco2.ringmod * (1.0f + *m_lfo2.ringmod * lfo2)); float mod11 = dco11 * (1.0f - ringmod1) + dco11 * dco12 * ringmod1; float mod12 = dco12 * (1.0f - ringmod1) + dco12 * dco11 * ringmod1; float mod21 = dco21 * (1.0f - ringmod2) + dco21 * dco22 * ringmod2; float mod22 = dco22 * (1.0f - ringmod2) + dco22 * dco21 * ringmod2; // filters if (dcf1_enabled) { const float env1 = 0.5f * (1.0f + *m_dcf1.envelope * pv->dcf1_env.tick()); const float cutoff1 = synthv1_sigmoid_1(*m_dcf1.cutoff * env1 * (1.0f + *m_lfo1.cutoff * lfo1)); const float reso1 = synthv1_sigmoid_1(*m_dcf1.reso * env1 * (1.0f + *m_lfo1.reso * lfo1)); switch (int(*m_dcf1.slope)) { case 3: // Formant mod11 = pv->dcf17.output(mod11, cutoff1, reso1); mod12 = pv->dcf18.output(mod12, cutoff1, reso1); break; case 2: // Biquad mod11 = pv->dcf15.output(mod11, cutoff1, reso1); mod12 = pv->dcf16.output(mod12, cutoff1, reso1); break; case 1: // 24db/octave mod11 = pv->dcf13.output(mod11, cutoff1, reso1); mod12 = pv->dcf14.output(mod12, cutoff1, reso1); break; case 0: // 12db/octave default: mod11 = pv->dcf11.output(mod11, cutoff1, reso1); mod12 = pv->dcf12.output(mod12, cutoff1, reso1); break; } } if (dcf2_enabled) { const float env2 = 0.5f * (1.0f + *m_dcf2.envelope * pv->dcf2_env.tick()); const float cutoff2 = synthv1_sigmoid_1(*m_dcf2.cutoff * env2 * (1.0f + *m_lfo2.cutoff * lfo2)); const float reso2 = synthv1_sigmoid_1(*m_dcf2.reso * env2 * (1.0f + *m_lfo2.reso * lfo2)); switch (int(*m_dcf2.slope)) { case 3: // Formant mod21 = pv->dcf27.output(mod21, cutoff2, reso2); mod22 = pv->dcf28.output(mod22, cutoff2, reso2); break; case 2: // Biquad mod21 = pv->dcf25.output(mod21, cutoff2, reso2); mod22 = pv->dcf26.output(mod22, cutoff2, reso2); break; case 1: // 24db/octave mod21 = pv->dcf23.output(mod21, cutoff2, reso2); mod22 = pv->dcf24.output(mod22, cutoff2, reso2); break; case 0: // 12db/octave default: mod21 = pv->dcf21.output(mod21, cutoff2, reso2); mod22 = pv->dcf22.output(mod22, cutoff2, reso2); break; } } // volumes const float wid1 = m_wid1.value(j); const float mid1 = 0.5f * (mod11 + mod12); const float sid1 = 0.5f * (mod11 - mod12); const float vol1 = vel1 * m_vol1.value(j) * m_dca1.volume.value() * pv->dca1_env.tick() * pv->out1_vol.value(j); const float wid2 = m_wid2.value(j); const float mid2 = 0.5f * (mod21 + mod22); const float sid2 = 0.5f * (mod21 - mod22); const float vol2 = vel2 * m_vol2.value(j) * m_dca2.volume.value() * pv->dca2_env.tick() * pv->out2_vol.value(j); // outputs const float out11 = vol1 * (mid1 + sid1 * wid1) * pv->out1_pan.value(j, 0) * m_pan1.value(j, 0); const float out12 = vol1 * (mid1 - sid1 * wid1) * pv->out1_pan.value(j, 1) * m_pan1.value(j, 1); const float out21 = vol2 * (mid2 + sid2 * wid2) * pv->out2_pan.value(j, 0) * m_pan2.value(j, 0); const float out22 = vol2 * (mid2 - sid2 * wid2) * pv->out2_pan.value(j, 1) * m_pan2.value(j, 1); for (k = 0; k < m_nchannels; ++k) { const float dry1 = (k & 1 ? out12 : out11); const float dry2 = (k & 1 ? out22 : out21); const float wet1 = fxsend1 * dry1; const float wet2 = fxsend2 * dry2; const float dry = dry1 + dry2; const float wet = wet1 + wet2; *v_outs[k]++ += dry - wet; *v_sfxs[k]++ += wet; } if (j == 0) { pv->dco1_balance = lfo1 * *m_lfo1.balance; pv->dco2_balance = lfo2 * *m_lfo2.balance; pv->out1_panning = lfo1 * *m_lfo1.panning; pv->out2_panning = lfo2 * *m_lfo2.panning; pv->out1_volume = lfo1 * *m_lfo1.volume + 1.0f; pv->out2_volume = lfo2 * *m_lfo2.volume + 1.0f; } } nblock -= ngen; // voice ramps countdown pv->dco1_bal.process(ngen); pv->dco2_bal.process(ngen); pv->dca1_pre.process(ngen); pv->dca2_pre.process(ngen); pv->out1_pan.process(ngen); pv->out2_pan.process(ngen); pv->out1_vol.process(ngen); pv->out2_vol.process(ngen); // envelope countdowns if (pv->dca1_env.running && pv->dca1_env.frames == 0) m_dca1.env.next(&pv->dca1_env); if (pv->dca2_env.running && pv->dca2_env.frames == 0) m_dca2.env.next(&pv->dca2_env); if (pv->dca1_env.stage == synthv1_env::End && pv->dca2_env.stage == synthv1_env::End) { if (pv->note1 < 0 && pv->note2 < 0) free_voice(pv); nblock = 0; } else { if (pv->dcf1_env.running && pv->dcf1_env.frames == 0) m_dcf1.env.next(&pv->dcf1_env); if (pv->dcf2_env.running && pv->dcf2_env.frames == 0) m_dcf2.env.next(&pv->dcf2_env); if (pv->lfo1_env.running && pv->lfo1_env.frames == 0) m_lfo1.env.next(&pv->lfo1_env); if (pv->lfo2_env.running && pv->lfo2_env.frames == 0) m_lfo2.env.next(&pv->lfo2_env); } } // next playing voice pv = pv_next; } // chorus if (m_nchannels > 1) { m_chorus.process(m_sfxs[0], m_sfxs[1], nframes, *m_cho.wet, *m_cho.delay, *m_cho.feedb, *m_cho.rate, *m_cho.mod); } // effects for (k = 0; k < m_nchannels; ++k) { float *in = m_sfxs[k]; // flanger m_flanger[k].process(in, nframes, *m_fla.wet, *m_fla.delay, *m_fla.feedb, *m_fla.daft * float(k)); // phaser m_phaser[k].process(in, nframes, *m_pha.wet, *m_pha.rate, *m_pha.feedb, *m_pha.depth, *m_pha.daft * float(k)); // delay m_delay[k].process(in, nframes, *m_del.wet, *m_del.delay, *m_del.feedb, get_bpm(*m_del.bpm)); } // reverb if (m_nchannels > 1) { m_reverb.process(m_sfxs[0], m_sfxs[1], nframes, *m_rev.wet, *m_rev.feedb, *m_rev.room, *m_rev.damp, *m_rev.width); } // output mix-down for (k = 0; k < m_nchannels; ++k) { uint32_t n; float *sfx = m_sfxs[k]; // compressor if (int(*m_dyn.compress) > 0) m_comp[k].process(sfx, nframes); // limiter if (int(*m_dyn.limiter) > 0) { float *p = sfx; float *q = sfx; for (n = 0; n < nframes; ++n) *q++ = synthv1_sigmoid(*p++); } // mix-down float *out = outs[k]; for (n = 0; n < nframes; ++n) *out++ += *sfx++; } // post-processing m_dca1.volume.tick(nframes); m_out1.width.tick(nframes); m_out1.panning.tick(nframes); m_out1.volume.tick(nframes); m_wid1.process(nframes); m_pan1.process(nframes); m_vol1.process(nframes); m_dca2.volume.tick(nframes); m_out2.width.tick(nframes); m_out2.panning.tick(nframes); m_out2.volume.tick(nframes); m_wid2.process(nframes); m_pan2.process(nframes); m_vol2.process(nframes); m_controls.process(nframes); } // process running state... bool synthv1_impl::running ( bool on ) { const bool running = m_running; m_running = on; return running; } //------------------------------------------------------------------------- // synthv1 - decl. // synthv1::synthv1 ( uint16_t nchannels, float srate, uint32_t nsize ) { m_pImpl = new synthv1_impl(this, nchannels, srate, nsize); } synthv1::~synthv1 (void) { delete m_pImpl; } void synthv1::setChannels ( uint16_t nchannels ) { m_pImpl->setChannels(nchannels); } uint16_t synthv1::channels (void) const { return m_pImpl->channels(); } void synthv1::setSampleRate ( float srate ) { m_pImpl->setSampleRate(srate); } float synthv1::sampleRate (void) const { return m_pImpl->sampleRate(); } void synthv1::setBufferSize ( uint32_t nsize ) { m_pImpl->setBufferSize(nsize); } uint32_t synthv1::bufferSize (void) const { return m_pImpl->bufferSize(); } void synthv1::setTempo ( float bpm ) { m_pImpl->setTempo(bpm); } float synthv1::tempo (void) const { return m_pImpl->tempo(); } void synthv1::setParamPort ( ParamIndex index, float *pfParam ) { m_pImpl->setParamPort(index, pfParam); } synthv1_port *synthv1::paramPort ( ParamIndex index ) const { return m_pImpl->paramPort(index); } void synthv1::setParamValue ( ParamIndex index, float fValue ) { m_pImpl->setParamValue(index, fValue); } float synthv1::paramValue ( ParamIndex index ) const { return m_pImpl->paramValue(index); } void synthv1::process_midi ( uint8_t *data, uint32_t size ) { #ifdef CONFIG_DEBUG_0 fprintf(stderr, "synthv1[%p]::process_midi(%u)", this, size); for (uint32_t i = 0; i < size; ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, "\n"); #endif m_pImpl->process_midi(data, size); } void synthv1::process ( float **ins, float **outs, uint32_t nframes ) { m_pImpl->process(ins, outs, nframes); } // controllers accessor synthv1_controls *synthv1::controls (void) const { return m_pImpl->controls(); } // programs accessor synthv1_programs *synthv1::programs (void) const { return m_pImpl->programs(); } // process state bool synthv1::running ( bool on ) { return m_pImpl->running(on); } // all stabilize void synthv1::stabilize (void) { m_pImpl->stabilize(); } // all reset clear void synthv1::reset (void) { m_pImpl->reset(); } // MIDI input asynchronous status notification accessors void synthv1::midiInEnabled ( bool on ) { m_pImpl->midiInEnabled(on); } uint32_t synthv1::midiInCount (void) { return m_pImpl->midiInCount(); } // MIDI direct note on/off triggering void synthv1::directNoteOn ( int note, int vel ) { m_pImpl->directNoteOn(note, vel); } // Micro-tuning support void synthv1::setTuningEnabled ( bool enabled ) { m_pImpl->setTuningEnabled(enabled); } bool synthv1::isTuningEnabled (void) const { return m_pImpl->isTuningEnabled(); } void synthv1::setTuningRefPitch ( float refPitch ) { m_pImpl->setTuningRefPitch(refPitch); } float synthv1::tuningRefPitch (void) const { return m_pImpl->tuningRefPitch(); } void synthv1::setTuningRefNote ( int refNote ) { m_pImpl->setTuningRefNote(refNote); } int synthv1::tuningRefNote (void) const { return m_pImpl->tuningRefNote(); } void synthv1::setTuningScaleFile ( const char *pszScaleFile ) { m_pImpl->setTuningScaleFile(pszScaleFile); } const char *synthv1::tuningScaleFile (void) const { return m_pImpl->tuningScaleFile(); } void synthv1::setTuningKeyMapFile ( const char *pszKeyMapFile ) { m_pImpl->setTuningKeyMapFile(pszKeyMapFile); } const char *synthv1::tuningKeyMapFile (void) const { return m_pImpl->tuningKeyMapFile(); } void synthv1::resetTuning (void) { m_pImpl->resetTuning(); } // end of synthv1.cpp synthv1-1.5.1/src/PaxHeaders/synthv1_formant.h0000644000000000000000000000013215270627745016331 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_formant.h0000644000175000001440000001225315270627745016324 0ustar00rncbcusers// synthv1_formant.h // /**************************************************************************** Copyright (C) 2012-2021, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #ifndef __synthv1_formant_h #define __synthv1_formant_h #include #include //--------------------------------------------------------------------- // synthv1_formant - formant parallel filter after Dennis H. Klatt's // Software for a cascade/parallel formant synthesizer // 1979 MIT; 1980 Acoustical Society of America. // class synthv1_formant { public: // constants static const uint32_t NUM_VTABS = 5; static const uint32_t NUM_VOWELS = 5; static const uint32_t NUM_FORMANTS = 5; static const uint32_t NUM_STEPS = 320; // 2-pole filter coeffs. struct Coeffs { float a0, b1, b2; }; // vocal/vowel table struct Vtab { float freq[NUM_FORMANTS]; // frequency [Hz] float gain[NUM_FORMANTS]; // peak gain [dB] float band[NUM_FORMANTS]; // bandwidth [Hz] }; // main impl. class Impl { public: // ctor. Impl(float srate = 44100.0f) : m_srate(srate) { reset_coeffs(); } // sample-rate accessors void setSampleRate(float srate) { m_srate = srate; reset_coeffs(); } float sampleRate() const { return m_srate; } // formant coeffs. accessor const Coeffs& coeffs(uint32_t i) const { return m_ctabs[i]; } // reset coeffs. method void reset_coeffs(float cutoff = 0.5f, float reso = 0.0f); protected: // compute coeffs. for given vocal formant table void vtab_coeffs(Coeffs& coeffs, const Vtab *vtab, uint32_t i, float p); private: // instance members float m_srate; // filter coeffs. Coeffs m_ctabs[NUM_FORMANTS]; }; // ctor. synthv1_formant(Impl *pImpl = 0) : m_pImpl(pImpl), m_cutoff(0.5f), m_reso(0.0f), m_nstep(0) { reset_coeffs(); } // reset impl. void reset(Impl *pImpl) { m_pImpl = pImpl; reset_coeffs(); } void reset_filters(float cutoff, float reso) { for (uint32_t i = 0; i < NUM_FORMANTS; ++i) m_filters[i].reset(); update(cutoff, reso); } // output tick float output(float in, float cutoff, float reso) { update(cutoff, reso); float out = 0.0f; for (uint32_t i = 0; i < NUM_FORMANTS; ++i) out += m_filters[i].output(in); return out; } // process block void process(float *in, uint32_t nframes, float wet, float cutoff, float reso) { for (uint32_t i = 0; i < nframes; ++i) { const float out = output(in[i], cutoff, reso); in[i] *= (1.0f - wet); in[i] += (wet * out); } } protected: // step-wise smoothed coeff. class Coeff { public: Coeff() { reset(); } void reset() { m_value = m_vstep = 0.0f; m_nstep = 0; } void set_value(float value) { m_nstep = NUM_STEPS; m_vstep = (value - m_value) / float(m_nstep); } float value() const { return m_value + m_vstep * float(m_nstep); } float tick() { if (m_nstep > 0) { m_value += m_vstep; --m_nstep; } return m_value; } private: float m_value; float m_vstep; uint32_t m_nstep; }; // 2-pole resonator filter class Filter { public: Filter() { reset(); } void reset() { m_a0.reset(); m_b1.reset(); m_b2.reset(); m_out1 = m_out2 = 0.0f; } void reset_coeffs(const Coeffs& coeffs) { m_a0.set_value(coeffs.a0); m_b1.set_value(coeffs.b1); m_b2.set_value(coeffs.b2); } float output(float in) { const float out = m_a0.tick() * in + m_b1.tick() * m_out1 - m_b2.tick() * m_out2; m_out2 = m_out1; m_out1 = out; return out; } private: Coeff m_a0, m_b1, m_b2; float m_out1, m_out2; }; // update method void update(float cutoff, float reso) { if (m_nstep > 0) --m_nstep; else if (::fabsf(m_cutoff - cutoff) > 0.001f || ::fabsf(m_reso - reso) > 0.001f) { m_nstep = NUM_STEPS; m_cutoff = cutoff; m_reso = reso; reset_coeffs(); } } // reset coeffs. method void reset_coeffs(); private: // instance members Impl *m_pImpl; // parameters. float m_cutoff; float m_reso; // slew-rate control. uint32_t m_nstep; // formant filters Filter m_filters[NUM_FORMANTS]; // base vocal tables static Vtab g_bass_vtab[NUM_VOWELS]; static Vtab g_tenor_vtab[NUM_VOWELS]; static Vtab g_countertenor_vtab[NUM_VOWELS]; static Vtab g_soprano_vtab[NUM_VOWELS]; static Vtab g_alto_vtab[NUM_VOWELS]; static Vtab *g_vtabs[NUM_VTABS]; }; #endif // __synthv1_formant_h // end of synthv1_formant.h synthv1-1.5.1/src/PaxHeaders/synthv1_list.h0000644000000000000000000000013215270627745015636 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_list.h0000644000175000001440000000330515270627745015627 0ustar00rncbcusers// synthv1_list.h // /**************************************************************************** Copyright (C) 2012, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #ifndef __synthv1_list_h #define __synthv1_list_h //------------------------------------------------------------------------- // synthv1_list - generic double-linked list node. template class synthv1_list { public: synthv1_list() : m_prev(0), m_next(0) {} void append(T *p) { p->m_prev = m_prev; p->m_next = 0; if (m_prev) m_prev->m_next = p; else m_next = p; m_prev = p; } void remove(T *p) { if (p->m_prev) p->m_prev->m_next = p->m_next; else m_next = p->m_next; if (p->m_next) p->m_next->m_prev = p->m_prev; else m_prev = p->m_prev; } T *prev() const { return m_prev; } T *next() const { return m_next; } private: T *m_prev; T *m_next; }; #endif // __synthv1_list_h // end of synthv1_list.h synthv1-1.5.1/src/PaxHeaders/synthv1widget_palette.cpp0000644000000000000000000000013215270627745020060 xustar0030 mtime=1793273829.975582031 30 atime=1793273829.975582031 30 ctime=1793273829.975582031 synthv1-1.5.1/src/synthv1widget_palette.cpp0000644000175000001440000010132715270627745020054 0ustar00rncbcusers// synthv1widget_palette.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1widget_palette.h" #include "ui_synthv1widget_palette.h" #include #include #include #include #include #include #include #include #include #include #include // Local static consts. static const char *ColorThemesGroup = "/ColorThemes/"; static const char *PaletteEditorGroup = "/PaletteEditor/"; static const char *DefaultDirKey = "DefaultDir"; static const char *ShowDetailsKey = "ShowDetails"; static const char *DefaultSuffix = "conf"; static struct { const char *key; QPalette::ColorRole value; } g_colorRoles[] = { { "Window", QPalette::Window }, { "WindowText", QPalette::WindowText }, { "Button", QPalette::Button }, { "ButtonText", QPalette::ButtonText }, { "Light", QPalette::Light }, { "Midlight", QPalette::Midlight }, { "Dark", QPalette::Dark }, { "Mid", QPalette::Mid }, { "Text", QPalette::Text }, { "BrightText", QPalette::BrightText }, { "Base", QPalette::Base }, { "AlternateBase", QPalette::AlternateBase }, { "Shadow", QPalette::Shadow }, { "Highlight", QPalette::Highlight }, { "HighlightedText", QPalette::HighlightedText }, { "Link", QPalette::Link }, { "LinkVisited", QPalette::LinkVisited }, { "ToolTipBase", QPalette::ToolTipBase }, { "ToolTipText", QPalette::ToolTipText }, #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) { "PlaceholderText", QPalette::PlaceholderText }, #endif { "NoRole", QPalette::NoRole }, { nullptr, QPalette::NoRole } }; //------------------------------------------------------------------------- // synthv1widget_palette synthv1widget_palette::synthv1widget_palette ( QWidget *parent, const QPalette& pal ) : QDialog(parent), p_ui(new Ui::synthv1widget_palette), m_ui(*p_ui) { m_ui.setupUi(this); m_settings = nullptr; m_owner = false; m_modelUpdated = false; m_paletteUpdated = false; m_dirtyCount = 0; m_dirtyTotal = 0; updateGenerateButton(); m_paletteModel = new PaletteModel(this); m_ui.paletteView->setModel(m_paletteModel); ColorDelegate *delegate = new ColorDelegate(this); m_ui.paletteView->setItemDelegate(delegate); m_ui.paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers); // m_ui.paletteView->setAlternatingRowColors(true); m_ui.paletteView->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui.paletteView->setDragEnabled(true); m_ui.paletteView->setDropIndicatorShown(true); m_ui.paletteView->setRootIsDecorated(false); m_ui.paletteView->setColumnHidden(2, true); m_ui.paletteView->setColumnHidden(3, true); QObject::connect(m_ui.nameCombo, SIGNAL(editTextChanged(const QString&)), SLOT(nameComboChanged(const QString&))); QObject::connect(m_ui.saveButton, SIGNAL(clicked()), SLOT(saveButtonClicked())); QObject::connect(m_ui.deleteButton, SIGNAL(clicked()), SLOT(deleteButtonClicked())); QObject::connect(m_ui.generateButton, SIGNAL(changed()), SLOT(generateButtonChanged())); QObject::connect(m_ui.resetButton, SIGNAL(clicked()), SLOT(resetButtonClicked())); QObject::connect(m_ui.detailsCheck, SIGNAL(clicked()), SLOT(detailsCheckClicked())); QObject::connect(m_ui.importButton, SIGNAL(clicked()), SLOT(importButtonClicked())); QObject::connect(m_ui.exportButton, SIGNAL(clicked()), SLOT(exportButtonClicked())); QObject::connect(m_paletteModel, SIGNAL(paletteChanged(const QPalette&)), SLOT(paletteChanged(const QPalette&))); QObject::connect(m_ui.dialogButtons, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.dialogButtons, SIGNAL(rejected()), SLOT(reject())); setPalette(pal, pal); QDialog::adjustSize(); } synthv1widget_palette::~synthv1widget_palette (void) { setSettings(nullptr); delete p_ui; } void synthv1widget_palette::setPalette ( const QPalette& pal ) { m_palette = pal; #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const uint mask = pal.resolveMask(); #else const uint mask = pal.resolve(); #endif for (int i = 0; g_colorRoles[i].key; ++i) { if ((mask & (1 << i)) == 0) { const QPalette::ColorRole cr = g_colorRoles[i].value; m_palette.setBrush(QPalette::Active, cr, m_parentPalette.brush(QPalette::Active, cr)); m_palette.setBrush(QPalette::Inactive, cr, m_parentPalette.brush(QPalette::Inactive, cr)); m_palette.setBrush(QPalette::Disabled, cr, m_parentPalette.brush(QPalette::Disabled, cr)); } } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_palette.setResolveMask(mask); #else m_palette.resolve(mask); #endif updateGenerateButton(); m_paletteUpdated = true; if (!m_modelUpdated) m_paletteModel->setPalette(m_palette, m_parentPalette); m_paletteUpdated = false; } void synthv1widget_palette::setPalette ( const QPalette& pal, const QPalette& parentPal ) { m_parentPalette = parentPal; setPalette(pal); } const QPalette& synthv1widget_palette::palette (void) const { return m_palette; } void synthv1widget_palette::setSettings ( QSettings *settings, bool owner ) { if (m_settings && m_owner) delete m_settings; m_settings = settings; m_owner = owner; m_ui.detailsCheck->setChecked(isShowDetails()); updateNamedPaletteList(); updateDialogButtons(); } QSettings *synthv1widget_palette::settings (void) const { return m_settings; } void synthv1widget_palette::nameComboChanged ( const QString& name ) { if (m_dirtyCount > 0 && m_ui.nameCombo->findText(name) < 0) { updateDialogButtons(); } else { resetButtonClicked(); setPaletteName(name); ++m_dirtyTotal; } } void synthv1widget_palette::saveButtonClicked (void) { const QString& name = m_ui.nameCombo->currentText(); if (name.isEmpty()) return; QString filename = namedPaletteConf(name); if (filename.isEmpty() || !QFileInfo(filename).isWritable()) { const QString& title = tr("Save Palette - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); QString dirname = defaultDir(); if (!dirname.isEmpty()) dirname.append(QDir::separator()); dirname.append(paletteName() + '.' + DefaultSuffix); filename = QFileDialog::getSaveFileName(this, title, dirname, filters.join(";;")); } if (!filename.isEmpty() && saveNamedPaletteConf(name, filename, m_palette)) { addNamedPaletteConf(name, filename); setPalette(m_palette, m_palette); updateNamedPaletteList(); resetButtonClicked(); } } void synthv1widget_palette::deleteButtonClicked (void) { const QString& name = m_ui.nameCombo->currentText(); if (m_ui.nameCombo->findText(name) >= 0) { deleteNamedPaletteConf(name); updateNamedPaletteList(); updateDialogButtons(); } } void synthv1widget_palette::generateButtonChanged (void) { const QColor& color = m_ui.generateButton->brush().color(); const QPalette& pal = QPalette(color); setPalette(pal); ++m_dirtyCount; updateDialogButtons(); } void synthv1widget_palette::resetButtonClicked (void) { const bool blocked = blockSignals(true); for (int i = 0; g_colorRoles[i].key; ++i) { const QPalette::ColorRole cr = g_colorRoles[i].value; const QModelIndex& index = m_paletteModel->index(cr, 0); m_paletteModel->setData(index, false, Qt::EditRole); } m_dirtyCount = 0; updateDialogButtons(); blockSignals(blocked); } void synthv1widget_palette::detailsCheckClicked (void) { const int cw = (m_ui.paletteView->viewport()->width() >> 2); QHeaderView *header = m_ui.paletteView->header(); header->resizeSection(0, cw); if (m_ui.detailsCheck->isChecked()) { m_ui.paletteView->setColumnHidden(2, false); m_ui.paletteView->setColumnHidden(3, false); header->resizeSection(1, cw); header->resizeSection(2, cw); header->resizeSection(3, cw); m_paletteModel->setGenerate(false); } else { m_ui.paletteView->setColumnHidden(2, true); m_ui.paletteView->setColumnHidden(3, true); header->resizeSection(1, cw * 3); m_paletteModel->setGenerate(true); } } void synthv1widget_palette::importButtonClicked (void) { const QString& title = tr("Import File - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); const QString& filename = QFileDialog::getOpenFileName(this, title, defaultDir(), filters.join(";;")); if (filename.isEmpty()) return; QSettings conf(filename, QSettings::IniFormat); conf.beginGroup(ColorThemesGroup); const QStringList names = conf.childGroups(); conf.endGroup(); int imported = 0; QStringListIterator name_iter(names); while (name_iter.hasNext()) { const QString& name = name_iter.next(); if (!name.isEmpty()) { addNamedPaletteConf(name, filename); setPaletteName(name); ++imported; } } if (imported > 0) { updateNamedPaletteList(); resetButtonClicked(); setDefaultDir(QFileInfo(filename).absolutePath()); } else { QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Could not import from file:\n\n" "%1\n\nSorry.").arg(filename)); } } void synthv1widget_palette::exportButtonClicked (void) { const QString& title = tr("Export File - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); QString dirname = defaultDir(); if (!dirname.isEmpty()) dirname.append(QDir::separator()); dirname.append(paletteName() + '.' + DefaultSuffix); const QString& filename = QFileDialog::getSaveFileName(this, title, dirname, filters.join(";;")); if (filename.isEmpty()) return; const QFileInfo fi(filename); const QString& name = fi.baseName(); if (saveNamedPaletteConf(name, filename, m_palette)) { // addNamedPaletteConf(name, filename); setDefaultDir(fi.absolutePath()); } } void synthv1widget_palette::paletteChanged ( const QPalette& pal ) { m_modelUpdated = true; if (!m_paletteUpdated) setPalette(pal); m_modelUpdated = false; ++m_dirtyCount; updateDialogButtons(); } void synthv1widget_palette::setPaletteName ( const QString& name ) { const bool blocked = m_ui.nameCombo->blockSignals(true); m_ui.nameCombo->setEditText(name); QPalette pal; if (namedPalette(m_settings, name, pal, true)) setPalette(pal, pal); m_dirtyCount = 0; updateDialogButtons(); m_ui.nameCombo->blockSignals(blocked); } QString synthv1widget_palette::paletteName (void) const { return m_ui.nameCombo->currentText(); } void synthv1widget_palette::updateNamedPaletteList (void) { const bool blocked = m_ui.nameCombo->blockSignals(true); const QString old_name = m_ui.nameCombo->currentText(); m_ui.nameCombo->clear(); m_ui.nameCombo->insertItems(0, namedPaletteList()); // m_ui.nameCombo->model()->sort(0); const int i = m_ui.nameCombo->findText(old_name); if (i >= 0) m_ui.nameCombo->setCurrentIndex(i); else m_ui.nameCombo->setEditText(old_name); m_ui.nameCombo->blockSignals(blocked); } void synthv1widget_palette::updateGenerateButton (void) { m_ui.generateButton->setBrush( m_palette.brush(QPalette::Active, QPalette::Button)); } void synthv1widget_palette::updateDialogButtons (void) { const QString& name = m_ui.nameCombo->currentText(); const QString& filename = namedPaletteConf(name); const int i = m_ui.nameCombo->findText(name); m_ui.saveButton->setEnabled(!name.isEmpty() && (m_dirtyCount > 0 || i < 0)); m_ui.deleteButton->setEnabled(i >= 0); m_ui.resetButton->setEnabled(m_dirtyCount > 0); m_ui.exportButton->setEnabled(!name.isEmpty() || i >= 0); m_ui.dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(i >= 0); } bool synthv1widget_palette::namedPalette ( const QString& name, QPalette& pal ) const { return namedPalette(m_settings, name, pal); } bool synthv1widget_palette::namedPalette ( QSettings *settings, const QString& name, QPalette& pal, bool fixup ) { int result = 0; if (!name.isEmpty() && loadNamedPalette(settings, name, pal)) { ++result; } else { const QString& filename = namedPaletteConf(settings, name); if (!filename.isEmpty() && QFileInfo(filename).isReadable() && loadNamedPaletteConf(name, filename, pal)) { ++result; } } // Dark themes grayed/disabled color group fix... if (!fixup && pal.base().color().value() < 0x7f) { const QColor& color = pal.window().color(); const int groups = int(QPalette::Active | QPalette::Inactive) + 1; for (int i = 0; i < groups; ++i) { const QPalette::ColorGroup cg = QPalette::ColorGroup(i); pal.setBrush(cg, QPalette::Light, color.lighter(140)); pal.setBrush(cg, QPalette::Midlight, color.lighter(100)); pal.setBrush(cg, QPalette::Mid, color.lighter(90)); pal.setBrush(cg, QPalette::Dark, color.darker(160)); pal.setBrush(cg, QPalette::Shadow, color.darker(180)); } pal.setColorGroup(QPalette::Disabled, pal.windowText().color().darker(), pal.button(), pal.light(), pal.dark(), pal.mid(), pal.text().color().darker(), pal.text().color().lighter(), pal.base(), pal.window()); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) pal.setColor(QPalette::Disabled, QPalette::Highlight, pal.mid().color()); pal.setColor(QPalette::Disabled, QPalette::ButtonText, pal.mid().color()); #endif ++result; } return (result > 0); } QStringList synthv1widget_palette::namedPaletteList (void) const { return namedPaletteList(m_settings); } QStringList synthv1widget_palette::namedPaletteList ( QSettings *settings ) { QStringList list; if (settings) { settings->beginGroup(ColorThemesGroup); list.append(settings->childKeys()); list.append(settings->childGroups()); // legacy... settings->endGroup(); } return list; } QString synthv1widget_palette::namedPaletteConf ( const QString& name ) const { return namedPaletteConf(m_settings, name); } QString synthv1widget_palette::namedPaletteConf ( QSettings *settings, const QString& name ) { QString ret; if (settings && !name.isEmpty()) { settings->beginGroup(ColorThemesGroup); ret = settings->value(name).toString(); settings->endGroup(); } return ret; } void synthv1widget_palette::addNamedPaletteConf ( const QString& name, const QString& filename ) { addNamedPaletteConf(m_settings, name, filename); ++m_dirtyTotal; } void synthv1widget_palette::addNamedPaletteConf ( QSettings *settings, const QString& name, const QString& filename ) { if (settings) { settings->beginGroup(ColorThemesGroup); settings->remove(name); // remove legacy keys! settings->setValue(name, filename); settings->endGroup(); } } void synthv1widget_palette::deleteNamedPaletteConf ( const QString& name ) { if (m_settings) { m_settings->beginGroup(ColorThemesGroup); m_settings->remove(name); m_settings->endGroup(); ++m_dirtyTotal; } } bool synthv1widget_palette::loadNamedPaletteConf ( const QString& name, const QString& filename, QPalette& pal ) { QSettings conf(filename, QSettings::IniFormat); return loadNamedPalette(&conf, name, pal); } bool synthv1widget_palette::saveNamedPaletteConf ( const QString& name, const QString& filename, const QPalette& pal ) { QSettings conf(filename, QSettings::IniFormat); return saveNamedPalette(&conf, name, pal); } bool synthv1widget_palette::loadNamedPalette ( QSettings *settings, const QString& name, QPalette& pal ) { if (settings == nullptr) return false; int result = 0; settings->beginGroup(ColorThemesGroup); QStringListIterator name_iter(settings->childGroups()); while (name_iter.hasNext() && !result) { const QString& name2 = name_iter.next(); if (name2 == name) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) uint mask = pal.resolve(); #endif settings->beginGroup(name + '/'); QStringListIterator iter(settings->childKeys()); while (iter.hasNext()) { const QString& key = iter.next(); const QPalette::ColorRole cr = synthv1widget_palette::colorRole(key); const QStringList& clist = settings->value(key).toStringList(); if (clist.count() == 3) { pal.setColor(QPalette::Active, cr, QColor(clist.at(0))); pal.setColor(QPalette::Inactive, cr, QColor(clist.at(1))); pal.setColor(QPalette::Disabled, cr, QColor(clist.at(2))); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) mask &= ~(1 << int(cr)); #endif ++result; } } #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) pal.resolve(mask); #endif settings->endGroup(); } } settings->endGroup(); return (result > 0); } bool synthv1widget_palette::saveNamedPalette ( QSettings *settings, const QString& name, const QPalette& pal ) { if (settings == nullptr) return false; settings->beginGroup(ColorThemesGroup); settings->beginGroup(name + '/'); for (int i = 0; g_colorRoles[i].key; ++i) { const QString& key = QString::fromLatin1(g_colorRoles[i].key); const QPalette::ColorRole cr = g_colorRoles[i].value; QStringList clist; clist.append(pal.color(QPalette::Active, cr).name()); clist.append(pal.color(QPalette::Inactive, cr).name()); clist.append(pal.color(QPalette::Disabled, cr).name()); settings->setValue(key, clist); } settings->endGroup(); settings->endGroup(); return true; } QPalette::ColorRole synthv1widget_palette::colorRole ( const QString& name ) { static QHash s_colorRoles; if (s_colorRoles.isEmpty()) { for (int i = 0; g_colorRoles[i].key; ++i) { const QString& key = QString::fromLatin1(g_colorRoles[i].key); const QPalette::ColorRole value = g_colorRoles[i].value; s_colorRoles.insert(key, value); } } return s_colorRoles.value(name, QPalette::NoRole); } bool synthv1widget_palette::isDirty (void) const { return (m_dirtyTotal > 0); } void synthv1widget_palette::accept (void) { setShowDetails(m_ui.detailsCheck->isChecked()); if (m_dirtyCount > 0) saveButtonClicked(); QDialog::accept(); } void synthv1widget_palette::reject (void) { if (m_dirtyCount > 0) { const QString& name = paletteName(); if (name.isEmpty()) { if (QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Some settings have been changed.\n\n" "Do you want to discard the changes?"), QMessageBox::Discard | QMessageBox::Cancel) == QMessageBox::Cancel) return; } else { switch (QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Some settings have been changed:\n\n" "\"%1\".\n\nDo you want to save the changes?") .arg(name), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Save: saveButtonClicked(); // Fall thru... case QMessageBox::Discard: break; default: // Cancel... return; } } } QDialog::reject(); } void synthv1widget_palette::setDefaultDir ( const QString& dir ) { if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); m_settings->setValue(DefaultDirKey, dir); m_settings->endGroup(); } } QString synthv1widget_palette::defaultDir (void) const { QString dir; if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); dir = m_settings->value(DefaultDirKey).toString(); m_settings->endGroup(); } return dir; } void synthv1widget_palette::setShowDetails ( bool on ) { if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); m_settings->setValue(ShowDetailsKey, on); m_settings->endGroup(); } } bool synthv1widget_palette::isShowDetails (void) const { bool on = false; if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); on = m_settings->value(ShowDetailsKey).toBool(); m_settings->endGroup(); } return on; } void synthv1widget_palette::showEvent ( QShowEvent *event ) { QDialog::showEvent(event); detailsCheckClicked(); } void synthv1widget_palette::resizeEvent ( QResizeEvent *event ) { QDialog::resizeEvent(event); detailsCheckClicked(); } //------------------------------------------------------------------------- // synthv1widget_palette::PaletteModel synthv1widget_palette::PaletteModel::PaletteModel ( QObject *parent ) : QAbstractTableModel(parent) { for (m_nrows = 0; g_colorRoles[m_nrows].key; ++m_nrows) { const QPalette::ColorRole value = g_colorRoles[m_nrows].value; const QString& key = QString::fromLatin1(g_colorRoles[m_nrows].key); m_roleNames.insert(value, key); } m_generate = true; } int synthv1widget_palette::PaletteModel::rowCount ( const QModelIndex& ) const { return m_nrows; } int synthv1widget_palette::PaletteModel::columnCount ( const QModelIndex& ) const { return 4; } QVariant synthv1widget_palette::PaletteModel::data ( const QModelIndex& index, int role ) const { if (!index.isValid()) return QVariant(); if (index.row() < 0 || index.row() >= m_nrows) return QVariant(); if (index.column() < 0 || index.column() >= 4) return QVariant(); if (index.column() == 0) { if (role == Qt::DisplayRole) return m_roleNames.value(QPalette::ColorRole(index.row())); if (role == Qt::EditRole) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const uint mask = m_palette.resolveMask(); #else const uint mask = m_palette.resolve(); #endif return bool(mask & (1 << index.row())); } } else if (role == Qt::BackgroundRole) { return m_palette.color( columnToGroup(index.column()), QPalette::ColorRole(index.row())); } return QVariant(); } bool synthv1widget_palette::PaletteModel::setData ( const QModelIndex& index, const QVariant& value, int role ) { if (!index.isValid()) return false; if (index.column() != 0 && role == Qt::BackgroundRole) { const QColor& color = value.value(); const QPalette::ColorRole cr = QPalette::ColorRole(index.row()); const QPalette::ColorGroup cg = columnToGroup(index.column()); m_palette.setBrush(cg, cr, color); QModelIndex index_begin = PaletteModel::index(cr, 0); QModelIndex index_end = PaletteModel::index(cr, 3); if (m_generate) { m_palette.setBrush(QPalette::Inactive, cr, color); switch (cr) { case QPalette::WindowText: case QPalette::Text: case QPalette::ButtonText: case QPalette::Base: break; case QPalette::Dark: m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, color); m_palette.setBrush(QPalette::Disabled, QPalette::Dark, color); m_palette.setBrush(QPalette::Disabled, QPalette::Text, color); m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, color); index_begin = PaletteModel::index(0, 0); index_end = PaletteModel::index(m_nrows - 1, 3); break; case QPalette::Window: m_palette.setBrush(QPalette::Disabled, QPalette::Base, color); m_palette.setBrush(QPalette::Disabled, QPalette::Window, color); index_begin = PaletteModel::index(QPalette::Base, 0); break; case QPalette::Highlight: m_palette.setBrush(QPalette::Disabled, QPalette::Highlight, color.darker(120)); break; default: m_palette.setBrush(QPalette::Disabled, cr, color); break; } } emit paletteChanged(m_palette); emit dataChanged(index_begin, index_end); return true; } if (index.column() == 0 && role == Qt::EditRole) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) uint mask = m_palette.resolveMask(); #else uint mask = m_palette.resolve(); #endif const bool masked = value.value(); const int i = index.row(); if (masked) { mask |= (1 << i); } else { const QPalette::ColorRole cr = QPalette::ColorRole(i); m_palette.setBrush(QPalette::Active, cr, m_parentPalette.brush(QPalette::Active, cr)); m_palette.setBrush(QPalette::Inactive, cr, m_parentPalette.brush(QPalette::Inactive, cr)); m_palette.setBrush(QPalette::Disabled, cr, m_parentPalette.brush(QPalette::Disabled, cr)); mask &= ~(1 << i); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_palette.setResolveMask(mask); #else m_palette.resolve(mask); #endif emit paletteChanged(m_palette); const QModelIndex& index_end = PaletteModel::index(i, 3); emit dataChanged(index, index_end); return true; } return false; } Qt::ItemFlags synthv1widget_palette::PaletteModel::flags ( const QModelIndex& index ) const { if (!index.isValid()) return Qt::ItemIsEnabled; else return Qt::ItemIsEditable | Qt::ItemIsEnabled; } QVariant synthv1widget_palette::PaletteModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Color Role"); else if (section == groupToColumn(QPalette::Active)) return tr("Active"); else if (section == groupToColumn(QPalette::Inactive)) return tr("Inactive"); else if (section == groupToColumn(QPalette::Disabled)) return tr("Disabled"); } return QVariant(); } const QPalette& synthv1widget_palette::PaletteModel::palette(void) const { return m_palette; } void synthv1widget_palette::PaletteModel::setPalette ( const QPalette& palette, const QPalette& parentPalette ) { m_palette = palette; m_parentPalette = parentPalette; const QModelIndex& index_begin = index(0, 0); const QModelIndex& index_end = index(m_nrows - 1, 3); emit dataChanged(index_begin, index_end); } QPalette::ColorGroup synthv1widget_palette::PaletteModel::columnToGroup ( int index ) const { if (index == 1) return QPalette::Active; else if (index == 2) return QPalette::Inactive; return QPalette::Disabled; } int synthv1widget_palette::PaletteModel::groupToColumn ( QPalette::ColorGroup group ) const { if (group == QPalette::Active) return 1; else if (group == QPalette::Inactive) return 2; return 3; } //------------------------------------------------------------------------- // synthv1widget_palette::ColorDelegate QWidget *synthv1widget_palette::ColorDelegate::createEditor ( QWidget *parent, const QStyleOptionViewItem&, const QModelIndex& index ) const { QWidget *editor = nullptr; if (index.column() == 0) { RoleEditor *ed = new RoleEditor(parent); QObject::connect(ed, SIGNAL(changed(QWidget *)), SIGNAL(commitData(QWidget *))); // ed->setFocusPolicy(Qt::NoFocus); // ed->installEventFilter(const_cast(this)); editor = ed; } else { ColorEditor *ed = new ColorEditor(parent); QObject::connect(ed, SIGNAL(changed(QWidget *)), SIGNAL(commitData(QWidget *))); ed->setFocusPolicy(Qt::NoFocus); ed->installEventFilter(const_cast(this)); editor = ed; } return editor; } void synthv1widget_palette::ColorDelegate::setEditorData ( QWidget *editor, const QModelIndex& index ) const { if (index.column() == 0) { const bool masked = index.model()->data(index, Qt::EditRole).value(); RoleEditor *ed = static_cast(editor); ed->setEdited(masked); const QString& colorName = index.model()->data(index, Qt::DisplayRole).value(); ed->setLabel(colorName); } else { const QColor& color = index.model()->data(index, Qt::BackgroundRole).value(); ColorEditor *ed = static_cast(editor); ed->setColor(color); } } void synthv1widget_palette::ColorDelegate::setModelData ( QWidget *editor, QAbstractItemModel *model, const QModelIndex& index ) const { if (index.column() == 0) { RoleEditor *ed = static_cast(editor); const bool masked = ed->edited(); model->setData(index, masked, Qt::EditRole); } else { ColorEditor *ed = static_cast(editor); if (ed->changed()) { const QColor& color = ed->color(); model->setData(index, color, Qt::BackgroundRole); } } } void synthv1widget_palette::ColorDelegate::updateEditorGeometry ( QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QItemDelegate::updateEditorGeometry(editor, option, index); editor->setGeometry(editor->geometry().adjusted(0, 0, -1, -1)); } void synthv1widget_palette::ColorDelegate::paint ( QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem opt = option; const bool masked = index.model()->data(index, Qt::EditRole).value(); if (index.column() == 0 && masked) opt.font.setBold(true); QItemDelegate::paint(painter, opt, index); // painter->setPen(opt.palette.midlight().color()); painter->setPen(Qt::darkGray); painter->drawLine(opt.rect.right(), opt.rect.y(), opt.rect.right(), opt.rect.bottom()); painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); } QSize synthv1widget_palette::ColorDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex &index) const { return QItemDelegate::sizeHint(option, index) + QSize(4, 4); } //------------------------------------------------------------------------- // synthv1widget_palette::ColorButton synthv1widget_palette::ColorButton::ColorButton ( QWidget *parent ) : QPushButton(parent), m_brush(Qt::darkGray) { QPushButton::setMinimumWidth(48); QObject::connect(this, SIGNAL(clicked()), SLOT(chooseColor())); } const QBrush& synthv1widget_palette::ColorButton::brush (void) const { return m_brush; } void synthv1widget_palette::ColorButton::setBrush ( const QBrush& brush ) { m_brush = brush; update(); } void synthv1widget_palette::ColorButton::paintEvent ( QPaintEvent *event ) { QPushButton::paintEvent(event); QStyleOptionButton opt; opt.initFrom(this); const QRect& rect = style()->subElementRect(QStyle::SE_PushButtonContents, &opt, this); QPainter paint(this); paint.setBrush(QBrush(m_brush.color())); paint.drawRect(rect.adjusted(+1, +1, -2, -2)); } void synthv1widget_palette::ColorButton::chooseColor (void) { const QColor color = QColorDialog::getColor(m_brush.color(), this); if (color.isValid()) { m_brush.setColor(color); emit changed(); } } //------------------------------------------------------------------------- // synthv1widget_palette::ColorEditor synthv1widget_palette::ColorEditor::ColorEditor ( QWidget *parent ) : QWidget(parent) { QLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); m_button = new synthv1widget_palette::ColorButton(this); layout->addWidget(m_button); QObject::connect(m_button, SIGNAL(changed()), SLOT(colorChanged())); setFocusProxy(m_button); m_changed = false; } void synthv1widget_palette::ColorEditor::setColor ( const QColor& color ) { m_button->setBrush(color); m_changed = false; } QColor synthv1widget_palette::ColorEditor::color (void) const { return m_button->brush().color(); } void synthv1widget_palette::ColorEditor::colorChanged (void) { m_changed = true; emit changed(this); } bool synthv1widget_palette::ColorEditor::changed (void) const { return m_changed; } //------------------------------------------------------------------------- // synthv1widget_palette::RoleEditor synthv1widget_palette::RoleEditor::RoleEditor ( QWidget *parent ) : QWidget(parent) { m_edited = false; QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); m_label = new QLabel(this); layout->addWidget(m_label); m_label->setAutoFillBackground(true); m_label->setIndent(3); // HACK: it should have the same value of textMargin in QItemDelegate setFocusProxy(m_label); m_button = new QToolButton(this); m_button->setToolButtonStyle(Qt::ToolButtonIconOnly); m_button->setIcon(QPixmap(":/images/itemReset.png")); m_button->setIconSize(QSize(8, 8)); m_button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); layout->addWidget(m_button); QObject::connect(m_button, SIGNAL(clicked()), SLOT(resetProperty())); } void synthv1widget_palette::RoleEditor::setLabel ( const QString& label ) { m_label->setText(label); } void synthv1widget_palette::RoleEditor::setEdited ( bool on ) { QFont font; if (on) font.setBold(on); m_label->setFont(font); m_button->setEnabled(on); m_edited = on; } bool synthv1widget_palette::RoleEditor::edited (void) const { return m_edited; } void synthv1widget_palette::RoleEditor::resetProperty (void) { setEdited(false); emit changed(this); } // end of synthv1widget_palette.cpp synthv1-1.5.1/src/PaxHeaders/synthv1.lv20000644000000000000000000000013215270627745015057 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973346489 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1.lv2/0000755000175000001440000000000015270627745015124 5ustar00rncbcuserssynthv1-1.5.1/src/synthv1.lv2/PaxHeaders/synthv1.ttl0000644000000000000000000000013215270627745017275 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1.lv2/synthv1.ttl0000644000175000001440000011750615270627745017277 0ustar00rncbcusers@prefix doap: . @prefix foaf: . @prefix rdf: . @prefix rdfs: . @prefix lv2: . @prefix lv2ui: . @prefix lv2urid: . @prefix lv2atom: . @prefix lv2midi: . @prefix lv2time: . @prefix lv2state: . @prefix lv2patch: . @prefix lv2worker: . @prefix lv2resize: . @prefix lv2pg: . @prefix mod: . @prefix synthv1_lv2: . a lv2:Plugin, lv2:InstrumentPlugin, doap:Project ; doap:name "synthv1" ; doap:maintainer [ foaf:name "rncbc aka. Rui Nuno Capela" ; foaf:homepage ; foaf:mbox ; ] ; doap:license ; lv2:minorVersion 0 ; lv2:microVersion 2 ; lv2:requiredFeature lv2urid:map, lv2worker:schedule ; lv2:optionalFeature lv2:hardRTCapable ; lv2:extensionData lv2state:interface, lv2worker:interface ; lv2patch:writable synthv1_lv2:P201_TUNING_ENABLED, synthv1_lv2:P202_TUNING_REF_PITCH, synthv1_lv2:P203_TUNING_REF_NOTE, synthv1_lv2:P204_TUNING_SCALE_FILE, synthv1_lv2:P205_TUNING_KEYMAP_FILE ; lv2:port [ a lv2:InputPort, lv2atom:AtomPort ; lv2atom:bufferType lv2atom:Sequence ; lv2atom:supports lv2midi:MidiEvent, lv2time:Position, lv2patch:Message ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "in"; lv2:name "MIDI In"; ], [ a lv2:OutputPort, lv2atom:AtomPort ; lv2atom:bufferType lv2atom:Sequence ; lv2atom:supports lv2patch:Message ; lv2:designation lv2:control ; lv2resize:minimumSize 5120 ; lv2:index 1 ; lv2:symbol "notify"; lv2:name "Notify"; ] ; lv2:port [ a lv2:InputPort, lv2:AudioPort ; lv2:index 2 ; lv2:symbol "in_L" ; lv2:name "Audio In L" ; ], [ a lv2:InputPort, lv2:AudioPort ; lv2:index 3 ; lv2:symbol "in_R" ; lv2:name "Audio In R" ; ] ; lv2:port [ a lv2:OutputPort, lv2:AudioPort ; lv2:index 4 ; lv2:symbol "Out_L" ; lv2:name "Audio Out L" ; ], [ a lv2:OutputPort, lv2:AudioPort ; lv2:index 5 ; lv2:symbol "Out_R" ; lv2:name "Audio Out R" ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 6 ; lv2:symbol "DCO1_SHAPE1" ; lv2:name "DCO1 Wave Shape 1" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 7 ; lv2:symbol "DCO1_WIDTH1" ; lv2:name "DCO1 Wave Width 1" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 8 ; lv2:symbol "DCO1_BANDL1" ; lv2:name "DCO1 Wave Bandlimit 1" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 9 ; lv2:symbol "DCO1_SYNC1" ; lv2:name "DCO1 Wave Sync 1" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 10 ; lv2:symbol "DCO1_SHAPE2" ; lv2:name "DCO1 Wave Shape 2" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 11 ; lv2:symbol "DCO1_WIDTH2" ; lv2:name "DCO1 Width 2" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 12 ; lv2:symbol "DCO1_BANDL2" ; lv2:name "DCO1 Wave Bandlimit 2" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 13 ; lv2:symbol "DCO1_SYNC2" ; lv2:name "DCO1 Wave Sync 2" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 14 ; lv2:symbol "DCO1_BALANCE" ; lv2:name "DCO1 Balance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 15 ; lv2:symbol "DCO1_DETUNE" ; lv2:name "DCO1 Detune" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 16 ; lv2:symbol "DCO1_PHASE" ; lv2:name "DCO1 Phase" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 17 ; lv2:symbol "DCO1_RINGMOD" ; lv2:name "DCO1 Ring Mod" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 18 ; lv2:symbol "DCO1_OCTAVE" ; lv2:name "DCO1 Octave" ; lv2:default 0.0 ; lv2:minimum -4.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 19 ; lv2:symbol "DCO1_TUNING" ; lv2:name "DCO1 Tuning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 20 ; lv2:symbol "DCO1_GLIDE" ; lv2:name "DCO1 Glide" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 21 ; lv2:symbol "DCO1_ENVTIME" ; lv2:name "DCO1 Env.Time" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 22 ; lv2:symbol "DCF1_ENABLED" ; lv2:name "DCF1 Enabled" ; lv2:portProperty lv2:toggled ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 23 ; lv2:symbol "DCF1_CUTOFF" ; lv2:name "DCF1 Cutoff" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 24 ; lv2:symbol "DCF1_RESO" ; lv2:name "DCF1 Resonance" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 25 ; lv2:symbol "DCF1_TYPE" ; lv2:name "DCF1 Type" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "LPF"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "BPF"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "HPF"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "BRF"; rdf:value 3 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 3.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 26 ; lv2:symbol "DCF1_SLOPE" ; lv2:name "DCF1 Slope" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "12dB/oct"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "24dB/oct"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Biquad"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Formant"; rdf:value 3 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 3.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 27 ; lv2:symbol "DCF1_ENVELOPE" ; lv2:name "DCF1 Envelope" ; lv2:default 1.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 28 ; lv2:symbol "DCF1_ATTACK" ; lv2:name "DCF1 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 29 ; lv2:symbol "DCF1_DECAY" ; lv2:name "DCF1 Decay" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 30 ; lv2:symbol "DCF1_SUSTAIN" ; lv2:name "DCF1 Sustain" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 31 ; lv2:symbol "DCF1_RELEASE" ; lv2:name "DCF1 Release" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G102_DCF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 32 ; lv2:symbol "LFO1_ENABLED" ; lv2:name "LFO1 Enabled" ; lv2:portProperty lv2:toggled ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 33 ; lv2:symbol "LFO1_SHAPE" ; lv2:name "LFO1 Wave Shape" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 34 ; lv2:symbol "LFO1_WIDTH" ; lv2:name "LFO1 Wave Width" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 35 ; lv2:symbol "LFO1_BPM" ; lv2:name "LFO1 BPM" ; lv2:default 180.0 ; lv2:minimum 0.0 ; lv2:maximum 360.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 36 ; lv2:symbol "LFO1_RATE" ; lv2:name "LFO1 Rate" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 37 ; lv2:symbol "LFO1_SYNC" ; lv2:name "LFO1 Sync" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 38 ; lv2:symbol "LFO1_SWEEP" ; lv2:name "LFO1 Sweep" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 39 ; lv2:symbol "LFO1_PITCH" ; lv2:name "LFO1 Pitch" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 40 ; lv2:symbol "LFO1_BALANCE" ; lv2:name "LFO1 Balance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 41 ; lv2:symbol "LFO1_RINGMOD" ; lv2:name "LFO1 Ring Mod" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 42 ; lv2:symbol "LFO1_CUTOFF" ; lv2:name "LFO1 Cutoff" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 43 ; lv2:symbol "LFO1_RESO" ; lv2:name "LFO1 Resonance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 44 ; lv2:symbol "LFO1_PANNING" ; lv2:name "LFO1 Panning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 45 ; lv2:symbol "LFO1_VOLUME" ; lv2:name "LFO1 Volume" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 46 ; lv2:symbol "LFO1_ATTACK" ; lv2:name "LFO1 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 47 ; lv2:symbol "LFO1_DECAY" ; lv2:name "LFO1 Decay" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 48 ; lv2:symbol "LFO1_SUSTAIN" ; lv2:name "LFO1 Sustain" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 49 ; lv2:symbol "LFO1_RELEASE" ; lv2:name "LFO1 Release" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 50 ; lv2:symbol "DCA1_VOLUME" ; lv2:name "DCA1 Volume" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G104_DCA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 51 ; lv2:symbol "DCA1_ATTACK" ; lv2:name "DCA1 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G104_DCA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 52 ; lv2:symbol "DCA1_DECAY" ; lv2:name "DCA1 Decay" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G104_DCA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 53 ; lv2:symbol "DCA1_SUSTAIN" ; lv2:name "DCA1 Sustain" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G104_DCA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 54 ; lv2:symbol "DCA1_RELEASE" ; lv2:name "DCA1 Release" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G104_DCA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 55 ; lv2:symbol "OUT1_WIDTH" ; lv2:name "OUT1 Stereo Width" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G105_OUT1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 56 ; lv2:symbol "OUT1_PANNING" ; lv2:name "OUT1 Panning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G105_OUT1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 57 ; lv2:symbol "OUT1_FXSEND" ; lv2:name "OUT1 FX Send" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G105_OUT1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 58 ; lv2:symbol "OUT1_VOLUME" ; lv2:name "OUT1 Volume" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G105_OUT1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 59 ; lv2:symbol "DEF1_PITCHBEND" ; lv2:name "DEF1 Pitchbend" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 60 ; lv2:symbol "DEF1_MODWHEEL" ; lv2:name "DEF1 Modwheel" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 61 ; lv2:symbol "DEF1_PRESSURE" ; lv2:name "DEF1 Pressure" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 62 ; lv2:symbol "DEF1_VELOCITY" ; lv2:name "DEF1 Velocity" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 63 ; lv2:symbol "DEF1_CHANNEL" ; lv2:name "DEF1 Channel" ; lv2:portProperty lv2:integer ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 16.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 64 ; lv2:symbol "DEF1_MONO" ; lv2:name "DEF1 Mono" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Poly"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Mono"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Legato"; rdf:value 2 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 2.0 ; lv2pg:group synthv1_lv2:G106_DEF1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 65 ; lv2:symbol "DCO2_SHAPE1" ; lv2:name "DCO2 Wave Shape 1" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 66 ; lv2:symbol "DCO2_WIDTH1" ; lv2:name "DCO2 Wave Width 1" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 67 ; lv2:symbol "DCO2_BANDL1" ; lv2:name "DCO2 Wave Bandlimit 1" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 68 ; lv2:symbol "DCO2_SYNC1" ; lv2:name "DCO2 Wave Sync 1" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 69 ; lv2:symbol "DCO2_SHAPE2" ; lv2:name "DCO2 Wave Shape 2" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 70 ; lv2:symbol "DCO2_WIDTH2" ; lv2:name "DCO2 Wave Width 2" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 71 ; lv2:symbol "DCO2_BANDL2" ; lv2:name "DCO2 Wave Bandlimit 2" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 72 ; lv2:symbol "DCO2_SYNC2" ; lv2:name "DCO2 Wave Sync 2" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 73 ; lv2:symbol "DCO2_BALANCE" ; lv2:name "DCO2 Balance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 74 ; lv2:symbol "DCO2_DETUNE" ; lv2:name "DCO2 Detune" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 75 ; lv2:symbol "DCO2_PHASE" ; lv2:name "DCO2 Phase" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 76 ; lv2:symbol "DCO2_RINGMOD" ; lv2:name "DCO2 Ring Mod" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G101_DCO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 77 ; lv2:symbol "DCO2_OCTAVE" ; lv2:name "DCO2 Octave" ; lv2:default -2.0 ; lv2:minimum -4.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 78 ; lv2:symbol "DCO2_TUNING" ; lv2:name "DCO2 Tuning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 79 ; lv2:symbol "DCO2_GLIDE" ; lv2:name "DCO2 Glide" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 80 ; lv2:symbol "DCO2_ENVTIME" ; lv2:name "DCO2 Env.Time" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G201_DCO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 81 ; lv2:symbol "DCF2_ENABLED" ; lv2:name "DCF2 Enabled" ; lv2:portProperty lv2:toggled ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 82 ; lv2:symbol "DCF2_CUTOFF" ; lv2:name "DCF2 Cutoff" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 83 ; lv2:symbol "DCF2_RESO" ; lv2:name "DCF2 Resonance" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 84 ; lv2:symbol "DCF2_TYPE" ; lv2:name "DCF2 Type" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "LPF"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "BPF"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "HPF"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "BRF"; rdf:value 3 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 3.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 85 ; lv2:symbol "DCF2_SLOPE" ; lv2:name "DCF2 Slope" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "12dB/oct"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "24dB/oct"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Biquad"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Formant"; rdf:value 3 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 3.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 86 ; lv2:symbol "DCF2_ENVELOPE" ; lv2:name "DCF2 Envelope" ; lv2:default 1.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 87 ; lv2:symbol "DCF2_ATTACK" ; lv2:name "DCF2 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 88 ; lv2:symbol "DCF2_DECAY" ; lv2:name "DCF2 Decay" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 89 ; lv2:symbol "DCF2_SUSTAIN" ; lv2:name "DCF2 Sustain" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 90 ; lv2:symbol "DCF2_RELEASE" ; lv2:name "DCF2 Release" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G202_DCF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 91 ; lv2:symbol "LFO2_ENABLED" ; lv2:name "LFO2 Enabled" ; lv2:portProperty lv2:toggled ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 92 ; lv2:symbol "LFO2_SHAPE" ; lv2:name "LFO2 Wave Shape" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Pulse"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Saw"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Sine"; rdf:value 2 ] ; lv2:scalePoint [ rdfs:label "Rand"; rdf:value 3 ] ; lv2:scalePoint [ rdfs:label "Noise"; rdf:value 4 ] ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 93 ; lv2:symbol "LFO2_WIDTH" ; lv2:name "LFO2 Wave Width" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 94 ; lv2:symbol "LFO2_BPM" ; lv2:name "LFO2 BPM" ; lv2:default 180.0 ; lv2:minimum 0.0 ; lv2:maximum 360.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 95 ; lv2:symbol "LFO2_RATE" ; lv2:name "LFO2 Rate" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 96 ; lv2:symbol "LFO2_SYNC" ; lv2:name "LFO2 Sync" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 97 ; lv2:symbol "LFO2_SWEEP" ; lv2:name "LFO2 Sweep" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 98 ; lv2:symbol "LFO2_PITCH" ; lv2:name "LFO2 Pitch" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 99 ; lv2:symbol "LFO2_BALANCE" ; lv2:name "LFO2 Balance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 100 ; lv2:symbol "LFO2_RINGMOD" ; lv2:name "LFO2 Ring Mod" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G103_LFO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 101 ; lv2:symbol "LFO2_CUTOFF" ; lv2:name "LFO2 Cutoff" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 102 ; lv2:symbol "LFO2_RESO" ; lv2:name "LFO2 Resonance" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 103 ; lv2:symbol "LFO2_PANNING" ; lv2:name "LFO2 Panning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 104 ; lv2:symbol "LFO2_VOLUME" ; lv2:name "LFO2 Volume" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 105 ; lv2:symbol "LFO2_ATTACK" ; lv2:name "LFO2 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 106 ; lv2:symbol "LFO2_DECAY" ; lv2:name "LFO2 Decay" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 107 ; lv2:symbol "LFO2_SUSTAIN" ; lv2:name "LFO2 Sustain" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 108 ; lv2:symbol "LFO2_RELEASE" ; lv2:name "LFO2 Release" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G203_LFO2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 109 ; lv2:symbol "DCA2_VOLUME" ; lv2:name "DCA2 Volume" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G204_DCA2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 110 ; lv2:symbol "DCA2_ATTACK" ; lv2:name "DCA2 Attack" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G204_DCA2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 111 ; lv2:symbol "DCA2_DECAY" ; lv2:name "DCA2 Decay" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G204_DCA2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 112 ; lv2:symbol "DCA2_SUSTAIN" ; lv2:name "DCA2 Sustain" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G204_DCA2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 113 ; lv2:symbol "DCA2_RELEASE" ; lv2:name "DCA2 Release" ; lv2:default 0.1 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G204_DCA2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 114 ; lv2:symbol "OUT2_WIDTH" ; lv2:name "OUT2 Stereo Width" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G205_OUT2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 115 ; lv2:symbol "OUT2_PANNING" ; lv2:name "OUT2 Panning" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G205_OUT2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 116 ; lv2:symbol "OUT2_FXSEND" ; lv2:name "OUT2 FX Send" ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G205_OUT2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 117 ; lv2:symbol "OUT2_VOLUME" ; lv2:name "OUT2 Volume" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G205_OUT2 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 118 ; lv2:symbol "DEF2_PITCHBEND" ; lv2:name "DEF2 Pitchbend" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 4.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 119 ; lv2:symbol "DEF2_MODWHEEL" ; lv2:name "DEF2 Modwheel" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 120 ; lv2:symbol "DEF2_PRESSURE" ; lv2:name "DEF2 Pressure" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 121 ; lv2:symbol "DEF2_VELOCITY" ; lv2:name "DEF2 Velocity" ; lv2:default 0.2 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 122 ; lv2:symbol "DEF2_CHANNEL" ; lv2:name "DEF2 Channel" ; lv2:portProperty lv2:integer ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 16.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 123 ; lv2:symbol "DEF2_MONO" ; lv2:name "DEF2 Mono" ; lv2:portProperty lv2:integer, lv2:enumeration ; lv2:scalePoint [ rdfs:label "Poly"; rdf:value 0 ] ; lv2:scalePoint [ rdfs:label "Mono"; rdf:value 1 ] ; lv2:scalePoint [ rdfs:label "Legato"; rdf:value 2 ] ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 2.0 ; lv2pg:group synthv1_lv2:G206_DEF2 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 124 ; lv2:symbol "CHO1_WET" ; lv2:name "Chorus Wet" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G301_CHO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 125 ; lv2:symbol "CHO1_DELAY" ; lv2:name "Chorus Delay" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G301_CHO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 126 ; lv2:symbol "CHO1_FEEDB" ; lv2:name "Chorus Feedback" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G301_CHO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 127 ; lv2:symbol "CHO1_RATE" ; lv2:name "Chorus Rate" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G301_CHO1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 128 ; lv2:symbol "CHO1_MOD" ; lv2:name "Chorus Modulation" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G301_CHO1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 129 ; lv2:symbol "FLA1_WET" ; lv2:name "Flanger Wet" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G302_FLA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 130 ; lv2:symbol "FLA1_DELAY" ; lv2:name "Flanger Delay" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G302_FLA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 131 ; lv2:symbol "FLA1_FEEDB" ; lv2:name "Flanger Feedback" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G302_FLA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 132 ; lv2:symbol "FLA1_DAFT" ; lv2:name "Flanger Daft" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G302_FLA1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 133 ; lv2:symbol "PHA1_WET" ; lv2:name "Phaser Wet" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G303_PHA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 134 ; lv2:symbol "PHA1_RATE" ; lv2:name "Phaser Rate" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G303_PHA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 135 ; lv2:symbol "PHA1_FEEDB" ; lv2:name "Phaser Feedback" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G303_PHA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 136 ; lv2:symbol "PHA1_DEPTH" ; lv2:name "Phaser Depth" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G303_PHA1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 137 ; lv2:symbol "PHA1_DAFT" ; lv2:name "Phaser Daft" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G303_PHA1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 138 ; lv2:symbol "DEL1_WET" ; lv2:name "Delay Wet" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G304_DEL1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 139 ; lv2:symbol "DEL1_DELAY" ; lv2:name "Delay Delay" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G304_DEL1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 140 ; lv2:symbol "DEL1_FEEDB" ; lv2:name "Delay Feedback" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G304_DEL1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 141 ; lv2:symbol "DEL1_BPM" ; lv2:name "Delay BPM" ; lv2:default 180.0 ; lv2:minimum 0.0 ; lv2:maximum 360.0 ; lv2pg:group synthv1_lv2:G304_DEL1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 142 ; lv2:symbol "REV1_WET" ; lv2:name "Reverb Wet" ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G305_REV1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 143 ; lv2:symbol "REV1_ROOM" ; lv2:name "Reverb Room" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G305_REV1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 144 ; lv2:symbol "REV1_DAMP" ; lv2:name "Reverb Damp" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G305_REV1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 145 ; lv2:symbol "REV1_FEEDB" ; lv2:name "Reverb Feedback" ; lv2:default 0.5 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G305_REV1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 146 ; lv2:symbol "REV1_WIDTH" ; lv2:name "Reverb Width" ; lv2:default 0.0 ; lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G305_REV1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 147 ; lv2:symbol "DYN1_COMPRESS" ; lv2:name "Dynamic Compressor" ; lv2:portProperty lv2:toggled ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G306_DYN1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 148 ; lv2:symbol "DYN1_LIMITER" ; lv2:name "Dynamic Limiter" ; lv2:portProperty lv2:toggled ; lv2:default 1.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ; lv2pg:group synthv1_lv2:G306_DYN1 ; ] ; lv2:port [ a lv2:InputPort, lv2:ControlPort ; lv2:index 149 ; lv2:symbol "KEY1_LOW" ; lv2:name "Keyboard Low" ; lv2:portProperty lv2:integer ; lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 127.0 ; lv2pg:group synthv1_lv2:G501_KEY1 ; ], [ a lv2:InputPort, lv2:ControlPort ; lv2:index 150 ; lv2:symbol "KEY1_HIGH" ; lv2:name "Keyboard High" ; lv2:portProperty lv2:integer ; lv2:default 127.0 ; lv2:minimum 0.0 ; lv2:maximum 127.0 ; lv2pg:group synthv1_lv2:G501_KEY1 ; ] . synthv1_lv2:P201_TUNING_ENABLED a lv2:Parameter ; rdfs:label "P201 Tuning Enabled" ; rdfs:range lv2atom:Bool . synthv1_lv2:P202_TUNING_REF_PITCH a lv2:Parameter ; rdfs:label "P202 Tuning Reference Pitch" ; rdfs:range lv2atom:Float ; lv2:default 440.0 ; lv2:minimum 20.0 ; lv2:maximum 8000.0 . synthv1_lv2:P203_TUNING_REF_NOTE a lv2:Parameter ; rdfs:label "P203 Tuning Reference Note" ; rdfs:range lv2atom:Int ; lv2:default 69 ; lv2:minimum 0 ; lv2:maximum 127 . synthv1_lv2:P204_TUNING_SCALE_FILE a lv2:Parameter ; rdfs:label "P204 Tuning Scale File" ; rdfs:range lv2atom:Path ; mod:fileTypes "scl" . synthv1_lv2:P205_TUNING_KEYMAP_FILE a lv2:Parameter ; rdfs:label "P205 Tuning Key Map File" ; rdfs:range lv2atom:Path ; mod:fileTypes "kbm" . synthv1_lv2:G101_DCO1 a lv2pg:InputGroup; lv2:name "Synth 1 - DCO1" ; lv2:symbol "DCO1" . synthv1_lv2:G102_DCF1 a lv2pg:InputGroup; lv2:name "Synth 1 - DCF1" ; lv2:symbol "DCF1" . synthv1_lv2:G103_LFO1 a lv2pg:InputGroup; lv2:name "Synth 1 - LFO1" ; lv2:symbol "LFO1" . synthv1_lv2:G104_DCA1 a lv2pg:InputGroup; lv2:name "Synth 1 - DCA1" ; lv2:symbol "DCA1" . synthv1_lv2:G105_OUT1 a lv2pg:InputGroup; lv2:name "Synth 1 - OUT1" ; lv2:symbol "OUT1" . synthv1_lv2:G106_DEF1 a lv2pg:InputGroup; lv2:name "Synth 1 - DEF1" ; lv2:symbol "DEF1" . synthv1_lv2:G201_DCO2 a lv2pg:InputGroup; lv2:name "Synth 2 - DCO2" ; lv2:symbol "DCO2" . synthv1_lv2:G202_DCF2 a lv2pg:InputGroup; lv2:name "Synth 2 - DCF2" ; lv2:symbol "DCF2" . synthv1_lv2:G203_LFO2 a lv2pg:InputGroup; lv2:name "Synth 2 - LFO2" ; lv2:symbol "LFO2" . synthv1_lv2:G204_DCA2 a lv2pg:InputGroup; lv2:name "Synth 2 - DCA2" ; lv2:symbol "DCA2" . synthv1_lv2:G205_OUT2 a lv2pg:InputGroup; lv2:name "Synth 2 - OUT2" ; lv2:symbol "OUT2" . synthv1_lv2:G206_DEF2 a lv2pg:InputGroup; lv2:name "Synth 2 - DEF2" ; lv2:symbol "DEF2" . synthv1_lv2:G301_CHO1 a lv2pg:InputGroup; lv2:name "Effects - Chorus" ; lv2:symbol "CHO1" . synthv1_lv2:G302_FLA1 a lv2pg:InputGroup; lv2:name "Effects - Flanger" ; lv2:symbol "FLA1" . synthv1_lv2:G303_PHA1 a lv2pg:InputGroup; lv2:name "Effects - Phaser" ; lv2:symbol "PHA1" . synthv1_lv2:G304_DEL1 a lv2pg:InputGroup; lv2:name "Effects - Delay" ; lv2:symbol "DEL1" . synthv1_lv2:G305_REV1 a lv2pg:InputGroup; lv2:name "Effects - Reverb" ; lv2:symbol "REV1" . synthv1_lv2:G306_DYN1 a lv2pg:InputGroup; lv2:name "Effects - Dynamic" ; lv2:symbol "DYN1" . synthv1_lv2:G401_TUN1 a lv2pg:InputGroup; lv2:name "Config - Tuning" ; lv2:symbol "TUN1" . synthv1_lv2:G501_KEY1 a lv2pg:InputGroup; lv2:name "Input - Keyboard" ; lv2:symbol "KEY1" . synthv1-1.5.1/src/synthv1.lv2/PaxHeaders/synthv1_ui.ttl0000644000000000000000000000013215270627745017772 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1.lv2/synthv1_ui.ttl0000644000175000001440000000164415270627745017767 0ustar00rncbcusers@prefix lv2: . @prefix lv2ui: . @prefix synthv1_lv2: . lv2ui:ui synthv1_lv2:ui_x11, synthv1_lv2:ui_external . a lv2ui:Qt6UI ; lv2:requiredFeature ; lv2ui:binary . a lv2ui:X11UI ; lv2:requiredFeature ; lv2:optionalFeature lv2ui:resize, lv2ui:idleInterface, lv2ui:showInterface ; lv2:extensionData lv2ui:resize, lv2ui:idleInterface, lv2ui:showInterface ; lv2ui:binary . a ; lv2:requiredFeature ; lv2ui:binary . synthv1-1.5.1/src/synthv1.lv2/PaxHeaders/manifest-win32.ttl0000644000000000000000000000013215270627745020427 xustar0030 mtime=1793273829.973346489 30 atime=1793273829.973346489 30 ctime=1793273829.973346489 synthv1-1.5.1/src/synthv1.lv2/manifest-win32.ttl0000644000175000001440000000040015270627745020411 0ustar00rncbcusers@prefix lv2: . @prefix rdfs: . a lv2:Plugin, lv2:InstrumentPlugin ; lv2:binary ; rdfs:seeAlso , . synthv1-1.5.1/src/synthv1.lv2/PaxHeaders/synthv1_ui-win32.ttl0000644000000000000000000000013215270627745020732 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1.lv2/synthv1_ui-win32.ttl0000644000175000001440000000166315270627745020730 0ustar00rncbcusers@prefix lv2: . @prefix lv2ui: . @prefix synthv1_lv2: . lv2ui:ui synthv1_lv2:ui_windows, synthv1_lv2:ui_external . a lv2ui:Qt6UI ; lv2:requiredFeature ; lv2ui:binary . a lv2ui:WindowsUI ; lv2:requiredFeature ; lv2:optionalFeature lv2ui:resize, lv2ui:idleInterface, lv2ui:showInterface ; lv2:extensionData lv2ui:resize, lv2ui:idleInterface, lv2ui:showInterface ; lv2ui:binary . a ; lv2:requiredFeature ; lv2ui:binary . synthv1-1.5.1/src/synthv1.lv2/PaxHeaders/manifest.ttl0000644000000000000000000000013215270627745017467 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973346489 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1.lv2/manifest.ttl0000644000175000001440000000037715270627745017466 0ustar00rncbcusers@prefix lv2: . @prefix rdfs: . a lv2:Plugin, lv2:InstrumentPlugin ; lv2:binary ; rdfs:seeAlso , . synthv1-1.5.1/src/PaxHeaders/synthv1_programs.cpp0000644000000000000000000000013215270627745017050 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_programs.cpp0000644000175000001440000001000615270627745017035 0ustar00rncbcusers// synthv1_programs.cpp // /**************************************************************************** Copyright (C) 2012-2024 rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_programs.h" //------------------------------------------------------------------------- // synthv1_programs - Bank/programs database class (singleton). // // ctor. synthv1_programs::synthv1_programs ( synthv1 *pSynth ) : m_enabled(false), m_sched(pSynth), m_bank_msb(0), m_bank_lsb(0), m_bank(nullptr), m_prog(nullptr) { } // dtor. synthv1_programs::~synthv1_programs (void) { clear_banks(); } // prog. managers synthv1_programs::Prog *synthv1_programs::Bank::find_prog ( uint16_t prog_id ) const { return m_progs.value(prog_id, nullptr); } synthv1_programs::Prog *synthv1_programs::Bank::add_prog ( uint16_t prog_id, const QString& prog_name ) { Prog *prog = find_prog(prog_id); if (prog) { prog->set_name(prog_name); } else { prog = new Prog(prog_id, prog_name); m_progs.insert(prog_id, prog); } return prog; } void synthv1_programs::Bank::remove_prog ( uint16_t prog_id ) { Prog *prog = find_prog(prog_id); if (prog && m_progs.remove(prog_id)) delete prog; } void synthv1_programs::Bank::clear_progs (void) { qDeleteAll(m_progs); m_progs.clear(); } // bank managers synthv1_programs::Bank *synthv1_programs::find_bank ( uint16_t bank_id ) const { return m_banks.value(bank_id, nullptr); } synthv1_programs::Bank *synthv1_programs::add_bank ( uint16_t bank_id, const QString& bank_name ) { Bank *bank = find_bank(bank_id); if (bank) { bank->set_name(bank_name); } else { bank = new Bank(bank_id, bank_name); m_banks.insert(bank_id, bank); } return bank; } void synthv1_programs::remove_bank ( uint16_t bank_id ) { Bank *bank = find_bank(bank_id); if (bank && m_banks.remove(bank_id)) delete bank; } void synthv1_programs::clear_banks (void) { m_bank_msb = 0; m_bank_lsb = 0; m_bank = nullptr; m_prog = nullptr; qDeleteAll(m_banks); m_banks.clear(); } // current bank/prog. managers void synthv1_programs::bank_select_msb ( uint8_t bank_msb ) { m_bank_msb = 0x80 | (bank_msb & 0x7f); } void synthv1_programs::bank_select_lsb ( uint8_t bank_lsb ) { m_bank_lsb = 0x80 | (bank_lsb & 0x7f); } void synthv1_programs::bank_select ( uint16_t bank_id ) { bank_select_msb(bank_id >> 7); bank_select_lsb(bank_id); } uint16_t synthv1_programs::current_bank_id (void) const { uint16_t bank_id = 0; if (m_bank_msb & 0x80) bank_id = (m_bank_msb & 0x7f); if (m_bank_lsb & 0x80) { bank_id <<= 7; bank_id |= (m_bank_lsb & 0x7f); } return bank_id; } void synthv1_programs::prog_change ( uint16_t prog_id ) { select_program(current_bank_id(), prog_id); } void synthv1_programs::select_program ( uint16_t bank_id, uint16_t prog_id ) { if (!enabled()) return; if (m_bank && m_bank->id() == bank_id && m_prog && m_prog->id() == prog_id) return; m_sched.select_program(bank_id, prog_id); } void synthv1_programs::process_program ( synthv1 *pSynth, uint16_t bank_id, uint16_t prog_id ) { m_bank = find_bank(bank_id); m_prog = (m_bank ? m_bank->find_prog(prog_id) : nullptr); if (m_prog) { synthv1_param::loadPresetName(pSynth, m_prog->name()); pSynth->updateParams(); } } // end of synthv1_programs.cpp synthv1-1.5.1/src/PaxHeaders/synthv1_jack.cpp0000644000000000000000000000013215270627745016126 xustar0030 mtime=1793273829.973607084 30 atime=1793273829.973607084 30 ctime=1793273829.973607084 synthv1-1.5.1/src/synthv1_jack.cpp0000644000175000001440000007224215270627745016125 0ustar00rncbcusers// synthv1_jack.cpp // /**************************************************************************** Copyright (C) 2012-2026, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "synthv1_jack.h" #include "synthv1_config.h" #include "synthv1_param.h" #include "synthv1_programs.h" #include "synthv1_controls.h" #include #include #include #include #include #include #ifdef CONFIG_ALSA_MIDI //------------------------------------------------------------------------- // alsa input thread. #include class synthv1_alsa_thread : public QThread { public: synthv1_alsa_thread(synthv1_jack *synth) : QThread(), m_synth(synth), m_running(false) {} ~synthv1_alsa_thread() { // fake sync and wait if (m_running && isRunning()) do { m_running = false; } while (!wait(100)); } protected: void run() { snd_seq_t *seq = m_synth->alsa_seq(); if (seq == nullptr) return; m_running = true; int nfds; struct pollfd *pfds; nfds = snd_seq_poll_descriptors_count(seq, POLLIN); pfds = (struct pollfd *) alloca(nfds * sizeof(struct pollfd)); snd_seq_poll_descriptors(seq, pfds, nfds, POLLIN); int poll_rc = 0; while (m_running && poll_rc >= 0) { poll_rc = ::poll(pfds, nfds, 200); while (poll_rc > 0) { snd_seq_event_t *ev = nullptr; snd_seq_event_input(seq, &ev); m_synth->alsa_capture(ev); // snd_seq_free_event(ev); poll_rc = snd_seq_event_input_pending(seq, 0); } } m_running = false; } private: synthv1_jack *m_synth; volatile bool m_running; }; #endif // CONFIG_ALSA_MIDI //------------------------------------------------------------------------- // JACK process callback. static int synthv1_jack_process ( jack_nframes_t nframes, void *arg ) { return static_cast (arg)->process(nframes); } //---------------------------------------------------------------------- // JACK buffer-size change callback. static int synthv1_jack_buffer_size ( jack_nframes_t nframes, void *arg ) { static_cast (arg)->setBufferSize(nframes); return 0; } //---------------------------------------------------------------------- // JACK on-shutdown callback. static void synthv1_jack_on_shutdown ( void *arg ) { static_cast (arg)->shutdown(); } #ifdef CONFIG_JACK_SESSION #include #include //---------------------------------------------------------------------- // synthv1_jack_session_event -- JACK session event callabck // static void synthv1_jack_session_event ( jack_session_event_t *pSessionEvent, void *pvArg ) { synthv1_jack *pSynth = static_cast (pvArg); if (pSynth) pSynth->sessionEvent(pSessionEvent); } #endif // CONFIG_JACK_SESSION //------------------------------------------------------------------------- // synthv1_jack - impl. // synthv1_jack::synthv1_jack (const char *client_name) : synthv1(2) { m_client = nullptr; m_activated = false; m_audio_ins = nullptr; m_audio_outs = nullptr; m_ins = m_outs = nullptr; ::memset(m_params, 0, synthv1::NUM_PARAMS * sizeof(float)); #ifdef CONFIG_JACK_MIDI m_midi_in = nullptr; #endif #ifdef CONFIG_ALSA_MIDI m_alsa_seq = nullptr; // m_alsa_client = -1; m_alsa_port = -1; m_alsa_decoder = nullptr; m_alsa_buffer = nullptr; m_alsa_thread = nullptr; #endif synthv1::programs()->enabled(true); synthv1::controls()->enabled(true); open(client_name); activate(); } synthv1_jack::~synthv1_jack (void) { deactivate(); close(); } jack_client_t *synthv1_jack::client (void) const { return m_client; } int synthv1_jack::process ( jack_nframes_t nframes ) { if (!m_activated) return 0; const uint16_t nchannels = synthv1::channels(); float **ins = m_ins, **outs = m_outs; for (uint16_t k = 0; k < nchannels; ++k) { ins[k] = static_cast ( ::jack_port_get_buffer(m_audio_ins[k], nframes)); outs[k] = static_cast ( ::jack_port_get_buffer(m_audio_outs[k], nframes)); } jack_position_t pos; jack_transport_query(m_client, &pos); if (pos.valid & JackPositionBBT) { const float host_bpm = float(pos.beats_per_minute); if (::fabsf(host_bpm - synthv1::tempo()) > 0.001f) synthv1::setTempo(host_bpm); } uint32_t ndelta = 0; #ifdef CONFIG_JACK_MIDI void *midi_in = ::jack_port_get_buffer(m_midi_in, nframes); if (midi_in) { const uint32_t nevents = ::jack_midi_get_event_count(midi_in); for (uint32_t n = 0; n < nevents; ++n) { jack_midi_event_t event; ::jack_midi_event_get(&event, midi_in, n); if (event.time > ndelta) { const uint32_t nread = event.time - ndelta; if (nread > 0) { synthv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } ndelta = event.time; } synthv1::process_midi(event.buffer, event.size); } } #endif #ifdef CONFIG_ALSA_MIDI const jack_nframes_t buffer_size = ::jack_get_buffer_size(m_client); const jack_nframes_t frame_time = ::jack_last_frame_time(m_client); uint8_t event_buffer[1024]; jack_midi_event_t event; while (::jack_ringbuffer_peek(m_alsa_buffer, (char *) &event, sizeof(event)) == sizeof(event)) { if (event.time > frame_time) break; jack_nframes_t event_time = frame_time - event.time; if (event_time > buffer_size) event_time = 0; else event_time = buffer_size - event_time; if (event_time > ndelta) { const uint32_t nread = event_time - ndelta; if (nread > 0) { synthv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } ndelta = event_time; } ::jack_ringbuffer_read_advance(m_alsa_buffer, sizeof(event)); ::jack_ringbuffer_read(m_alsa_buffer, (char *) event_buffer, event.size); synthv1::process_midi(event_buffer, event.size); } #endif // CONFIG_ALSA_MIDI if (nframes > ndelta) synthv1::process(ins, outs, nframes - ndelta); return 0; } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #endif void synthv1_jack::open ( const char *client_name ) { // init param ports for (uint32_t i = 0; i < synthv1::NUM_PARAMS; ++i) { const synthv1::ParamIndex index = synthv1::ParamIndex(i); m_params[i] = synthv1_param::paramDefaultValue(index); synthv1::setParamPort(index, &m_params[i]); } // open client m_client = ::jack_client_open(client_name, JackNullOption, nullptr); if (m_client == nullptr) return; // set sample rate synthv1::setSampleRate(float(jack_get_sample_rate(m_client))); // synthv1::reset(); // register audio ports & buffers uint16_t nchannels = synthv1::channels(); m_audio_ins = new jack_port_t * [nchannels]; m_audio_outs = new jack_port_t * [nchannels]; m_ins = new float * [nchannels]; m_outs = new float * [nchannels]; char port_name[32]; for (uint16_t k = 0; k < nchannels; ++k) { ::snprintf(port_name, sizeof(port_name), "in_%d", k + 1); m_audio_ins[k] = ::jack_port_register(m_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); m_ins[k] = nullptr; ::snprintf(port_name, sizeof(port_name), "out_%d", k + 1); m_audio_outs[k] = ::jack_port_register(m_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); m_outs[k] = nullptr; } // register midi port #ifdef CONFIG_JACK_MIDI m_midi_in = ::jack_port_register(m_client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); #endif #ifdef CONFIG_ALSA_MIDI m_alsa_seq = nullptr; // m_alsa_client = -1; m_alsa_port = -1; m_alsa_decoder = nullptr; m_alsa_buffer = nullptr; m_alsa_thread = nullptr; // open alsa sequencer client... if (snd_seq_open(&m_alsa_seq, "hw", SND_SEQ_OPEN_INPUT, 0) >= 0) { snd_seq_set_client_name(m_alsa_seq, client_name); // m_alsa_client = snd_seq_client_id(m_alsa_seq); m_alsa_port = snd_seq_create_simple_port(m_alsa_seq, "in", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); snd_midi_event_new(1024, &m_alsa_decoder); m_alsa_buffer = ::jack_ringbuffer_create( 1024 * (sizeof(jack_midi_event_t) + 4)); m_alsa_thread = new synthv1_alsa_thread(this); m_alsa_thread->start(QThread::TimeCriticalPriority); } #endif // CONFIG_ALSA_MIDI // setup any local, initial buffers... synthv1::setBufferSize(::jack_get_buffer_size(m_client) << 2); ::jack_set_buffer_size_callback(m_client, synthv1_jack_buffer_size, this); ::jack_on_shutdown(m_client, synthv1_jack_on_shutdown, this); // set process callbacks... ::jack_set_process_callback(m_client, synthv1_jack_process, this); #ifdef CONFIG_JACK_SESSION // JACK session event callback... ::jack_set_session_callback(m_client, synthv1_jack_session_event, this); #endif } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif void synthv1_jack::activate (void) { if (!m_activated) { synthv1::reset(); if (m_client) { ::jack_activate(m_client); m_activated = true; } } } void synthv1_jack::deactivate (void) { if (m_activated) { if (m_client) { m_activated = false; ::jack_deactivate(m_client); } } } void synthv1_jack::close (void) { #ifdef CONFIG_ALSA_MIDI // close alsa sequencer client... if (m_alsa_seq) { if (m_alsa_thread) { delete m_alsa_thread; m_alsa_thread = nullptr; } if (m_alsa_buffer) { ::jack_ringbuffer_free(m_alsa_buffer); m_alsa_buffer = nullptr; } if (m_alsa_decoder) { snd_midi_event_free(m_alsa_decoder); m_alsa_decoder = nullptr; } if (m_alsa_port >= 0) { snd_seq_delete_simple_port(m_alsa_seq, m_alsa_port); m_alsa_port = -1; } snd_seq_close(m_alsa_seq); // m_alsa_client = -1; m_alsa_seq = nullptr; } #endif if (m_client == nullptr) return; #ifdef CONFIG_JACK_MIDI // unregister midi ports if (m_midi_in) { ::jack_port_unregister(m_client, m_midi_in); m_midi_in = nullptr; } #endif // unregister audio ports const uint16_t nchannels = synthv1::channels(); for (uint16_t k = 0; k < nchannels; ++k) { if (m_audio_outs && m_audio_outs[k]) { ::jack_port_unregister(m_client, m_audio_outs[k]); m_audio_outs[k] = nullptr; } if (m_outs && m_outs[k]) m_outs[k] = nullptr; if (m_audio_ins && m_audio_ins[k]) { ::jack_port_unregister(m_client, m_audio_ins[k]); m_audio_ins[k] = nullptr; } if (m_ins && m_ins[k]) m_ins[k] = nullptr; } if (m_outs) { delete [] m_outs; m_outs = nullptr; } if (m_ins) { delete [] m_ins; m_ins = nullptr; } if (m_audio_outs) { delete [] m_audio_outs; m_audio_outs = nullptr; } if (m_audio_ins) { delete [] m_audio_ins; m_audio_ins = nullptr; } // close client ::jack_client_close(m_client); m_client = nullptr; } #ifdef CONFIG_ALSA_MIDI // alsa sequencer client. snd_seq_t *synthv1_jack::alsa_seq (void) const { return m_alsa_seq; } // alsa event capture. void synthv1_jack::alsa_capture ( snd_seq_event_t *ev ) { if (m_alsa_decoder == nullptr) return; if (ev == nullptr) return; // ignored events... switch(ev->type) { case SND_SEQ_EVENT_OSS: case SND_SEQ_EVENT_CLIENT_START: case SND_SEQ_EVENT_CLIENT_EXIT: case SND_SEQ_EVENT_CLIENT_CHANGE: case SND_SEQ_EVENT_PORT_START: case SND_SEQ_EVENT_PORT_EXIT: case SND_SEQ_EVENT_PORT_CHANGE: case SND_SEQ_EVENT_PORT_SUBSCRIBED: case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: case SND_SEQ_EVENT_USR0: case SND_SEQ_EVENT_USR1: case SND_SEQ_EVENT_USR2: case SND_SEQ_EVENT_USR3: case SND_SEQ_EVENT_USR4: case SND_SEQ_EVENT_USR5: case SND_SEQ_EVENT_USR6: case SND_SEQ_EVENT_USR7: case SND_SEQ_EVENT_USR8: case SND_SEQ_EVENT_USR9: case SND_SEQ_EVENT_BOUNCE: case SND_SEQ_EVENT_USR_VAR0: case SND_SEQ_EVENT_USR_VAR1: case SND_SEQ_EVENT_USR_VAR2: case SND_SEQ_EVENT_USR_VAR3: case SND_SEQ_EVENT_USR_VAR4: case SND_SEQ_EVENT_NONE: return; } #ifdef CONFIG_DEBUG_0 // - show (input) event for debug purposes... fprintf(stderr, "ALSA MIDI In: 0x%02x", ev->type); if (ev->type == SND_SEQ_EVENT_SYSEX) { fprintf(stderr, " SysEx {"); unsigned char *data = (unsigned char *) ev->data.ext.ptr; for (unsigned int i = 0; i < ev->data.ext.len; ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, " }\n"); } else { for (unsigned int i = 0; i < sizeof(ev->data.raw8.d); ++i) fprintf(stderr, " %3d", ev->data.raw8.d[i]); fprintf(stderr, "\n"); } #endif const unsigned int nlimit = ::jack_ringbuffer_write_space(m_alsa_buffer); if (nlimit > sizeof(jack_midi_event_t) + 4) { unsigned char ev_buff[nlimit]; unsigned char *ev_data = &ev_buff[0] + sizeof(jack_midi_event_t); const int ev_size = snd_midi_event_decode(m_alsa_decoder, ev_data, nlimit - sizeof(jack_midi_event_t), ev); if (ev_size > 0) { jack_midi_event_t *ev_head = (jack_midi_event_t *) &ev_buff[0]; ev_head->time = ::jack_frame_time(m_client); ev_head->size = ev_size; ev_head->buffer = (jack_midi_data_t *) ev_data; ::jack_ringbuffer_write(m_alsa_buffer, (const char *) ev_buff, sizeof(jack_midi_event_t) + ev_size); } snd_midi_event_reset_decode(m_alsa_decoder); } } #endif // CONFIG_ALSA_MIDI #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif // JACK session event handler. void synthv1_jack::sessionEvent ( void *pvSessionArg ) { jack_session_event_t *pJackSessionEvent = (jack_session_event_t *) pvSessionArg; #ifdef CONFIG_DEBUG qDebug("synthv1_jack::sessionEvent()" " type=%d client_uuid=\"%s\" session_dir=\"%s\"", int(pJackSessionEvent->type), pJackSessionEvent->client_uuid, pJackSessionEvent->session_dir); #endif const bool bQuit = (pJackSessionEvent->type == JackSessionSaveAndQuit); const QString sSessionDir = QString::fromUtf8(pJackSessionEvent->session_dir); const QString sSessionName = QFileInfo(QFileInfo(sSessionDir).canonicalPath()).completeBaseName(); const QString sSessionFile = sSessionName + '.' + PROJECT_NAME; QStringList args; args << QCoreApplication::applicationFilePath(); args << QString("${SESSION_DIR}%1").arg(sSessionFile); synthv1_param::savePreset(this, QFileInfo(sSessionDir, sSessionFile).absoluteFilePath(), true); const QByteArray aCmdLine = args.join(" ").toUtf8(); pJackSessionEvent->command_line = ::strdup(aCmdLine.constData()); ::jack_session_reply(m_client, pJackSessionEvent); ::jack_session_event_free(pJackSessionEvent); if (bQuit) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QCoreApplication::exit(0); #else QCoreApplication::quit(); #endif } #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif // CONFIG_JACK_SESSION void synthv1_jack::updatePreset ( bool /*bDirty*/ ) { // nothing to do here... } void synthv1_jack::updateParam ( synthv1::ParamIndex /*index*/ ) { // nothing to do here... } void synthv1_jack::updateParams (void) { // nothing to do here... } void synthv1_jack::updateTuning (void) { synthv1::resetTuning(); } void synthv1_jack::shutdown (void) { synthv1_jack_application *pApp = synthv1_jack_application::getInstance(); if (pApp) pApp->shutdown(); } void synthv1_jack::shutdown_close (void) { m_activated = false; if (m_client) { ::jack_client_close(m_client); m_client = nullptr; } close(); } //------------------------------------------------------------------------- // synthv1_jack_application -- Singleton application instance. // #include "synthv1widget_jack.h" #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) #include #include #if defined(Q_OS_WINDOWS) #include #endif #endif #include #ifdef CONFIG_NSM #include "synthv1_nsm.h" #endif #ifdef HAVE_SIGNAL_H #include #include #include #include #include // File descriptor for SIGTERM notifier. static int g_fdSigterm[2] = { -1, -1 }; // Unix SIGTERM signal handler. static void synthv1_sigterm_handler ( int /*signo*/ ) { char c = 1; (void) (::write(g_fdSigterm[0], &c, sizeof(c)) > 0); } #endif // HAVE_SIGNAL_H // Constructor. synthv1_jack_application::synthv1_jack_application ( int& argc, char **argv ) : QObject(nullptr), m_pApp(nullptr), m_bGui(true), m_sClientName(PROJECT_NAME), m_pSynth(nullptr), m_pWidget(nullptr) #ifdef CONFIG_NSM , m_pNsmClient(nullptr) #endif { #ifdef Q_WS_X11 m_bGui = (::getenv("DISPLAY") != 0); #endif for (int i = 1; i < argc; ++i) { const QString& sArg = QString::fromLocal8Bit(argv[i]); if (sArg == "-g" || sArg == "--no-gui") m_bGui = false; } if (m_bGui) { #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #endif QApplication *pApp = new QApplication(argc, argv); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) pApp->setApplicationDisplayName(PROJECT_NAME); // PROJECT_NAME " - " + QObject::tr(PROJECT_DESCRIPTION)); #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) pApp->setDesktopFileName( QString("org.rncbc.%1").arg(PROJECT_NAME)); #endif #endif m_pApp = pApp; } else { m_pApp = new QCoreApplication(argc, argv); } #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) m_pApp->setApplicationName(PROJECT_NAME); m_pApp->setApplicationVersion(PROJECT_VERSION); #endif #ifdef HAVE_SIGNAL_H // Set to ignore any fatal "Broken pipe" signals. ::signal(SIGPIPE, SIG_IGN); // Initialize file descriptors for SIGTERM socket notifier. ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigterm); m_pSigtermNotifier = new QSocketNotifier(g_fdSigterm[1], QSocketNotifier::Read, this); QObject::connect(m_pSigtermNotifier, SIGNAL(activated(int)), SLOT(handle_sigterm())); // Install SIGTERM signal handler. struct sigaction sigterm; sigterm.sa_handler = synthv1_sigterm_handler; sigemptyset(&sigterm.sa_mask); sigterm.sa_flags = 0; sigterm.sa_flags |= SA_RESTART; ::sigaction(SIGTERM, &sigterm, nullptr); ::sigaction(SIGQUIT, &sigterm, nullptr); // Ignore SIGHUP/SIGINT signals. ::signal(SIGHUP, SIG_IGN); ::signal(SIGINT, SIG_IGN); #else m_pSigtermNotifier = nullptr; #endif // !HAVE_SIGNAL_H // Pseudo-singleton instance. g_pInstance = this; } // Destructor. synthv1_jack_application::~synthv1_jack_application (void) { g_pInstance = nullptr; #ifdef HAVE_SIGNAL_H if (m_pSigtermNotifier) delete m_pSigtermNotifier; #endif #ifdef CONFIG_NSM if (m_pNsmClient) delete m_pNsmClient; #endif if (m_pWidget) delete m_pWidget; if (m_pSynth) delete m_pSynth; if (m_pApp) delete m_pApp; } #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) void synthv1_jack_application::show_error( const QString& msg ) { #if defined(Q_OS_WINDOWS) QMessageBox::information(nullptr, QApplication::applicationName(), msg); #else const QByteArray tmp = msg.toUtf8() + '\n'; ::fputs(tmp.constData(), stderr); #endif } #endif // Argument parser method. bool synthv1_jack_application::parse_args (void) { const QStringList& args = m_pApp->arguments(); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QCommandLineParser parser; parser.setApplicationDescription( PROJECT_NAME " - " + QObject::tr(PROJECT_DESCRIPTION)); const QString s_no_gui = "no-gui"; const QString s_client_name = "client-name"; const QString s_help = "help"; parser.addOption({{"g", s_no_gui}, QObject::tr("Disable the graphical user interface (GUI)")}); parser.addOption({{"n", s_client_name}, QObject::tr("Set the JACK client name (default: %1)") .arg(PROJECT_NAME), "label"}); parser.addOption({{"?", s_help}, QObject::tr("Displays help on command-line options.")}); const QCommandLineOption& versionOption = parser.addVersionOption(); parser.addPositionalArgument("preset-file", QObject::tr("Load preset file (.%1)").arg(PROJECT_NAME), QObject::tr("[preset-file]")); if (!parser.parse(args)) { show_error(parser.errorText()); return false; } if (parser.isSet(s_help)) { show_error(parser.helpText()); return false; } if (parser.isSet(versionOption)) { QString sVersion = QString("%1 %2\n") .arg(PROJECT_NAME) .arg(QCoreApplication::applicationVersion()); sVersion += QString("Qt: %1").arg(qVersion()); #if defined(QT_STATIC) sVersion += "-static"; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) sVersion += ' '; sVersion += '('; sVersion += QApplication::platformName(); sVersion += ')'; #endif sVersion += '\n'; show_error(sVersion); return false; } if (parser.isSet(s_no_gui)) { // Ignored: parsed on startup... } if (parser.isSet(s_client_name)) { const QString& sVal = parser.value(s_client_name); if (sVal.isEmpty()) { show_error(QObject::tr("Option -n requires an argument (label).")); return false; } m_sClientName = sVal; } foreach (const QString& sArg, parser.positionalArguments()) { m_presets.append(sArg); } #else QTextStream out(stderr); const int argc = args.count(); for (int i = 1; i < argc; ++i) { QString sArg = args.at(i); QString sVal; const int iEqual = sArg.indexOf('='); if (iEqual >= 0) { sVal = sArg.right(sArg.length() - iEqual - 1); sArg = sArg.left(iEqual); } else if (i < argc - 1) { sVal = args.at(i + 1); if (sVal.at(0) == '-') sVal.clear(); } if (sArg == "-n" || sArg == "--client-name") { if (sVal.isNull()) { out << QObject::tr("Option -n requires an argument (label).\n\n"); return false; } m_sClientName = sVal; if (iEqual < 0) ++i; } else if (sArg == "-?" || sArg == "--help") { const QString sEot = "\n\t"; const QString sEol = "\n\n"; out << QObject::tr("Usage: %1 [options]").arg(args.at(0)) + sEol; out << PROJECT_NAME " - " << QObject::tr(PROJECT_DESCRIPTION) + sEol; out << QObject::tr("Options:") + sEol; out << " -g, --no-gui" + sEot + QObject::tr("Disable the graphical user interface (GUI)") + sEol; out << " -n, --client-name